|
|
1.1 ! root 1: Etherboot/NILO i386 initialisation path and external call interface ! 2: =================================================================== ! 3: ! 4: 1. Background ! 5: ! 6: GCC compiles 32-bit code. It is capable of producing ! 7: position-independent code, but the resulting binary is about 25% ! 8: bigger than the corresponding fixed-position code. Since one main use ! 9: of Etherboot is as firmware to be burned into an EPROM, code size must ! 10: be kept as small as possible. ! 11: ! 12: This means that we want to compile fixed-position code with GCC, and ! 13: link it to have a predetermined start address. The problem then is ! 14: that we must know the address that the code will be loaded to when it ! 15: runs. There are several ways to solve this: ! 16: ! 17: 1. Pick an address, link the code with this start address, then make ! 18: sure that the code gets loaded at that location. This is ! 19: problematic, because we may pick an address that we later end up ! 20: wanting to use to load the operating system that we're booting. ! 21: ! 22: 2. Pick an address, link the code with this start address, then set up ! 23: virtual addressing so that the virtual addresses match the ! 24: link-time addresses regardless of the real physical address that ! 25: the code is loaded to. This enables us to relocate Etherboot to ! 26: the top of high memory, where it will be out of the way of any ! 27: loading operating system. ! 28: ! 29: 3. Link the code with a text start address of zero and a data start ! 30: address also of zero. Use 16-bit real mode and the ! 31: quasi-position-independence it gives you via segment addressing. ! 32: Doing this requires that we generate 16-bit code, rather than ! 33: 32-bit code, and restricts us to a maximum of 64kB in each segment. ! 34: ! 35: There are other possible approaches (e.g. including a relocation table ! 36: and code that performs standard dynamic relocation), but the three ! 37: options listed above are probably the best available. ! 38: ! 39: Etherboot can be invoked in a variety of ways (ROM, floppy, as a PXE ! 40: NBP, etc). Several of these ways involve control being passed to ! 41: Etherboot with the CPU in 16-bit real mode. Some will involve the CPU ! 42: being in 32-bit protected mode, and there's an outside chance that ! 43: some may involve the CPU being in 16-bit protected mode. We will ! 44: almost certainly have to effect a CPU mode change in order to reach ! 45: the mode we want to be in to execute the C code. ! 46: ! 47: Additionally, Etherboot may wish to call external routines, such as ! 48: BIOS interrupts, which must be called in 16-bit real mode. When ! 49: providing a PXE API, Etherboot must provide a mechanism for external ! 50: code to call it from 16-bit real mode. ! 51: ! 52: Not all i386 builds of Etherboot will want to make real-mode calls. ! 53: For example, when built for LinuxBIOS rather than the standard PCBIOS, ! 54: no real-mode calls are necessary. ! 55: ! 56: For the ultimate in PXE compatibility, we may want to build Etherboot ! 57: to run permanently in real mode. ! 58: ! 59: There is a wide variety of potential combinations of mode switches ! 60: that we may wish to implement. There are additional complications, ! 61: such as the inability to access a high-memory stack when running in ! 62: real mode. ! 63: ! 64: 2. Transition libraries ! 65: ! 66: To handle all these various combinations of mode switches, we have ! 67: several "transition" libraries in Etherboot. We also have the concept ! 68: of an "internal" and an "external" environment. The internal ! 69: environment is the environment within which we can execute C code. ! 70: The external environment is the environment of whatever external code ! 71: we're trying to interface to, such as the system BIOS or a PXE NBP. ! 72: ! 73: As well as having a separate addressing scheme, the internal ! 74: environment also has a separate stack. ! 75: ! 76: The transition libraries are: ! 77: ! 78: a) librm ! 79: ! 80: librm handles transitions between an external 16-bit real-mode ! 81: environment and an internal 32-bit protected-mode environment with ! 82: virtual addresses. ! 83: ! 84: b) libkir ! 85: ! 86: libkir handles transitions between an external 16-bit real-mode (or ! 87: 16:16 or 16:32 protected-mode) environment and an internal 16-bit ! 88: real-mode (or 16:16 protected-mode) environment. ! 89: ! 90: c) libpm ! 91: ! 92: libpm handles transitions between an external 32-bit protected-mode ! 93: environment with flat physical addresses and an internal 32-bit ! 94: protected-mode environment with virtual addresses. ! 95: ! 96: The transition libraries handle the transitions required when ! 97: Etherboot is started up for the first time, the transitions required ! 98: to execute any external code, and the transitions required when ! 99: Etherboot exits (if it exits). When Etherboot provides a PXE API, ! 100: they also handle the transitions required when a PXE client makes a ! 101: PXE API call to Etherboot. ! 102: ! 103: Etherboot may use multiple transition libraries. For example, an ! 104: Etherboot ELF image does not require librm for its initial transitions ! 105: from prefix to runtime, but may require librm for calling external ! 106: real-mode functions. ! 107: ! 108: 3. Setup and initialisation ! 109: ! 110: Etherboot is conceptually divided into the prefix, the decompressor, ! 111: and the runtime image. (For non-compressed images, the decompressor ! 112: is a no-op.) The complete image comprises all three parts and is ! 113: distinct from the runtime image, which exclude the prefix and the ! 114: decompressor. ! 115: ! 116: The prefix does several tasks: ! 117: ! 118: Load the complete image into memory. (For example, the floppy ! 119: prefix issues BIOS calls to load the remainder of the complete image ! 120: from the floppy disk into RAM, and the ISA ROM prefix copies the ROM ! 121: contents into RAM for faster access.) ! 122: ! 123: Call the decompressor, if the runtime image is compressed. This ! 124: decompresses the runtime image. ! 125: ! 126: Call the runtime image's setup() routine. This is a routine ! 127: implemented in assembly code which sets up the internal environment ! 128: so that C code can execute. ! 129: ! 130: Call the runtime image's arch_initialise() routine. This is a ! 131: routine implemented in C which does some basic startup tasks, such ! 132: as initialising the console device, obtaining a memory map and ! 133: relocating the runtime image to high memory. ! 134: ! 135: Call the runtime image's arch_main() routine. This records the exit ! 136: mechanism requested by the prefix and calls main(). (The prefix ! 137: needs to register an exit mechanism because by the time main() ! 138: returns, the memory occupied by the prefix has most likely been ! 139: overwritten.) ! 140: ! 141: When acting as a PXE ROM, the ROM prefix contains an UNDI loader ! 142: routine in addition to its usual code. The UNDI loader performs a ! 143: similar sequence of steps: ! 144: ! 145: Load the complete image into memory. ! 146: ! 147: Call the decompressor. ! 148: ! 149: Call the runtime image's setup() routine. ! 150: ! 151: Call the runtime image's arch_initialise() routine. ! 152: ! 153: Call the runtime image's install_pxe_stack() routine. ! 154: ! 155: Return to caller. ! 156: ! 157: The runtime image's setup() routine will perform the following steps: ! 158: ! 159: Switch to the internal environment using an appropriate transition ! 160: library. This will record the parameters of the external ! 161: environment. ! 162: ! 163: Set up the internal environment: load a stack, and set up a GDT for ! 164: virtual addressing if virtual addressing is to be used. ! 165: ! 166: Switch back to the external environment using the transition ! 167: library. This will record the parameters of the internal ! 168: environment. ! 169: ! 170: Once the setup() routine has returned, the internal environment has been ! 171: set up ready for C code to run. The prefix can call C routines using ! 172: a function from the transition library. ! 173: ! 174: The runtime image's arch_initialise() routine will perform the ! 175: following steps: ! 176: ! 177: Zero the bss ! 178: ! 179: Initialise the console device(s) and print a welcome message. ! 180: ! 181: Obtain a memory map via the INT 15,E820 BIOS call or suitable ! 182: fallback mechanism. [not done if libkir is being used] ! 183: ! 184: Relocate the runtime image to the top of high memory. [not done if ! 185: libkir is being used] ! 186: ! 187: Install librm to base memory. [done only if librm is being used] ! 188: ! 189: Call initialise(). ! 190: ! 191: Return to the prefix, setting registers to indicate to the prefix ! 192: the new location of the transition library, if applicable. Which ! 193: registers these are is specific to the transition library being ! 194: used. ! 195: ! 196: Once the arch_initialise() routine has returned, the prefix will ! 197: probably call arch_main().
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.