Hi,
Let's go back to the start...
A kernel needs different boot loaders for different cases:
- for floppy (unpartitioned, with BPB, with old "int 0x13")
- for BIOS MBR partitioned hard disk (MBR partitioned, without BPB, with "int 0x13 extensions")
- for BIOS GPT/hybrid hard disk (GPT partitioned, without BPB, with "int 0x13 extensions")
- for BIOS network boot/PXE (completely different, no disk IO at all)
- for BIOS "no emulation El Torito" CDs
- for 32-bit UEFI
- for 64-bit UEFI
Building a boot loader into the kernel itself is silly. The boot loader and the kernel need to be completely separate; where (hopefully) all the different boot loaders start the same kernel, and where the OS installer is responsible for installing the appropriate boot loader/s onto whatever the boot device is.
Note: For some cases it does make sense to have multiple boot loaders on the same device - e.g. a single CD that contains a UEFI system partition with both 32-bit and 64-bit UEFI boot loaders, plus the BIOS "no emulation El Torito" boot loader, where the firmware auto-selects the boot loader it needs.
Now; given that the boot loader and kernel are completely separate binaries, you can not expect the kernel to be able to call anything in the boot loader directly. You either avoid this (e.g. build "gdt_flush" into your kernel so that kernel doesn't need to call a function in the boot loader); or you need to define some sort of interface/API (e.g. maybe boot loader provides a software interrupt that kernel can use, or maybe boot loader passes a table of function pointers to kernel when it starts the kernel, etc). If the boot loader does provide some sort of interface/API the linker isn't involved (either the kernel doesn't need to know the address of the function in the boot loader, or the kernel is told the address of the function in the boot loader at run-time and not at compile/link time).
Finally; I recommend documenting the requirements for the state of the computer when the boot loader passes control to the kernel. This should be a formal specification; and should include things like:
- what the physical and/or virtual address space will look like when the kernel is started
- which data (memory map, video buffer details, etc) is passed to the kernel and how
- what can the kernel assume about the CPU's state - which mode will the CPU be in, what will registers contain, can the kernel assume FPU has been initialised, is there a GDT or IDT, will caches be enabled, will MTRRs be configured, will IRQs be enabled, etc.
- the state/s of all other hardware - e.g. is A20 guaranteed to be enabled, what state are other CPUs in, are PCI devices guaranteed to be correctly preconfigured (and how) or in an undefined state, what about PIC/IO APIC (and local APIC), etc.
- if the boot loader has to provide some sort of interface/API, then that has to be included too. What CPU mode is used for the interface/API and which calling conventions. For each function the interface/API provides; what do they do, what are the input and output parameters, what are the limitations.
In general, someone should be able to download your specification and implement a boot loader that works with your kernel/s; without ever talking to you and without ever downloading or seeing any of your source code.
Cheers,
Brendan