0

After some time trying to write an x86 bootloader and asking my way on here in the process I think I need to take a step back and clear up some misconceptions i have about what I'm actually trying to achieve:

I understand that at the beginning of the boot process, the BIOS reads the first 512 byte from the boot medium and loads them into memory at address 0x7C00 below that from 0x500 downward and above that from 0x80000 upward (give or take) there is BIOS/video specific stuff loaded into memory that a bootloader should not touch.

But I'm not sure how the situation changes when the bootloader switches into protected mode. Say I do not relocate the bootloader code before doing this, I just disable interrupts, enable A20 and then directly jump into protected mode code with a GDT that is as simple as possible, i.e. one code and one data segment, both covering the whole 4GiB address space.

After I have done that, I assume my bootloader code is still located at 0x7C00. Can I then just move it anywhere I want before loading my kernel into memory? I.e. at 0x0? What about my stack, do I just let it start at 0xFFFFFFFF? Of course later I would want to set up paging but I'm not sure how to lay out my memory before I get to that point. Or is my approach suboptimal anyways and I should set up my GDT differently from the start? If so, what would be a "typical" way to do this, separate code and data segments together encompassing all memory addresses? Or even a separate stack segment?

Peter
  • 2,919
  • 1
  • 16
  • 35
  • 3
    Memory doesn't change when changing mode. You just need a "fixed reference" since the way addresses are calculated changes with modes. Since you are using a flat mapping (0-4GiB), you just need to translate a real mode logical address into its linear form (eg: 1111:2222 -> 13332). The stack must be located at an address where there actually is the RAM, 0xFFFFFFFF is always mapped to the flash-ROM so it's not writable and all your push would be silenty discarded. The usual way is a code and data segment encompassing all the address space. – Margaret Bloom Nov 20 '20 at 16:19
  • 2
    If you create a static binary that has an origin point of 0x7c00 then it must be loaded into memory at address 0x7c00 unless you find some way of writing position independent code. If you want to load a kernel at 0x20000 (as an example)then you must set that as your origin point in the linker script.Don't set the stack to an odd address and not all memory can be written to. 0xFFFFFFFF will likely be non writeable and contain a copy of the BIOS. Usually the best place is to load starting at 0x100000 since it avoids all the reserved spaced in the first 1MiB including the BIOS/Video Ram/EBDA etc. – Michael Petch Nov 20 '20 at 16:19
  • @MargaretBloom I think I understand now, I shouldn't map anything into the 0xA0000-0x100000 address range and I also can't map anything above the end of physical RAM. So then it seems to me like it would make the most sense to load the kernel to 0x100000. – Peter Nov 20 '20 at 16:57
  • @MichaelPetch But what addresses below 0xA0000 can actually use then? I assume I have to preserve real mode IVT and (E)BDA if I ever want to switch back to real mode, right? So can I relocate the bootloader to just below the EBDA and set esp to point below the bootloader code? – Peter Nov 20 '20 at 16:57
  • 1
    If you are on a system with a minimum of 1MiB of memory then all the space between 0x500 and up to the start of the EBDA. The EBDA is just below 0xA0000 and the size varies, but the location can be found (assuming no BIOS bug) by looking at the BDA that starts at address 0x00400 and in particular the 16-bit word at 0x0040e has the start segment of the EBDA. Alternatively the 16-bit word at 0x00413 contains the number of free kilobytes from beginning of memory at 0x00000 up to the start of the EBDA. This value is often 639 (639K) as the EBDA on many systems is 1KiB in size. – Michael Petch Nov 20 '20 at 17:06

0 Answers0