|
|
1.1 root 1: This code implements an X86 legacy bios. It is intended to be
2: compiled using standard gnu tools (eg, gas and gcc).
3:
4: To build, one should be able to run "make" in the main directory. The
5: resulting file "out/bios.bin" contains the processed bios image.
6:
7:
8: Testing of images:
9:
10: To test the bios under bochs, one will need to instruct bochs to use
11: the new bios image. Use the 'romimage' option - for example:
12:
13: bochs -q 'floppya: 1_44=myfdimage.img' 'romimage: file=out/bios.bin'
14:
15: To test under qemu, one will need to create a directory with all the
16: bios images and then overwrite the main bios image. For example:
17:
18: cp /usr/share/qemu/*.bin mybiosdir/
19: cp out/bios.bin mybiosdir/
20:
21: Once this is setup, one can instruct qemu to use the newly created
22: directory for rom images. For example:
23:
24: qemu -L mybiosdir/ -fda myfdimage.img
25:
26:
27: The following payloads have been tested:
28:
29: Freedos - see http://www.freedos.org/ . Useful tests include: booting
30: from installation cdrom, installing to hard drive and floppy, making
31: sure hard drive and floppy boots then work. It is also useful to take
32: the bootable floppy and hard-drive images, write them to an el-torito
33: bootable cdrom using the Linux mkisofs utility, and then boot those
34: cdrom images.
35:
36: Linux - useful hard drive image available from
37: http://fabrice.bellard.free.fr/qemu/linux-0.2.img.bz2 . It is also
38: useful to test standard distribution bootup and live cdroms.
39:
40: NetBSD - useful hard drive image available from
41: http://nopid.free.fr/small.ffs.bz2 . It is also useful to test
42: standard distribution installation cdroms.
43:
44:
45: Overview of files:
46:
47: The src/ directory contains the bios source code. Several of the
48: files are compiled twice - once for 16bit mode and once for 32bit
49: mode. (The build system will remove code that is not needed for a
50: particular mode.)
51:
52: The tools/ directory contains helper utilities for manipulating and
53: building the final rom.
54:
55: The out/ directory is created by the build process - it contains all
56: temporary and final files.
57:
58:
59: Build overview:
60:
61: The 16bit code is compiled via gcc to assembler (file out/ccode.16.s).
62: The gcc "-fwhole-program" and "-ffunction-sections -fdata-sections"
63: options are used to optimize the process so that gcc can efficiently
64: compile and discard unneeded code. (In the code, one can use the
65: macros 'VISIBLE16' and 'VISIBLE32' to instruct a symbol to be
66: outputted in 16bit and 32bit mode respectively.)
67:
68: This resulting assembler code is pulled into romlayout.S. The gas
69: option ".code16gcc" is used prior to including the gcc generated
70: assembler - this option enables gcc to generate valid 16 bit code.
71:
72: The post code (post.c) is entered, via the function _start(), in 32bit
73: mode. The 16bit post vector (in romlayout.S) transitions the cpu into
74: 32 bit mode before calling the post.c code.
75:
76: In the last step of compilation, the 32 bit code is merged into the 16
77: bit code so that one binary file contains both. Currently, both 16bit
78: and 32bit code will be located in the 64K block at segment 0xf000.
79:
80:
81: GCC 16 bit limitations:
82:
83: Although the 16bit code is compiled with gcc, developers need to be
84: aware of the environment. In particular, global variables _must_ be
85: treated specially.
86:
87: The code has full access to stack variables and general purpose
88: registers. The entry code in romlayout.S will push the original
89: registers on the stack before calling the C code and then pop them off
90: (including any required changes) before returning from the interrupt.
91: Changes to CS, DS, and ES segment registers in C code is also safe.
92: Changes to other segment registers (SS, FS, GS) need to be restored
93: manually.
94:
95: Stack variables (and pointers to stack variables) work as they
96: normally do in standard C code.
97:
98: However, variables stored outside the stack need to be accessed via
99: the GET_VAR and SET_VAR macros (or one of the helper macros described
100: below). This is due to the 16bit segment nature of the X86 cpu when
101: it is in "real mode". The C entry code will set DS and SS to point to
102: the stack segment. Variables not on the stack need to be accessed via
103: an explicit segment register. Any other access requires altering one
104: of the other segment registers (usually ES) and then accessing the
105: variable via that segment register.
106:
107: There are three low-level ways to access a remote variable:
108: GET/SET_VAR, GET/SET_FARVAR, and GET/SET_FLATPTR. The first set takes
109: an explicit segment descriptor (eg, "CS") and offset. The second set
110: will take a segment id and offset, set ES to the segment id, and then
111: make the access via the ES segment. The last method is similar to the
112: second, except it takes a pointer that would be valid in 32-bit flat
113: mode instead of a segment/offset pair.
114:
115: Most BIOS variables are stored in global variables, the "BDA", or
116: "EBDA" memory areas. Because this is common, three sets of helper
117: macros (GET/SET_GLOBAL, GET/SET_BDA, and GET/SET_EBDA) are available
118: to simplify these accesses.
119:
120: Global variables defined in the C code can be read in 16bit mode if
121: the variable declaration is marked with VAR16, VAR16VISIBLE,
122: VAR16EXPORT, or VAR16FIXED. The GET_GLOBAL macro will then allow read
123: access to the variable. Global variables are stored in the 0xf000
124: segment, and their values are persistent across soft resets. Because
125: the f-segment is marked read-only during run-time, the 16bit code is
126: not permitted to change the value of 16bit variables (use of the
127: SET_GLOBAL macro from 16bit mode will cause a link error). Code
128: running in 32bit mode can not access variables with VAR16, but can
129: access variables marked with VAR16VISIBLE, VAR16EXPORT, VAR16FIXED, or
130: with no marking at all. The 32bit code can use the GET/SET_GLOBAL
131: macros, but they are not required.
132:
133:
134: GCC 16 bit stack limitations:
135:
136: Another limitation of gcc is its use of 32-bit temporaries. Gcc will
137: allocate 32-bits of space for every variable - even if that variable
138: is only defined as a 'u8' or 'u16'. If one is not careful, using too
139: much stack space can break old DOS applications.
140:
141: There does not appear to be explicit documentation on the minimum
142: stack space available for bios calls. However, Freedos has been
143: observed to call into the bios with less than 150 bytes available.
144:
145: Note that the post code and boot code (irq 18/19) do not have a stack
146: limitation because the entry points for these functions transition the
147: cpu to 32bit mode and reset the stack to a known state. Only the
148: general purpose 16-bit service entry points are affected.
149:
150: There are some ways to reduce stack usage: making sure functions are
151: tail-recursive often helps, reducing the number of parameters passed
152: to functions often helps, sometimes reordering variable declarations
153: helps, inlining of functions can sometimes help, and passing of packed
154: structures can also help. It is also possible to transition to/from
155: an extra stack stored in the EBDA using the stack_hop helper function.
156:
157: Some useful stats: the overhead for the entry to a bios handler that
158: takes a 'struct bregs' is 42 bytes of stack space (6 bytes from
159: interrupt insn, 32 bytes to store registers, and 4 bytes for call
160: insn). An entry to an ISR handler without args takes 30 bytes (6 + 20
161: + 4).
162:
163:
164: Debugging the bios:
165:
166: The bios will output information messages to a special debug port.
167: Under qemu, one can view these messages by enabling the '#define
168: DEBUG_BIOS' definition in 'qemu/hw/pc.c'. Once this is done (and qemu
169: is recompiled), one should see status messages on the console.
170:
171: The gdb-server mechanism of qemu is also useful. One can use gdb with
172: qemu to debug system images. To use this, add '-s -S' to the qemu
173: command line. For example:
174:
175: qemu -L mybiosdir/ -fda myfdimage.img -s -S
176:
177: Then, in another session, run gdb with either out/rom16.o (to debug
178: bios 16bit code) or out/rom32.o (to debug bios 32bit code). For
179: example:
180:
181: gdb out/rom16.o
182:
183: Once in gdb, use the command "target remote localhost:1234" to have
184: gdb connect to qemu. See the qemu documentation for more information
185: on using gdb and qemu in this mode. Note that gdb seems to get
186: breakpoints confused when the cpu is in 16-bit real mode. This makes
187: stepping through the program difficult (though 'step instruction'
188: still works). Also, one may need to set 16bit break points at both
189: the cpu address and memory address (eg, break *0x1234 ; break
190: *0xf1234).
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.