kzinti wrote:
One thing I could never find a satisfactory answer to is where is UEFI allowed to load your bootloader in memory. Clearly a PE file can be relocated and I have tested that this is the case on 3 computers + QEMU. In all cases, by 0-based image gets relocated "somewhere" that is different on each machine.
Yep, that is the entire definition you get. UEFI will put your bootloader anywhere in memory. The only guarantees you have are that physical address == virtual address.
What Linux does to work around this is to relocate. It is normal now to have a compressed kernel (in fact, building an uncompressed kernel with UEFI stub is unsupported right now). Compressed kernels work by having a decompressor unpack the raw kernel image. The decompressor is mostly position independent (unlike the rest of the kernel). It will detect if it is in the way of the uncompressed kernel and relocate itself elsewhere if so.
kzinti wrote:
Now it so happened that Windows and Linux and any other sane OS probably wants to be at the end of the address space, but I don't know that's always true especially on non-PC platforms.
On PowerPC, every operating system wants to be loaded at the start of physical address space. This is because all exceptions disable paging there, and then jump to predetermined addresses in the first 12kB of address space (unless a certain bit is set in the MSR, in which case the exception vectors are in the first 12kB of the last megabyte of address space). Therefore most OSes have a 12kB block of exception handlers at the start, to be copied to the start of address space, which is easiest if the entire kernel image is just loaded there. I am, however, unaware of a UEFI implementation for PowerPC.
Back to the PC: My kernel does not care where it is loaded in physical memory, or even if it is contiguous. The bootloader gives a list of reserved physical memory ranges to the kernel, and the kernel only knows that those ranges are unavailable, for whatever reason. Since in my case, the bootloader turns on the virtual memory (that is, it switches over to my kernel's paging scheme), the kernel itself does not have to know.
kzinti wrote:
One could detect this and relocate some trampoline code out of the way, but that's pain I'd rather avoid if it is not necessary.
Oh, come on, it is not that painful. My bootloader turns on virtual memory as the very last thing before invoking the kernel. Therefore the code that does that is mostly self-contained. So why not just compile the trampoline code into a raw binary, then tack that binary into the bootloader as data? Then you can just copy it always. You need to know where the trampoline will end up, anyway, for the identity-map. So instead of putting it in a constant address, you use as base address whatever the allocator returns to you. Copy, set function pointer, call, done.