Source string Read only

(itstool) path: sect1/para
2045/20450
Context English State
main:
testb $SETDRV,-69(%bp) # Set drive number?
jnz disable_update # Yes
testb %dl,%dl # Drive number valid?
js save_curdrive # Possibly (0x80 set)
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.
save_curdrive:
movb %dl, (%bp) # Save drive number
pushw %dx # Also in the stack
#ifdef TEST /* test code, print internal bios drive */
rolb $1, %dl
movw $drive, %si
call putkey
#endif
callw putn # Print a newline
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.
movw $(partbl+0x4),%bx # Partition table (+4)
xorw %dx,%dx # Item number

read_entry:
movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
btw %dx,_FLAGS(%bp) # Entry enabled?
jnc next_entry # No
movb (%bx),%al # Load type
test %al, %al # skip empty partition
jz next_entry
movw $bootable_ids,%di # Lookup tables
movb $(TLEN+1),%cl # Number of entries
repne # Locate
scasb # type
addw $(TLEN-1), %di # Adjust
movb (%di),%cl # Partition
addw %cx,%di # description
callw putx # Display it

next_entry:
incw %dx # Next item
addb $0x10,%bl # Next entry
jnc read_entry # Till done
It is important to note that the active flag for each entry is cleared, so after the scanning, <emphasis>no</emphasis> partition entry is active in our memory copy of <filename>boot0</filename>. Later, the active flag will be set for the selected partition. This ensures that only one active partition exists if the user chooses to write the changes back to disk.
The next block tests for other drives. At startup, the <acronym>BIOS</acronym> writes the number of drives present in the computer to address <literal>0x475</literal>. If there are any other drives present, <filename>boot0</filename> prints the current drive to screen. The user may command <filename>boot0</filename> to scan partitions on another drive later.
popw %ax # Drive number
subb $0x79,%al # Does next
cmpb 0x475,%al # drive exist? (from BIOS?)
jb print_drive # Yes
decw %ax # Already drive 0?
jz print_prompt # Yes
We make the assumption that a single drive is present, so the jump to <literal>print_drive</literal> is not performed. We also assume nothing strange happened, so we jump to <literal>print_prompt</literal>.
This next block just prints out a prompt followed by the default option:
print_prompt:
movw $prompt,%si # Display
callw putstr # prompt
movb _OPT(%bp),%dl # Display
decw %si # default
callw putkey # key
jmp start_input # Skip beep
Finally, a jump is performed to <literal>start_input</literal>, where the <acronym>BIOS</acronym> services are used to start a timer and for reading user input from the keyboard; if the timer expires, the default option will be selected:
start_input:
xorb %ah,%ah # BIOS: Get
int $0x1a # system time
movw %dx,%di # Ticks when
addw _TICKS(%bp),%di # timeout
read_key:
movb $0x1,%ah # BIOS: Check
int $0x16 # for keypress
jnz got_key # Have input
xorb %ah,%ah # BIOS: int 0x1a, 00
int $0x1a # get system time
cmpw %di,%dx # Timeout?
jb read_key # No
An interrupt is requested with number <literal>0x1a</literal> and argument <literal>0</literal> in register <literal>%ah</literal>. The <acronym>BIOS</acronym> has a predefined set of services, requested by applications as software-generated interrupts through the <literal>int</literal> instruction and receiving arguments in registers (in this case, <literal>%ah</literal>). Here, particularly, we are requesting the number of clock ticks since last midnight; this value is computed by the <acronym>BIOS</acronym> through the <acronym>RTC</acronym> (Real Time Clock). This clock can be programmed to work at frequencies ranging from 2 Hz to 8192 Hz. The <acronym>BIOS</acronym> sets it to 18.2 Hz at startup. When the request is satisfied, a 32-bit result is returned by the <acronym>BIOS</acronym> in registers <literal>%cx</literal> and <literal>%dx</literal> (lower bytes in <literal>%dx</literal>). This result (the <literal>%dx</literal> part) is copied to register <literal>%di</literal>, and the value of the <varname>TICKS</varname> variable is added to <literal>%di</literal>. This variable resides in <filename>boot0</filename> at offset <literal>_TICKS</literal> (a negative value) from register <literal>%bp</literal> (which, recall, points to <literal>0x800</literal>). The default value of this variable is <literal>0xb6</literal> (182 in decimal). Now, the idea is that <filename>boot0</filename> constantly requests the time from the <acronym>BIOS</acronym>, and when the value returned in register <literal>%dx</literal> is greater than the value stored in <literal>%di</literal>, the time is up and the default selection will be made. Since the RTC ticks 18.2 times per second, this condition will be met after 10 seconds (this default behavior can be changed in the <filename>Makefile</filename>). Until this time has passed, <filename>boot0</filename> continually asks the <acronym>BIOS</acronym> for any user input; this is done through <literal>int 0x16</literal>, argument <literal>1</literal> in <literal>%ah</literal>.
Whether a key was pressed or the time expired, subsequent code validates the selection. Based on the selection, the register <literal>%si</literal> is set to point to the appropriate partition entry in the partition table. This new selection overrides the previous default one. Indeed, it becomes the new default. Finally, the ACTIVE flag of the selected partition is set. If it was enabled at compile time, the in-memory version of <filename>boot0</filename> with these modified values is written back to the <acronym>MBR</acronym> on disk. We leave the details of this implementation to the reader.
We now end our study with the last code block from the <filename>boot0</filename> program:
movw $0x7c00,%bx # Address for read
movb $0x2,%ah # Read sector
callw intx13 # from disk
jc beep # If error
cmpw $0xaa55,0x1fe(%bx) # Bootable?
jne beep # No
pushw %si # Save ptr to selected part.
callw putn # Leave some space
popw %si # Restore, next stage uses it
jmp *%bx # Invoke bootstrap
Recall that <literal>%si</literal> points to the selected partition entry. This entry tells us where the partition begins on disk. We assume, of course, that the partition selected is actually a FreeBSD slice.
From now on, we will favor the use of the technically more accurate term <quote>slice</quote> rather than <quote>partition</quote>.
The transfer buffer is set to <literal>0x7c00</literal> (register <literal>%bx</literal>), and a read for the first sector of the FreeBSD slice is requested by calling <literal>intx13</literal>. We assume that everything went okay, so a jump to <literal>beep</literal> is not performed. In particular, the new sector read must end with the magic sequence <literal>0xaa55</literal>. Finally, the value at <literal>%si</literal> (the pointer to the selected partition table) is preserved for use by the next stage, and a jump is performed to address <literal>0x7c00</literal>, where execution of our next stage (the just-read block) is started.
<literal>boot1</literal> Stage
So far we have gone through the following sequence:
The <acronym>BIOS</acronym> did some early hardware initialization, including the <acronym>POST</acronym>. The <acronym>MBR</acronym> (<filename>boot0</filename>) was loaded from absolute disk sector one to address <literal>0x7c00</literal>. Execution control was passed to that location.
<filename>boot0</filename> relocated itself to the location it was linked to execute (<literal>0x600</literal>), followed by a jump to continue execution at the appropriate place. Finally, <filename>boot0</filename> loaded the first disk sector from the FreeBSD slice to address <literal>0x7c00</literal>. Execution control was passed to that location.
There is a file <filename>/boot/boot1</filename>, but it is not the written to the beginning of the FreeBSD slice. Instead, it is concatenated with <filename>boot2</filename> to form <filename>boot</filename>, which <emphasis>is</emphasis> written to the beginning of the FreeBSD slice and read at boot time.
<filename>boot1</filename> is the next step in the boot-loading sequence. It is the first of three boot stages. Note that we have been dealing exclusively with disk sectors. Indeed, the <acronym>BIOS</acronym> loads the absolute first sector, while <filename>boot0</filename> loads the first sector of the FreeBSD slice. Both loads are to address <literal>0x7c00</literal>. We can conceptually think of these disk sectors as containing the files <filename>boot0</filename> and <filename>boot1</filename>, respectively, but in reality this is not entirely true for <filename>boot1</filename>. Strictly speaking, unlike <filename>boot0</filename>, <filename>boot1</filename> is not part of the boot blocks <_:footnote-1/>. Instead, a single, full-blown file, <filename>boot</filename> (<filename>/boot/boot</filename>), is what ultimately is written to disk. This file is a combination of <filename>boot1</filename>, <filename>boot2</filename> and the <literal>Boot Extender</literal> (or <acronym>BTX</acronym>). This single file is greater in size than a single sector (greater than 512 bytes). Fortunately, <filename>boot1</filename> occupies <emphasis>exactly</emphasis> the first 512 bytes of this single file, so when <filename>boot0</filename> loads the first sector of the FreeBSD slice (512 bytes), it is actually loading <filename>boot1</filename> and transferring control to it.
The main task of <filename>boot1</filename> is to load the next boot stage. This next stage is somewhat more complex. It is composed of a server called the <quote>Boot Extender</quote>, or <acronym>BTX</acronym>, and a client, called <filename>boot2</filename>. As we will see, the last boot stage, <filename>loader</filename>, is also a client of the <acronym>BTX</acronym> server.
Let us now look in detail at what exactly is done by <filename>boot1</filename>, starting like we did for <filename>boot0</filename>, at its entry point:
<filename>sys/boot/i386/boot2/boot1.S</filename>

Loading…

User avatar None

New source string

FreeBSD Doc / books_arch-handbookEnglish

New source string 9 months ago
Browse all component changes

Glossary

English English
No related strings found in the glossary.

Source information

Source string comment
(itstool) path: sect1/para
Flags
read-only
Source string location
book.translate.xml:858
String age
9 months ago
Source string age
9 months ago
Translation file
books/arch-handbook.pot, string 95