A 64-bit X86 Bootloader From Scratch

For most people, you turn on your computer, and it starts the operating system. However, the reality is much more complex as [Thasso] discovered. Even modern x86 chips start in 16-bit real mode and there is a bit of fancy footwork required to shift to modern protected mode with full 64-bit support. Want to see how? [Thasso] shows us the ropes.

Nowadays, it is handy to develop such things because you don’t have to use real hardware. An emulator like QEMU will suffice. If you know assembly language, the process is surprisingly simple, although there is a lot of nuance and subtlety. The biggest task is setting up appropriate paging tables to control the memory mapping. In real mode, segments have access to fixed 64 K blocks of memory unless you use some tricks. But in protected mode, segments define blocks of memory that can be very small or cover the entire address space. These segments define areas of memory even though it is possible to set segments to cover all memory and — sort of — ignore them. You still have to define them for the switch to protected mode.

In the bad old days, you had more reason to worry about this if you were writing a DOS Extender or using some tricks to get access to more memory. But still good to know if you are rolling your own operating system. Why do the processors still boot into real mode? Good question.

20 thoughts on “A 64-bit X86 Bootloader From Scratch

  1. 16-bit real mode is base code at the hardware level, hence why it’s still used for the absolute lowest level functions like booting. 32-bit and 64-bit code is ran in protected states meaning less access to resources directly without an API or HAL. Even UEFI has to be able to use read-write in real mode to ensure compatibility before handing it off to 32 or 64 bit processes.

  2. In real mode, you have access to the first 64 K of memory

    640 K

    Why do the processors still boot into real mode?

    On most platforms they don’t (on some they may) because of UEFI.

  3. In real mode you have access to 1MB of memory. e.g

    MOV DS,#((Full20BitAddr>>4)&0xf000)
    MOV AX,Full20BitAddr&0xffff

    Loads AX with a value anywhere in the 1MB region (gas-ish notation). It’s not a trick a such, since you have to set up the segment registers anyway.

      1. No worries, you did a good job I think. 🙂
        When it comes to topics like this, there’s always a high possibility someone says “yes, but..”
        And I’m not excluding myself from the list here. 😅
        It’s simply to tempting not to mention all the little details, I suppose.

    1. 1024KB + 64KB (-16 Bytes) if we’re nitpicking.
      In PC terminology, first ~64KB of Extended Memory is the so-called High Memory Area (HMA).
      That’s why A20 gate exist, to handle address wrap-around.

      It’s being needed by very old DOS programs foremost. Like those from mid-80s, who were ported over from CP/M (see CALL5 interface).
      – MS-DOS 5/6 itself can work with either state of the A20 Gate, it’s not that picky.
      It’s possible to check this with modern PCs or PC virtualizers with broken A20 emulation.

      Processor caches and DOS Extenders are a different matter, of course.
      They may depend on A20 Gate.

      Memory managers like EMM386 or QEMM do use V86 Mode and can (must!) emulate the A20 Gate, so the real A20 Gate isn’t so important anymore.

      That’s also lesser being known, by the way.
      Removal of A20 Gate in late x86 PCs was a non-issue once any type of DOS-virtualization comes into play.

      Speaking under correction.

      1. I couldn’t remember if gate A20 is enabled by default when a PC boots in real-mode, so I fat-fingered it ;-) !

        So, I guess that if it’s removed then memory will wrap around on boot? Otherwise it wouldn’t be possible to boot an early MSDOS on a current PC (though, being a Mac person I haven’t tried and I guess I could look it up, but I like typing comments).

      1. It adds unnecessary complexity and is insecure, as contradictory as this may sound.

        In essence, UEFI is a “miniature” OS (miniature for an OS, bloated for a BIOS) and thus neefs same level of anti-malware software.

        It would require weekly updates, an anti-virus guard, a firewall etc.

        Secondly, UEFI is not trustworthy.
        It’s on the lowest level and can/does lie to the PC operating system.

        It has drivers to integrated hardware, including the on-board USB3 controller and network card (NIC).

        It can thus phone home without anyone noticing, except by checking the traffic LEDs near the ethernet socket.

        The chipset on motherboard also is a similar threat this day, since it does tap the 5v standby power.
        It’s always being powered, even if the PC is off.
        See articles about Minix running inside intel chipsets.

        Last but not least had UEFI removed CSM, the Compatibility Support Module.
        CSM was a playload that carried the PC BIOS.
        It’s removal not only cut backwards compatibility to all previous PC generations, but also took away freedom.

        PC BIOS (or AT BIOS) allowed homebrew and put no restrictions to the software.
        It also was independent of a specific company, anyone could write a BIOS – the API/ABI was thoroughly being documented.

        In short, the user was free to do anything he/she/they wanted to do, boot into any OS no matter if old or new.

        BIOS allowed booting into Windows 98SE, for example.
        Here, it provided basic functionality. The VGA BIOS on graphics card did complete this basic functionality.

        In my opinion, UEFI was a mistake.
        Open Firmware or CoreBoot+SeaBIOS would have been better alternatives to it.

        Sure, UEFI is very feature rich and has a shiny GUI. But all it’s security features aren’t meant to protect the user.
        They’re rather meant to protect intel’s interests and the PC manufacturers, I think.

        PS: Back in the 90s, there was a graphical BIOS already (WinBIOS).
        The GUI of its setup utility supported both text-mode and VGA.
        And it did auto-detect the mouse, too!

        So UEFI wasn’t even need for these bells and whistles.
        Any firmware could have had a graphical interface, even old BIOS.

        1. I certainly miss open-firmware on Macs! (I think the OLPC used OF too and of course Sun workstations had an earlier version). I used OF to boot my iMac G3 (slot-loader) from USB recently to install a version of Debian that was bigger than the CD could store.

      2. UEFI means a PC is “turtles all the way down” …

        I need an operating system (UEFI) to boot my operating system (Linux).

        Also my Intel Management Engine has an operating system (Minix).
        And GRUB2 has loadable modules and a file system interface. So maybe it’s an operating system too.
        My WiFi chipset probably runs an RTOS. (FreeRTOS? ThreadX? I have no idea)

        My real complaint about UEFI is that it is quite complicated considering that the OS doesn’t really need it once the system is booted. Unlike the tighter coupling between BIOS and DOS.
        And yes, I’m over simplifying and ignoring Intel SMM.

    1. Spoiler alert: It’s hard. APs, Local APICs, SIPI, IPI, GDT, IDT… I don’t know an article on this, but there is of course the Linux Kernel (and others) to explore, not for the faint of heart.

  4. > Even modern x86 chips start in 16-bit real mode
    Oh it gets worse. At Intel I used to work the code that verifies the BIOS before loading and executing it. This particular blob was loaded directly by microcode. In order to perform its job, it would immediately switch to 32 bit mode, perform some hashing and signature verification, and then switch *back* into 16-bit mode to allow the BIOS to boot.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.