Here is how I am going to do similar task.
I have two PE images - one for kernel, ant.exe and second for HAL - hal.dll.
The bootloader reads the files from FS and loads them into RAM (as images). Both files have their Base Addresses they have been linked to. And they have dependencies between each other through import/export structures.
Bootloader binds them together (initializes IAT of both) and then jumps into EntryPoint of the kernel. Kernel then calls Hal functions.
Bootloader should be a little a memory manager, file system and PE loader. Get slightly more real.
It's impractical and not benefitial to idealize things to the point of "everything machine specific goes to HAL". Most should, not everything. Memory manager is huge, it should be a logically standalone kernel unit. Otherwise your Hal just turns into a big monolithic mess. Hal it's an enumerator of non-enumerable devices.
On my mips board (without PCIe) it's almost all controllers.