| This page provides a high-level description of some of the major code |
| phases that SeaBIOS transitions through and general information on |
| overall code flow. |
| |
| SeaBIOS code phases |
| =================== |
| |
| The SeaBIOS code goes through a few distinct code phases during its |
| execution lifecycle. Understanding these code phases can help when |
| reading and enhancing the code. |
| |
| POST phase |
| ---------- |
| |
| The Power On Self Test (POST) phase is the initialization phase of the |
| BIOS. This phase is entered when SeaBIOS first starts execution. The |
| goal of the phase is to initialize internal state, initialize external |
| interfaces, detect and setup hardware, and to then start the boot |
| phase. |
| |
| On emulators, this phase starts when the CPU starts execution in 16bit |
| mode at 0xFFFF0000:FFF0. The emulators map the SeaBIOS binary to this |
| address, and SeaBIOS arranges for romlayout.S:reset_vector() to be |
| present there. This code calls romlayout.S:entry_post() which then |
| calls post.c:handle_post() in 32bit mode. |
| |
| On coreboot, the build arranges for romlayout.S:entry_elf() to be |
| called in 32bit mode. This then calls post.c:handle_post(). |
| |
| On CSM, the build arranges for romlayout.S:entry_csm() to be called |
| (in 16bit mode). This then calls csm.c:handle_csm() in 32bit mode. |
| Unlike on the emulators and coreboot, the SeaBIOS CSM POST phase is |
| orchestrated with UEFI and there are several calls back and forth |
| between SeaBIOS and UEFI via handle_csm() throughout the POST |
| process. |
| |
| The POST phase itself has several sub-phases. |
| |
| * The "preinit" sub-phase: code run prior to [code relocation](Linking overview#Code relocation). |
| * The "init" sub-phase: code to initialize internal variables and |
| interfaces. |
| * The "setup" sub-phase: code to setup hardware and drivers. |
| * The "prepboot" sub-phase: code to finalize interfaces and prepare |
| for the boot phase. |
| |
| At completion of the POST phase, SeaBIOS invokes an "int 0x19" |
| software interrupt in 16bit mode which begins the boot phase. |
| |
| Boot phase |
| ---------- |
| |
| The goal of the boot phase is to load the first portion of the |
| operating system's boot loader into memory and start execution of that |
| boot loader. This phase starts when a software interrupt ("int 0x19" |
| or "int 0x18") is invoked. The code flow starts in 16bit mode in |
| romlayout.S:entry_19() or romlayout.S:entry_18() which then |
| transition to 32bit mode and call boot.c:handle_19() or |
| boot.c:handle_18(). |
| |
| The boot phase is technically also part of the "runtime" phase of |
| SeaBIOS. It is typically invoked immediately after the POST phase, |
| but it can also be invoked by an operating system or be invoked |
| multiple times in an attempt to find a valid boot media. Although the |
| boot phase C code runs in 32bit mode it does not have write access to |
| the 0x0f0000-0x100000 memory region and can not call the various |
| malloc_X() calls. See [Memory Model](Memory Model) for |
| more information. |
| |
| Main runtime phase |
| ------------------ |
| |
| The main runtime phase occurs after the boot phase starts the |
| operating system. Once in this phase, the SeaBIOS code may be invoked |
| by the operating system using various 16bit and 32bit calls. The goal |
| of this phase is to support these legacy calling interfaces and to |
| provide compatibility with BIOS standards. There are multiple entry |
| points for the BIOS - see the entry_XXX() assembler functions in |
| romlayout.S. |
| |
| Callers use most of these legacy entry points by setting up a |
| particular CPU register state, invoking the BIOS, and then inspecting |
| the returned CPU register state. To handle this, SeaBIOS will backup |
| the current register state into a "struct bregs" (see romlayout.S, |
| entryfuncs.S, and bregs.h) on call entry and then pass this struct to |
| the C code. The C code can then inspect the register state and modify |
| it. The assembler entry functions will then restore the (possibly |
| modified) register state from the "struct bregs" on return to the |
| caller. |
| |
| Resume and reboot |
| ----------------- |
| |
| As noted above, on emulators SeaBIOS handles the 0xFFFF0000:FFF0 |
| machine startup execution vector. This vector is also called on |
| machine faults and on some machine "resume" events. It can also be |
| called (as 0xF0000:FFF0) by software as a request to reboot the |
| machine (on emulators, coreboot, and CSM). |
| |
| The SeaBIOS "resume and reboot" code handles these calls and attempts |
| to determine the desired action of the caller. Code flow starts in |
| 16bit mode in romlayout.S:reset_vector() which calls |
| romlayout.S:entry_post() which calls romlayout.S:entry_resume() which |
| calls resume.c:handle_resume(). Depending on the request the |
| handle_resume() code may transition to 32bit mode. |
| |
| Technically this code is part of the "runtime" phase, so even though |
| parts of it run in 32bit mode it still has the same limitations of the |
| runtime phase. |
| |
| Threads |
| ======= |
| |
| Internally SeaBIOS implements a simple cooperative multi-tasking |
| system. The system works by giving each "thread" its own stack, and |
| the system round-robins between these stacks whenever a thread issues |
| a yield() call. This "threading" system may be more appropriately |
| described as [coroutines](http://en.wikipedia.org/wiki/Coroutine). |
| These "threads" do not run on multiple CPUs and are not preempted, so |
| atomic memory accesses and complex locking is not required. |
| |
| The goal of these threads is to reduce overall boot time by |
| parallelizing hardware delays. (For example, by allowing the wait for |
| an ATA hard drive to spin-up and respond to commands to occur in |
| parallel with the wait for a PS/2 keyboard to respond to a setup |
| command.) These hardware setup threads are only available during the |
| "setup" sub-phase of the [POST phase](#POST_phase). |
| |
| The code that implements threads is in stacks.c. |
| |
| Hardware interrupts |
| =================== |
| |
| The SeaBIOS C code always runs with hardware interrupts disabled. All |
| of the C code entry points (see romlayout.S) are careful to explicitly |
| disable hardware interrupts (via "cli"). Because running with |
| interrupts disabled increases interrupt latency, any C code that could |
| loop for a significant amount of time (more than about 1 ms) should |
| periodically call yield(). The yield() call will briefly enable |
| hardware interrupts to occur, then disable interrupts, and then resume |
| execution of the C code. |
| |
| There are two main reasons why SeaBIOS always runs C code with |
| interrupts disabled. The first reason is that external software may |
| override the default SeaBIOS handlers that are called on a hardware |
| interrupt event. Indeed, it is common for DOS based applications to do |
| this. These legacy third party interrupt handlers may have |
| undocumented expectations (such as stack location and stack size) and |
| may attempt to call back into the various SeaBIOS software services. |
| Greater compatibility and more reproducible results can be achieved by |
| only permitting hardware interrupts at specific points (via yield() |
| calls). The second reason is that much of SeaBIOS runs in 32bit mode. |
| Attempting to handle interrupts in both 16bit mode and 32bit mode and |
| switching between modes to delegate those interrupts is an unneeded |
| complexity. Although disabling interrupts can increase interrupt |
| latency, this only impacts legacy systems where the small increase in |
| interrupt latency is unlikely to be noticeable. |
| |
| Extra 16bit stack |
| ================= |
| |
| SeaBIOS implements 16bit real mode handlers for both hardware |
| interrupts and software request "interrupts". In a traditional BIOS, |
| these requests would use the caller's stack space. However, the |
| minimum amount of space the caller must provide has not been |
| standardized and very old DOS programs have been observed to allocate |
| very small amounts of stack space (100 bytes or less). |
| |
| By default, SeaBIOS now switches to its own stack on most 16bit real |
| mode entry points. This extra stack space is allocated in ["low |
| memory"](Memory Model). It ensures SeaBIOS uses a minimal amount of a |
| callers stack (typically no more than 16 bytes) for these legacy |
| calls. (More recently defined BIOS interfaces such as those that |
| support 16bit protected and 32bit protected mode calls standardize a |
| minimum stack size with adequate space, and SeaBIOS generally will not |
| use its extra stack in these cases.) |
| |
| The code to implement this stack "hopping" is in romlayout.S and in |
| stacks.c. |