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:
http://git.linuxtogo.org/
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