Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 1 | This code implements an X86 legacy bios. It is intended to be |
| 2 | compiled using standard gnu tools (eg, gas and gcc). |
| 3 | |
Kevin O'Connor | 8744e15 | 2013-10-14 21:52:28 -0400 | [diff] [blame^] | 4 | To build for QEMU, one should be able to run "make" in the main |
| 5 | directory. The resulting file "out/bios.bin" contains the processed |
| 6 | bios image. To build for coreboot, please see the coreboot wiki. To |
| 7 | build for CSM, please see README.CSM. |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 8 | |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 9 | |
| 10 | Testing of images: |
| 11 | |
| 12 | To test the bios under bochs, one will need to instruct bochs to use |
| 13 | the new bios image. Use the 'romimage' option - for example: |
| 14 | |
Kevin O'Connor | 59fead6 | 2008-05-10 15:49:20 -0400 | [diff] [blame] | 15 | bochs -q 'floppya: 1_44=myfdimage.img' 'romimage: file=out/bios.bin' |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 16 | |
| 17 | To test under qemu, one will need to create a directory with all the |
| 18 | bios images and then overwrite the main bios image. For example: |
| 19 | |
| 20 | cp /usr/share/qemu/*.bin mybiosdir/ |
Kevin O'Connor | 59fead6 | 2008-05-10 15:49:20 -0400 | [diff] [blame] | 21 | cp out/bios.bin mybiosdir/ |
Kevin O'Connor | ac467be | 2013-03-17 10:29:06 -0400 | [diff] [blame] | 22 | cp out/*.aml mybiosdir/ |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 23 | |
| 24 | Once this is setup, one can instruct qemu to use the newly created |
| 25 | directory for rom images. For example: |
| 26 | |
| 27 | qemu -L mybiosdir/ -fda myfdimage.img |
| 28 | |
| 29 | |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 30 | Overview of files: |
| 31 | |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 32 | The src/ directory contains the bios source code. Several of the |
| 33 | files are compiled twice - once for 16bit mode and once for 32bit |
Kevin O'Connor | 0942e7f | 2009-06-15 22:27:01 -0400 | [diff] [blame] | 34 | mode. (The build system will remove code that is not needed for a |
| 35 | particular mode.) |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 36 | |
Kevin O'Connor | 1c78711 | 2013-09-13 16:29:11 -0400 | [diff] [blame] | 37 | The vgasrc/ directory contains code for VGA BIOS implementations. |
| 38 | This code is separate from the main BIOS code in the src/ directory. |
| 39 | It produces a VGA BIOS rom in out/vgabios.bin. The VGA BIOS code is |
| 40 | always compiled in 16bit mode. |
| 41 | |
| 42 | The scripts/ directory contains helper utilities for manipulating and |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 43 | building the final rom. |
| 44 | |
| 45 | The out/ directory is created by the build process - it contains all |
| 46 | temporary and final files. |
| 47 | |
| 48 | |
| 49 | Build overview: |
| 50 | |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 51 | The 16bit code is compiled via gcc to assembler (file out/ccode.16.s). |
Kevin O'Connor | 0942e7f | 2009-06-15 22:27:01 -0400 | [diff] [blame] | 52 | The gcc "-fwhole-program" and "-ffunction-sections -fdata-sections" |
| 53 | options are used to optimize the process so that gcc can efficiently |
| 54 | compile and discard unneeded code. (In the code, one can use the |
Kevin O'Connor | 0fdf193 | 2011-10-04 21:12:28 -0400 | [diff] [blame] | 55 | macros 'VISIBLE16' and 'VISIBLE32FLAT' to instruct a symbol to be |
Kevin O'Connor | 0942e7f | 2009-06-15 22:27:01 -0400 | [diff] [blame] | 56 | outputted in 16bit and 32bit mode respectively.) |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 57 | |
| 58 | This resulting assembler code is pulled into romlayout.S. The gas |
| 59 | option ".code16gcc" is used prior to including the gcc generated |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 60 | assembler - this option enables gcc to generate valid 16 bit code. |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 61 | |
Kevin O'Connor | 0fdf193 | 2011-10-04 21:12:28 -0400 | [diff] [blame] | 62 | The post code (post.c) is entered, via the function handle_post(), in |
| 63 | 32bit mode. The 16bit post vector (in romlayout.S) transitions the |
| 64 | cpu into 32 bit mode before calling the post.c code. |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 65 | |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 66 | In the last step of compilation, the 32 bit code is merged into the 16 |
| 67 | bit code so that one binary file contains both. Currently, both 16bit |
Kevin O'Connor | 0fdf193 | 2011-10-04 21:12:28 -0400 | [diff] [blame] | 68 | and 32bit code will be located in the memory at 0xe0000-0xfffff. |
Kevin O'Connor | f076a3e | 2008-02-25 22:25:15 -0500 | [diff] [blame] | 69 | |
| 70 | |
| 71 | GCC 16 bit limitations: |
| 72 | |
| 73 | Although the 16bit code is compiled with gcc, developers need to be |
| 74 | aware of the environment. In particular, global variables _must_ be |
| 75 | treated specially. |
| 76 | |
| 77 | The code has full access to stack variables and general purpose |
| 78 | registers. The entry code in romlayout.S will push the original |
| 79 | registers on the stack before calling the C code and then pop them off |
| 80 | (including any required changes) before returning from the interrupt. |
| 81 | Changes to CS, DS, and ES segment registers in C code is also safe. |
| 82 | Changes to other segment registers (SS, FS, GS) need to be restored |
| 83 | manually. |
| 84 | |
| 85 | Stack variables (and pointers to stack variables) work as they |
| 86 | normally do in standard C code. |
| 87 | |
| 88 | However, variables stored outside the stack need to be accessed via |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 89 | the GET_VAR and SET_VAR macros (or one of the helper macros described |
| 90 | below). This is due to the 16bit segment nature of the X86 cpu when |
| 91 | it is in "real mode". The C entry code will set DS and SS to point to |
| 92 | the stack segment. Variables not on the stack need to be accessed via |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 93 | an explicit segment register. Any other access requires altering one |
| 94 | of the other segment registers (usually ES) and then accessing the |
| 95 | variable via that segment register. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 96 | |
| 97 | There are three low-level ways to access a remote variable: |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 98 | GET/SET_VAR, GET/SET_FARVAR, and GET/SET_FLATPTR. The first set takes |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 99 | an explicit segment descriptor (eg, "CS") and offset. The second set |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 100 | will take a segment id and offset, set ES to the segment id, and then |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 101 | make the access via the ES segment. The last method is similar to the |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 102 | second, except it takes a pointer that would be valid in 32-bit flat |
| 103 | mode instead of a segment/offset pair. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 104 | |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 105 | Most BIOS variables are stored in global variables, the "BDA", or |
| 106 | "EBDA" memory areas. Because this is common, three sets of helper |
| 107 | macros (GET/SET_GLOBAL, GET/SET_BDA, and GET/SET_EBDA) are available |
Kevin O'Connor | 14b255b | 2013-07-14 14:40:19 -0400 | [diff] [blame] | 108 | to simplify these accesses. Also, an area in the 0xc0000-0xf0000 |
| 109 | memory range is made available for internal BIOS run-time variables |
Kevin O'Connor | 490797e | 2013-09-14 12:38:56 -0400 | [diff] [blame] | 110 | that are marked with the VARLOW attribute. These variables can then |
Kevin O'Connor | 14b255b | 2013-07-14 14:40:19 -0400 | [diff] [blame] | 111 | be accessed with the GET/SET_LOW macros. |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 112 | |
| 113 | Global variables defined in the C code can be read in 16bit mode if |
Kevin O'Connor | 14b255b | 2013-07-14 14:40:19 -0400 | [diff] [blame] | 114 | the variable declaration is marked with VAR16, VARFSEG, or VAR16FIXED. |
| 115 | The GET_GLOBAL macro will then allow read access to the variable. |
| 116 | Global variables are stored in the 0xf000 segment. Because the |
| 117 | f-segment is marked read-only during run-time, the 16bit code is not |
| 118 | permitted to change the value of 16bit variables (use of the |
| 119 | SET_GLOBAL macro from 16bit mode will cause a link error). Code |
| 120 | running in 32bit mode can not access variables with VAR16, but can |
| 121 | access variables marked with VARFSEG, VARLOW, VAR16FIXED, or with no |
| 122 | marking at all. The 32bit code can use the GET/SET_GLOBAL macros, but |
| 123 | they are not required. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 124 | |
| 125 | |
| 126 | GCC 16 bit stack limitations: |
| 127 | |
| 128 | Another limitation of gcc is its use of 32-bit temporaries. Gcc will |
| 129 | allocate 32-bits of space for every variable - even if that variable |
| 130 | is only defined as a 'u8' or 'u16'. If one is not careful, using too |
| 131 | much stack space can break old DOS applications. |
| 132 | |
| 133 | There does not appear to be explicit documentation on the minimum |
| 134 | stack space available for bios calls. However, Freedos has been |
Kevin O'Connor | 0bb2a44 | 2008-04-01 21:09:05 -0400 | [diff] [blame] | 135 | observed to call into the bios with less than 150 bytes available. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 136 | |
| 137 | Note that the post code and boot code (irq 18/19) do not have a stack |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 138 | limitation because the entry points for these functions transition the |
| 139 | cpu to 32bit mode and reset the stack to a known state. Only the |
| 140 | general purpose 16-bit service entry points are affected. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 141 | |
| 142 | There are some ways to reduce stack usage: making sure functions are |
| 143 | tail-recursive often helps, reducing the number of parameters passed |
| 144 | to functions often helps, sometimes reordering variable declarations |
| 145 | helps, inlining of functions can sometimes help, and passing of packed |
Kevin O'Connor | 0afee52 | 2009-02-05 20:32:41 -0500 | [diff] [blame] | 146 | structures can also help. It is also possible to transition to/from |
| 147 | an extra stack stored in the EBDA using the stack_hop helper function. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 148 | |
Kevin O'Connor | 0bb2a44 | 2008-04-01 21:09:05 -0400 | [diff] [blame] | 149 | Some useful stats: the overhead for the entry to a bios handler that |
Kevin O'Connor | 0942e7f | 2009-06-15 22:27:01 -0400 | [diff] [blame] | 150 | takes a 'struct bregs' is 42 bytes of stack space (6 bytes from |
| 151 | interrupt insn, 32 bytes to store registers, and 4 bytes for call |
Kevin O'Connor | 0bb2a44 | 2008-04-01 21:09:05 -0400 | [diff] [blame] | 152 | insn). An entry to an ISR handler without args takes 30 bytes (6 + 20 |
| 153 | + 4). |
| 154 | |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 155 | |
| 156 | Debugging the bios: |
| 157 | |
| 158 | The bios will output information messages to a special debug port. |
Kevin O'Connor | 0fdf193 | 2011-10-04 21:12:28 -0400 | [diff] [blame] | 159 | Under qemu, one can view these messages by adding '-chardev |
| 160 | stdio,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios' to |
| 161 | the qemu command line. Once this is done, one should see status |
| 162 | messages on the console. |
Kevin O'Connor | 838f08f | 2008-03-30 11:07:42 -0400 | [diff] [blame] | 163 | |
| 164 | The gdb-server mechanism of qemu is also useful. One can use gdb with |
| 165 | qemu to debug system images. To use this, add '-s -S' to the qemu |
| 166 | command line. For example: |
| 167 | |
| 168 | qemu -L mybiosdir/ -fda myfdimage.img -s -S |
| 169 | |
| 170 | Then, in another session, run gdb with either out/rom16.o (to debug |
| 171 | bios 16bit code) or out/rom32.o (to debug bios 32bit code). For |
| 172 | example: |
| 173 | |
| 174 | gdb out/rom16.o |
| 175 | |
| 176 | Once in gdb, use the command "target remote localhost:1234" to have |
| 177 | gdb connect to qemu. See the qemu documentation for more information |
| 178 | on using gdb and qemu in this mode. Note that gdb seems to get |
| 179 | breakpoints confused when the cpu is in 16-bit real mode. This makes |
| 180 | stepping through the program difficult (though 'step instruction' |
| 181 | still works). Also, one may need to set 16bit break points at both |
| 182 | the cpu address and memory address (eg, break *0x1234 ; break |
| 183 | *0xf1234). |