The translation is temporarily closed for contributions due to maintenance, please come back later.
Context English Chinese (Simplified) (zh_CN)
_ translator-credits
$FreeBSD$
The <acronym>BIOS</acronym> The <acronym>BIOS</acronym>
This sector is our boot-sequence starting point. As we will see, this sector contains a copy of our <filename>boot0</filename> program. A jump is made by the <acronym>BIOS</acronym> to address <literal>0x7c00</literal> so it starts executing.
After control is received from the <acronym>BIOS</acronym> at memory address <literal>0x7c00</literal>, <filename>boot0</filename> starts executing. It is the first piece of code under FreeBSD control. The task of <filename>boot0</filename> is quite simple: scan the partition table and let the user choose which partition to boot from. The Partition Table is a special, standard data structure embedded in the <acronym>MBR</acronym> (hence embedded in <filename>boot0</filename>) describing the four standard PC <quote>partitions</quote> <_:footnote-1/>. <filename>boot0</filename> resides in the filesystem as <filename>/boot/boot0</filename>. It is a small 512-byte file, and it is exactly what FreeBSD's installation procedure wrote to the hard disk's <acronym>MBR</acronym> if you chose the <quote>bootmanager</quote> option at installation time. Indeed, <filename>boot0</filename> <emphasis>is</emphasis> the <acronym>MBR</acronym>.
The <acronym>MBR</acronym> must fit into 512 bytes, a single disk sector. This program uses low-level <quote>tricks</quote> like taking advantage of the side effects of certain instructions and reusing register values from previous operations to make the most out of the fewest possible instructions. Care must also be taken when handling the partition table, which is embedded in the <acronym>MBR</acronym> itself. For these reasons, be very careful when modifying <filename>boot0.S</filename>.
Note that the <filename>boot0.S</filename> source file is assembled <quote>as is</quote>: instructions are translated one by one to binary, with no additional information (no <acronym>ELF</acronym> file format, for example). This kind of low-level control is achieved at link time through special control flags passed to the linker. For example, the text section of the program is set to be located at address <literal>0x600</literal>. In practice this means that <filename>boot0</filename> must be loaded to memory address <literal>0x600</literal> in order to function properly.
It is worth looking at the <filename>Makefile</filename> for <filename>boot0</filename> (<filename>sys/boot/i386/boot0/Makefile</filename>), as it defines some of the run-time behavior of <filename>boot0</filename>. For instance, if a terminal connected to the serial port (COM1) is used for I/O, the macro <literal>SIO</literal> must be defined (<literal>-DSIO</literal>). <literal>-DPXE</literal> enables boot through <acronym>PXE</acronym> by pressing <keycap>F6</keycap>. Additionally, the program defines a set of <emphasis>flags</emphasis> that allow further modification of its behavior. All of this is illustrated in the <filename>Makefile</filename>. For example, look at the linker directives which command the linker to start the text section at address <literal>0x600</literal>, and to build the output file <quote>as is</quote> (strip out any file formatting):
Let us now start our study of the <acronym>MBR</acronym>, or <filename>boot0</filename>, starting where execution begins.
Some modifications have been made to some instructions in favor of better exposition. For example, some macros are expanded, and some macro tests are omitted when the result of the test is known. This applies to all of the code examples shown.
When in doubt, we refer the reader to the official Intel manuals, which describe the exact semantics for each instruction: <link xlink:href="http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html"/>.
This first block of code is the entry point of the program. It is where the <acronym>BIOS</acronym> transfers control. First, it makes sure that the string operations autoincrement its pointer operands (the <literal>cld</literal> instruction) <_:footnote-1/>. Then, as it makes no assumption about the state of the segment registers, it initializes them. Finally, it sets the stack pointer register (<literal>%sp</literal>) to address <literal>0x7c00</literal>, so we have a working stack.
The next block is responsible for the relocation and subsequent jump to the relocated code.
As <filename>boot0</filename> is loaded by the <acronym>BIOS</acronym> to address <literal>0x7C00</literal>, it copies itself to address <literal>0x600</literal> and then transfers control there (recall that it was linked to execute at address <literal>0x600</literal>). The source address, <literal>0x7c00</literal>, is copied to register <literal>%si</literal>. The destination address, <literal>0x600</literal>, to register <literal>%di</literal>. The number of bytes to copy, <literal>512</literal> (the program's size), is copied to register <literal>%cx</literal>. Next, the <literal>rep</literal> instruction repeats the instruction that follows, that is, <literal>movsb</literal>, the number of times dictated by the <literal>%cx</literal> register. The <literal>movsb</literal> instruction copies the byte pointed to by <literal>%si</literal> to the address pointed to by <literal>%di</literal>. This is repeated another 511 times. On each repetition, both the source and destination registers, <literal>%si</literal> and <literal>%di</literal>, are incremented by one. Thus, upon completion of the 512-byte copy, <literal>%di</literal> has the value <literal>0x600</literal>+<literal>512</literal>= <literal>0x800</literal>, and <literal>%si</literal> has the value <literal>0x7c00</literal>+<literal>512</literal>= <literal>0x7e00</literal>; we have thus completed the code <emphasis>relocation</emphasis>.
Next, the destination register <literal>%di</literal> is copied to <literal>%bp</literal>. <literal>%bp</literal> gets the value <literal>0x800</literal>. The value <literal>16</literal> is copied to <literal>%cl</literal> in preparation for a new string operation (like our previous <literal>movsb</literal>). Now, <literal>stosb</literal> is executed 16 times. This instruction copies a <literal>0</literal> value to the address pointed to by the destination register (<literal>%di</literal>, which is <literal>0x800</literal>), and increments it. This is repeated another 15 times, so <literal>%di</literal> ends up with value <literal>0x810</literal>. Effectively, this clears the address range <literal>0x800</literal>-<literal>0x80f</literal>. This range is used as a (fake) partition table for writing the <acronym>MBR</acronym> back to disk. Finally, the sector field for the <acronym>CHS</acronym> addressing of this fake partition is given the value 1 and a jump is made to the main function from the relocated code. Note that until this jump to the relocated code, any reference to an absolute address was avoided.
The following code block tests whether the drive number provided by the <acronym>BIOS</acronym> should be used, or the one stored in <filename>boot0</filename>.
This code tests the <literal>SETDRV</literal> bit (<literal>0x20</literal>) in the <emphasis>flags</emphasis> variable. Recall that register <literal>%bp</literal> points to address location <literal>0x800</literal>, so the test is done to the <emphasis>flags</emphasis> variable at address <literal>0x800</literal>-<literal>69</literal>= <literal>0x7bb</literal>. This is an example of the type of modifications that can be done to <filename>boot0</filename>. The <literal>SETDRV</literal> flag is not set by default, but it can be set in the <filename>Makefile</filename>. When set, the drive number stored in the <acronym>MBR</acronym> is used instead of the one provided by the <acronym>BIOS</acronym>. We assume the defaults, and that the <acronym>BIOS</acronym> provided a valid drive number, so we jump to <literal>save_curdrive</literal>.
The next block saves the drive number provided by the <acronym>BIOS</acronym>, and calls <literal>putn</literal> to print a new line on the screen.
Note that we assume <varname>TEST</varname> is not defined, so the conditional code in it is not assembled and will not appear in our executable <filename>boot0</filename>.
Our next block implements the actual scanning of the partition table. It prints to the screen the partition type for each of the four entries in the partition table. It compares each type with a list of well-known operating system file systems. Examples of recognized partition types are <acronym>NTFS</acronym> (<trademark class="registered">Windows</trademark>, ID 0x7), <literal>ext2fs</literal> (<trademark class="registered">Linux</trademark>, ID 0x83), and, of course, <literal>ffs</literal>/<literal>ufs2</literal> (FreeBSD, ID 0xa5). The implementation is fairly simple.