Hi,
As has been discussed a couple of times on this list, I have been working on a project to port the "bochs bios" to gcc. The code is available via git at:
This email is to go through some of the details of what the "legacybios" code provides and how it works. Note that the code in "legacybios" really is a port of "bochs bios", so the two code bases have nearly identical coverage.
There are three major parts of the "legacybios" code:
* 16bit software interrupt handlers * 16bit hardware interrupt handlers * "post" code
The 16bit software interrupt handlers can be thought of as a code library. They provide a series of standard 'functions' that boot loaders and operating systems may call. Granted, the calling convention of the bios is a bit odd - instead of using 'call' insns one uses 'int' insns, parameters are generally passed in registers, and everything runs in 16bit mode. Fortunately, there is plenty of documentation describing what these calls do - implementing the library itself is not technically challenging.
The 16bit hardware interrupts are like the 16bit software interrupt handlers, except they get called by hardware instead of software. The "legacybios" code only implements enough hardware interrupts to make the standard software interrupt handlers work properly.
Finally, the "post" code is used to initialize the system and start the boot process. This "post" code is compiled in 32bit mode. It is possible to enter the bios in 16bit mode (by jumping to 0xf000:0xfff0), however all this will do is transition the cpu to 32bit mode and then call the 32bit "post" code.
The "post" stage does the following tasks:
* minimal hardware detection and init * option rom scan and execution * pci init * various bios table creation * transition to 16bit mode and raise int19 to start boot process
I suspect the hardware detection and initialization does not have much overlap with coreboot. The most important part of this section is the IDE initialization and disk scan. (Some other code, for example keyboard init, may be redundant when used with coreboot.)
The option rom scan and execution allows the bios code to call out to additional system roms. The "post" stage runs in 32bit mode, but the option roms are called in 16bit mode - the "legacybios" code simply transitions to/from 16bit mode to make these calls. The option rom code is executed natively on the processor.
The "pci init" code looks to be redundant with what coreboot already provides. The code in "legacybios" seems to be intel northbride/soutbridge specific.
The "legacybios" code provides the following memory tables:
* bda, ebda, and 0xf0000 segment * e820 memory map * PIR * mptable * smbios * acpi tables (rsdp, rsdt, fadt, facs, dsdt, madt)
Some old DOS programs make assumptions about specific memory locations in the first 1Meg of ram. The "legacybios" code fills in these areas to make these old programs happy - in short they are the "bios data area" at 0x00000 - 0x00500, the "extended bios data area" at 0x9fc00 - 0xa0000, and some hardcoded locations in the 0xf0000 bios segment itself. The bios working storage areas (in bda and ebda) and the 16bit interrupt vector table is also setup (part of the bda).
The e820 map is populated by assuming a linear memory layout. The maximum memory is passed into the bios (via a cmos variable, see below), and the resulting e820 map is then generated.
The PIR table is simply hard coded at compile time.
The mptable code attempts to auto-detect the number of cpus in the system (by raising a "broadcast SIPI"). The rest of the table is then filled with hardcoded information.
The smbios and acpi tables use the passed in memory size, the detected cpu count, and info from the "cpuid" insn - the rest is hardcoded.
It is not clear to me how much of the acpi/mptable/smbios code is reusable on real hardware. It is possible that most of the info in these tables is irrelevant, and the "legacybios" code may therefore be a good starting point. However, it may also be simpler to have coreboot just pass these tables in.
Hardware assumptions that "legacybios" makes:
* code loaded into 0xf0000, and execution jumps to it (16bit or 32bit) * cmos settings * ioports (debug port) * hardware specific code (ram shadowing, apm)
As mentioned earlier, the bios needs to be loaded into memory starting at 0xf0000 and its init code needs to be run. The rom code is 64K in size (compressed with lzma it is currently 23K).
Certain settings are passed into the bios via cmos variables - qemu and bochs emulators use this to select certain settings. The main variables needed are: memory size, floppy drive type, and boot order. The system also uses variables for harddrive geometry and "equipment list flags", but I believe the code can be extended to autodetect these settings.
Both qemu and bochs implement a debug port (0x0403) that allows the bios to send debug messages to the console. This will not, of course, work on real hardware. It should be possible to send the debug messages to a serial port. The remainder of the ioports used by "legacybios" look to be standard hardware.
Some of the hardware accesses made by the "legacybios" code appear to be specific to hardware in the emulator. The code attempts to enable ram shadowing of the memory segment at 0xf00000 - it does this so that it can put acpi/mptable/smbios tables into that area. After it is complete, it attempts to disable writes to that region. The code sequence looks to be specific to intel north/southbridges. It isn't necessary to disable writes to 0xf0000 (though it would be nice), but it is necessary to have the ability to alter that memory. Also, there are stubs in "legacybios" for apm support - should one want to implement this on real hardware the appropriate power saving code would need to be implemented.
Comments? -Kevin