|
|
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.