Proposed change to Bootstrapping and Kernel Initialization in FreeBSD Architecture Handbook

Sergio Andrés Gómez del Real sergio.g.delreal at gmail.com
Tue Jan 14 11:53:44 UTC 2014


I started studying the FreeBSD boot system and found that the documentation
is limited and, in some cases, not true; for example it doesn't address
appropriately the boot1 stage, stating erroneously that it is limited for
booting with a floppy.
Anyway, I've been working on an up-to-date version, and would like to hear
some feedback.

I attach the diff file for
doc/en_US.ISO8859-1/books/arch-handbook/boot/chapter.xml.

Thanks.
-------------- next part --------------
Index: boot/chapter.xml
===================================================================
--- boot/chapter.xml	(revision 43469)
+++ boot/chapter.xml	(working copy)
@@ -3,6 +3,7 @@
 The FreeBSD Documentation Project
 
 Copyright (c) 2002 Sergey Lyubka <devnull at uptsoft.com>
+Copyright (c) 2014 Sergio Andr?s G?mez del Real <Sergio.G.DelReal at gmail.com>
 All rights reserved
 $FreeBSD$
 -->
@@ -10,6 +11,7 @@
   <info><title>Bootstrapping and Kernel Initialization</title>
     <authorgroup>
       <author><personname><firstname>Sergey</firstname><surname>Lyubka</surname></personname><contrib>Contributed by </contrib></author> <!-- devnull at uptsoft.com  12 Jun 2002 -->
+      <author><personname><firstname>Sergio Andr?s</firstname><surname>G?mez del Real</surname></personname></author> <!-- Jan 2014 -->
     </authorgroup>
   </info>
   
@@ -23,7 +25,10 @@
     <indexterm><primary>IA-32</primary></indexterm>
     <indexterm><primary>booting</primary></indexterm>
     <indexterm><primary>system initialization</primary></indexterm>
-    <para>This chapter is an overview of the boot and system
+    <para>The FreeBSD Boot System is not a trivial software. As soon as we get execution control from the BIOS, we must handle a considerable amount of low-level configuration before the kernel can be loaded and executed. We must, moreover, do this in a simple and flexible manner, as to allow the user a great deal of customization possibilities.
+
+
+This chapter is an overview of the boot and system
       initialization process, starting from the BIOS (firmware) POST,
       to the first user process creation.  Since the initial steps of
       system startup are very architecture dependent, the IA-32
@@ -33,31 +38,14 @@
   <sect1 xml:id="boot-overview">
     <title>Overview</title>
 
-    <para>A computer running FreeBSD can boot by several methods,
-      although the most common method, booting from a harddisk where
-      the OS is installed, will be discussed here.  The boot process
-      is divided into several steps:</para>
+    <para>The Boot Process is an extremely machine-dependent activity; not only code must be written for every computer architecture case, but there may also be different forms to boot under the same architecture. For example, taking a look at the <filename>/usr/sys/src/boot</filename> directory will reveal a great amount of architecture-dependent code; there is a directory for each of the various supported architectures. For instance, we find the directories <filename>arm, i386, ia64, powerpc, </filename>etc. Moreover, if we enter the directory specific to the x86 architecture (that is, <filename>i386</filename>), we can see directories belonging to different boot standards. For example, <filename>mbr</filename>, which stands for Master Boot Record, <filename>gpt</filename>, which stands for GUID Partition Table, or <filename>efi</filename>, which stands for Extensible Firmware Interface. Each boot standard has got its own conventions and data structures that conform to that standard.
 
-    <itemizedlist>
-      <listitem><para>BIOS POST</para></listitem>
-      <listitem><para><literal>boot0</literal> stage</para></listitem>
-      <listitem><para><literal>boot2</literal> stage</para></listitem>
-      <listitem><para>loader stage</para></listitem>
-      <listitem><para>kernel initialization</para></listitem>
-    </itemizedlist>
+Here we study in detail the boot process for the x86 architecture using the MBR boot standard.</para>
+<para>The key to understand this process is that it is divided into a sequence of <emphasis>stages</emphasis>, each one more complex than the preceding one. These stages are named <emphasis>boot1, boot2</emphasis> and <emphasis>loader</emphasis> in &man.boot.8; for the first, second and third stage respectively. The Boot System executes each stage in sequence, and the third and last stage, <emphasis>loader</emphasis>, is responsible for loading the FreeBSD kernel. We will look at each stage in the following sections. Additionally, prior to the first stage (<emphasis>boot1</emphasis>), an additional step is necessary in the process. This step is the Master Boot Record, conveniently called <emphasis>boot0.</emphasis></para>
 
-    <indexterm><primary>BIOS POST</primary></indexterm>
-    <indexterm><primary>boot0</primary></indexterm>
-    <indexterm><primary>boot2</primary></indexterm>
-    <indexterm><primary>loader</primary></indexterm>
-    <para>The <literal>boot0</literal> and <literal>boot2</literal>
-      stages are also referred to as <emphasis>bootstrap stages 1 and
-      2</emphasis> in &man.boot.8; as the first steps in FreeBSD's
-      3-stage bootstrapping procedure.  Various information is printed
-      on the screen at each stage, so you may visually recognize them
-      using the table that follows.  Please note that the actual data
-      may differ from machine to machine:</para>
-
+<para>
+Here is an example of the output generated by the different boot stages. Please note that the actual data may differ from machine to machine:
+</para>
     <informaltable frame="none" pgwide="0">
       <tgroup cols="2">
 	<tbody>
@@ -110,26 +98,48 @@
 	</tbody>
       </tgroup>
     </informaltable>
+<para>
+The rest of this chapter comprises the following sections:
+</para>
+
+    <itemizedlist>
+      <listitem><para>The BIOS</para></listitem>
+      <listitem><para>The Master Boot Record</para></listitem>
+      <listitem><para><literal>boot1</literal> stage</para></listitem>
+      <listitem><para>The BTX Server</para></listitem>
+<listitem><para><literal>boot2</literal> stage</para></listitem>
+      <listitem><para><literal>loader</literal> stage</para></listitem>
+      <listitem><para>Kernel Initialization</para></listitem>
+    </itemizedlist>
+
+    <indexterm><primary>BIOS POST</primary></indexterm>
+    <indexterm><primary>MBR and boot0</primary></indexterm>
+    <indexterm><primary>boot1</primary></indexterm>
+    <indexterm><primary>The BTX Server</primary></indexterm>
+    <indexterm><primary>BTX client: boot2</primary></indexterm>
+    <indexterm><primary>BTX client: loader</primary></indexterm>
+
   </sect1>
 
   <sect1 xml:id="boot-bios">
-    <title>BIOS POST</title>
 
+    <title>The BIOS</title>
+
     <para>When the PC powers on, the processor's registers are set
       to some predefined values.  One of the registers is the
       <emphasis>instruction pointer</emphasis> register, and its value
       after a power on is well defined: it is a 32-bit value of
-      0xfffffff0.  The instruction pointer register points to code to
-      be executed by the processor.  One of the registers is the
+      <literal>0xfffffff0</literal>.  The instruction pointer register (also known as the Program Counter) points to code to
+      be executed by the processor.  Another important register is the
       <literal>cr0</literal> 32-bit control register, and its value
-      just after the reboot is 0.  One of the cr0's bits, the bit PE
+      just after the reboot is <literal>0</literal>.  One of the <literal>cr0</literal>'s bits, the bit PE
       (Protection Enabled) indicates whether the processor is running
-      in protected or real mode.  Since at boot time this bit is
-      cleared, the processor boots in real mode.  Real mode means,
+      in 32-bit protected or 16-bit real mode.  Since at boot time this bit is
+      cleared, the processor boots in 16-bit real mode.  Real mode means,
       among other things, that linear and physical addresses are
-      identical.</para>
+      identical. The reason for the processor not to start immediately in 32-bit protected mode is backwards compatibility. In particular, the boot process relies on the services provided by the BIOS, and the BIOS itself work in legacy, 16-bit code.</para>
 
-    <para>The value of 0xfffffff0 is slightly less then 4Gb, so unless
+    <para>The value of <literal>0xfffffff0</literal> is slightly less then 4Gb, so unless
       the machine has 4Gb physical memory, it cannot point to a valid
       memory address.  The computer's hardware translates this address
       so that it points to a BIOS memory block.</para>
@@ -139,7 +149,7 @@
       has a relatively small amount of read-only memory (ROM).  This
       memory contains various low-level routines that are specific to
       the hardware supplied with the motherboard.  So, the processor
-      will first jump to the address 0xfffffff0, which really resides
+      will first jump to the address <literal>0xfffffff0</literal>, which really resides
       in the BIOS's memory.  Usually this address contains a jump
       instruction to the BIOS's POST routines.</para>
 
@@ -154,7 +164,7 @@
     <para>The very last thing in the POST is the <literal>INT
 	0x19</literal> instruction.  That instruction reads 512 bytes
       from the first sector of boot device into the memory at address
-      0x7c00.  The term <emphasis>first sector</emphasis> originates
+      <literal>0x7c00</literal>.  The term <emphasis>first sector</emphasis> originates
       from harddrive architecture, where the magnetic plate is divided
       to a number of cylindrical tracks.  Tracks are numbered, and
       every track is divided by a number (usually 64) sectors.  Track
@@ -166,28 +176,29 @@
 	  utilities such as &man.disklabel.8; may store the
 	  information in this area, mostly in the second
 	  sector.</para></footnote>.</para>
+<para>This sector is our boot-sequence starting point; as we will see, this sector is in fact our <filename>boot0</filename> program. A jump is made by the BIOS to address <literal>0x7c00</literal> so it starts executing.</para>
   </sect1>
 
   <sect1 xml:id="boot-boot0">
-    <title><literal>boot0</literal> Stage</title>
+    <title>The Master Boot Record</title>
 
     <indexterm><primary>MBR</primary></indexterm>
-    <para>Take a look at the file <filename>/boot/boot0</filename>.
-      This is a small 512-byte file, and it is exactly what FreeBSD's
+<para>
+After control is received from the BIOS at memory address <literal>0x7c00</literal>, <filename>boot0</filename> starts executing; it is the first piece of code under our control. In essence, 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 MBR (hence embedded in <filename>boot0</filename>) describing the 4 standard PC partitions <footnote><para>en.wikipedia.org/wiki/Master_boot_record</para></footnote>.
+<filename>boot0</filename> resides in the filesystem at location <filename>/boot/boot0</filename>.
+      It is a small 512-byte file, and it is exactly what FreeBSD's
       installation procedure wrote to your harddisk's MBR if you chose
       the <quote>bootmanager</quote> option at installation
-      time.</para>
+      time; indeed, <filename>boot0</filename> <emphasis>is</emphasis> the MBR.</para>
 
     <para>As mentioned previously, the <literal>INT 0x19</literal>
       instruction loads an MBR, i.e., the <filename>boot0</filename>
-      content, into the memory at address 0x7c00.  Taking a look at
-      the file <filename>sys/boot/i386/boot0/boot0.S</filename> can
-      give a guess at what is happening there - this is the boot
-      manager, which is an awesome piece of code written by Robert
+      content, into the memory at address <literal>0x7c00</literal>.  The source file for <filename>boot0</filename> can be found in <filename>sys/boot/i386/boot0/boot0.S</filename> - which is an awesome piece of code written by Robert
       Nordier.</para>
 
-    <para>The MBR, or, <filename>boot0</filename>, has a special
-      structure starting from offset 0x1be, called the
+
+    <para>As pointed out earlier, the MBR, or <filename>boot0</filename>, has a special
+      structure starting from offset <literal>0x1be</literal>, called the
       <emphasis>partition table</emphasis>.  It has 4 records of 16
       bytes each, called <emphasis>partition records</emphasis>, which
       represent how the harddisk(s) are partitioned, or, in FreeBSD's
@@ -222,56 +233,411 @@
       different ways: LBA (Logical Block Addressing) has the starting
       sector for the partition and the partition's length, while CHS
       (Cylinder Head Sector) has coordinates for the first and last
-      sectors of the partition.</para>
+      sectors of the partition. The partition table ends with the special signature <literal>0xaa55</literal>.</para>
+<para>It is important to note that, because of the size contraint of the MBR (it must fit in 512 bytes, that is, in 1 disk sector), and because care must be taken when handling the Partition Table (which is embedded in the MBR itself), this program make use of little low-level 'tricks', like <quote><emphasis>...using side effects of certain instructions, reusing register values from previous operations, etc.</emphasis></quote>, so as to make the most out of the least possible instructions. For this reason, extreme care must be taken when modifying <filename>boot0.S</filename>.</para>
+<para>
+Also, note that the <filename>boot0.S</filename> source file is assembled "as is"; instructions are translated one by one to binary, with no additional information (no ELF 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.</para>
 
-    <para>The boot manager scans the partition table and prints the
-      menu on the screen so the user can select what disk and what
-      slice to boot.  By pressing an appropriate key,
-      <filename>boot0</filename> performs the following
-      actions:</para>
+<para>It is also worth looking at the Makefile for boot0 (<filename>sys/boot/i386/boot0/Makefile</filename>), as it defines some of its behaviour. For instance, if a terminal connected to the serial port (COM 1) is used for I/O, the macro <literal>SIO</literal> must be defined (<literal>-DSIO</literal>). Other options include enabling boot through PXE pressing F6 (<literal>-DPXE</literal>). Additionally, the program defines a set of <emphasis>flags</emphasis> that allow to further modify its behavior. All this is illustrated in the <filename>Makefile</filename>. Let us, 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 "as is" (strip out any file formatting):
+<programlisting><filename>sys/boot/i386/boot0/Makefile:</filename>
+BOOT_BOOT0_ORG?=0x600
+LDFLAGS=-e start -Ttext ${BOOT_BOOT0_ORG} -Wl,-N,-S,--oformat,binary
+</programlisting>
+</para>
 
+
+<para>Let us now start our study of the MBR, or <filename>boot0</filename>, program, starting where execution begins: <footnote><para>Please note that subtle modifications were made to some instructions in favor of better exposition. This applies to <emphasis>all</emphasis> our code examples.</para></footnote>:
+<programlisting><filename>sys/boot/i386/boot0/boot0.S:</filename>
+start:		cld			# String ops inc
+		xorw %ax,%ax		# Zero
+		movw %ax,%es		# Address
+		movw %ax,%ds		#  data
+		movw %ax,%ss		# Set up
+		movw 0x7c00,%sp		#  stack
+</programlisting>
+This first block of code is the entry point of the program; it is where the BIOS transfers control. First, it makes sure that the string operations autoincrement its pointer operands (the cld instruction)<footnote><para>When in doubt, we remit the reader to the official Intel Manuals, which describe the <emphasis>exact</emphasis> semantics for each instruction: http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html.</para></footnote>. Then, it makes no assumption about the state of the segment registers, so it initializes them. Finally, it sets the stack pointer register (%sp) to address <literal>0x7c00</literal>, so we have a working stack.
+</para>
+<para>
+
+The next block is responsible for the relocation and subsequent jump to the relocated code:
+<programlisting><filename>sys/boot/i386/boot0/boot0.S:</filename>
+		movw $0x7c00,%si	# Source
+		movw $0x600,%di		# Destination
+		movw $512,%cx		# Word count
+		rep			# Relocate
+		movsb			#  code
+		movw %di,%bp		# Address variables
+		movb $16,%cl		# Words to clear
+		rep			# Zero
+		stosb			#  them
+		incb -0xe(%di)		# Set the S field to 1
+
+		jmp main-LOAD+ORIGIN	# Jump to relocated code
+</programlisting>
+Because <filename>boot0</filename> is loaded by the BIOS 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 0x600). The source address, <literal>0x7c00</literal>, is copied to register %si; the destination address, <literal>0x600</literal>, to register %di. The number of bytes to copy, <literal>512</literal> (the program's size), is copied to register %cx. Next, the rep instruction repeats the instruction that follows, that is, movsb, the number of times dictaded by the %cx register. The movsb instruction copies the byte pointed to by %si to the address pointed to by %di. This is repeated another 511 times; on each repetition, both the source and destination registers, %si and %di, are incremented by one. Thus, upon completion of the 512-byte copy, %di has the value <literal>0x600</literal>+<literal>512</literal>=<literal>0x800</literal>, and %si 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 %di is now copied to %bp; %bp gets the value <literal>0x800</literal>. The value <literal>16</literal> is now copied to %cl in preparation for a new string operation (like our previous movsb). Now, stosb is executed 16 times. This instruction copies a <literal>0</literal> value to the address pointed to by the destination register (%di, which is <literal>0x800</literal>), and increments it. This is repeated another 15 times, so %di 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 back the MBR to disk. Finally, the sector field for the CHS addressing of this fake partition is set, 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.
+</para>
+<para>
+The following code block simply tests if the drive number provided by the BIOS should be used, or else the one saved by <filename>boot0</filename>:
+<programlisting><filename>sys/boot/i386/boot0/boot0.S:</filename>
+main:
+		testb $SETDRV,-69(%bp)	# Set drive number?
+		jnz disable_update	# Yes
+		testb %dl,%dl		# Drive number valid?
+		js save_curdrive	# Possibly (0x80 set)
+</programlisting>
+This code tests the SETDRV bit in the <emphasis>flags</emphasis> variable. Recall that register %bp 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 SETDRV flag is <emphasis>not</emphasis> set by default, but it can be set in the <filename>Makefile</filename>; when set, the <emphasis>drive number</emphasis> stored in the MBR is used instead of the one provided by the BIOS. We assume the defaults, and that the BIOS provided a valid drive number, so we jump to <literal>save_curdrive</literal>.
+</para>
+<para>
+The next block saves the <emphasis>drive number</emphasis> provided by the BIOS, and calls <literal>putn</literal> to print a new line on the screen:
+<programlisting><filename>sys/boot/i386/boot0/boot0.S:</filename>
+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
+</programlisting>
+Note that we assume TEST is not defined, so the conditional code in it is not assembled and won't appear in our executable <filename>boot0</filename>.
+</para>
+<para>
+Our next block implements the actual scanning of the partition table. It prints to the screen the partition type for each of the 4 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 NTFS (Windows. ID 0x7), ext2fs (Linux. ID 0x83) and, of course, ffs/ufs2 (FreeBSD. ID 0xa5). The implementation is fairly simple:
+<programlisting>
+		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
+</programlisting>
+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 1 active partition exists if the user chooses to write-back the changes to disk.
+
+
+Our next block tests for other drives present. At startup, the BIOS writes the number of drives present in the computer to address <literal>0x475</literal>. If there is any other drive present, <filename>boot0</filename> prints the current drive to screen; the user may command <filename>boot0</filename> to scan partitions on another drive later:
+<programlisting>
+		popw %ax		# Drive number
+		subb $0x80-0x1,%al	# Does next
+		cmpb NHRDRV,%al		#  drive exist? (from BIOS?)
+		jb print_drive		# Yes
+		decw %ax		# Already drive 0?
+		jz print_prompt		# Yes
+</programlisting>
+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>. 
+</para>
+<para>
+This next block just prints out a prompt followed by the default option: 
+<programlisting>
+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
+</programlisting>
+Finally, a jump is perfored to <literal>start_input</literal>, where the BIOS 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:
+<programlisting>
+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
+</programlisting>
+Note that an interrupt is requested with number <literal>0x1a</literal>, and argument <literal>0</literal> in register <literal>%ah</literal>. The BIOS has a predefined list of interrupt services, requested 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 BIOS through the <emphasis>Real Time Clock</emphasis>. This clock can be programmed to work at frequencies ranging from <literal>2hz</literal> to <literal>8192hz</literal>; the BIOS sets it to <literal>18.2hz</literal> at startup. The 32-bit result is returned by the BIOS 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 TICKS variable is added to <literal>%di</literal>. This variable is placed in <filename>boot0</filename> at offset <literal>_TICKS</literal> (a negative value) from register <literal>%bp</literal> (which, recall, points to 0x800). The default value of this variable is 0xb6 (182 in decimal). The idea is that <filename>boot0</filename> constantly requests the time to the BIOS, 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 would be made. Since the Real Time Clock ticks <literal>18.2</literal> times per second, this condition will be met after 10 seconds (this default behaviour can be changed in the <filename>Makefile</filename>. While this time has not passed, <filename>boot0</filename> continually asks the BIOS 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, the register <literal>%si</literal> is set to point to the appropiate partition entry in the partition table, according to the selection, and that selection overrides the previous default one; indeed, it becomes the <emphasis>new</emphasis> default. Finally, the ACTIVE flag of the selected partition is set. If enabled at compile time, <filename>boot0</filename> with these modified values, are written back to the MBR on disk. We leave the details of this implementation to the reader.
+</para>
+<para>
+We now end our study with the last code block from the <filename>boot0</filename> program:
+<programlisting>
+		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
+</programlisting>
+Recall that <literal>%si</literal> points to the appropiate partition entry. This entry tells us where the partition begins on disk; we assume, of course, that the partition selected is a FreeBSD partition. The transfer buffer is set to <literal>0x7c00</literal> (register <literal>%bx</literal>), and a read for the first sector of the FreeBSD partition is requested by calling <literal>intx13</literal>. We assume that everything went OK, so a jump to <literal>beep</literal> is <emphasis>not</emphasis> 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.
+
+</para>
+
+
+  </sect1>
+
+  <sect1 xml:id="boot-boot1">
+    <title><literal>boot1</literal> Stage</title>
+
+    <para>So far we have gone through the following sequence:
     <itemizedlist>
       <listitem>
-	<para>modifies the bootable flag for the selected partition to
-	  make it bootable, and clears the previous</para>
+	<para>The BIOS did some early hardware initialization, including the POST. The MBR (<filename>boot0</filename>) was loaded from absolute disk sector 1 to address <literal>0x7c00</literal>. Execution control was passed to that location.</para>
       </listitem>
 
       <listitem>
-	<para>saves itself to disk to remember what partition (slice)
-	  has been selected so to use it as the default on the next
-	  boot</para>
+	<para><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 appropiate place. Finally, <filename>boot0</filename> loaded the first disk sector from the FreeBSD partition (slice) to address <literal>0x7c00</literal>. Execution control was passed to that location.</para>
       </listitem>
+    </itemizedlist>
 
+<filename>boot1</filename> is the next step in our boot-loading sequence; it is the first of three boot stages.
+It is important to note that we have been dealing exclusively with disk sectors. Indeed, the BIOS 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 files containing <filename>boot0</filename> and <filename>boot1</filename>, respectively, but in reality this is not entirely true for <filename>boot1</filename>. Strictly speaking, there is no file <filename>boot1</filename><footnote><para>There is a file <filename>/boot/boot1</filename>, but it is not the file read at boot time; it is concatanated with <filename>boot2</filename> to form <filename>boot</filename>.</para></footnote>; instead, a single, full-blown file, <filename>boot</filename>, is what you will find in the file system (<filename>boot/boot</filename>). This file is a composition of <filename>boot1</filename>, <filename>boot2</filename> and the <literal>Boot Extender</literal> (or <literal>BTX</literal>). This big 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, indeed, when <filename>boot0</filename> loads the first sector of the FreeBSD slice (512 bytes), it is actually loading <filename>boot1</filename> and transfering control to it.</para>
+<para>
+
+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 <emphasis>the</emphasis> server, called the <literal>Boot Extender</literal>, or <literal>BTX</literal>, and <emphasis>a</emphasis> client, called <filename>boot2</filename>. We say <emphasis>a</emphasis> client because, as we will see, the last boot stage is <filename>loader</filename>, which is also a client of the <literal>BTX server</literal>.
+</para>
+<para>
+Let us now look in detail what exactly is done by <filename>boot1</filename>, starting like we did for <filename>boot0</filename>, at its entry point:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+start:
+ jmp main
+</programlisting>
+The entry point at <literal>start</literal> simply jumps past a special data area to the label <literal>main</literal>, which in turn looks like this:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+main:		cld			# String ops inc
+		xor %cx,%cx		# Zero
+		mov %cx,%es		# Address
+		mov %cx,%ds		#  data
+		mov %cx,%ss		# Set up
+		mov $start,%sp		#  stack
+		mov %sp,%si		# Source
+		mov $0x700,%di		# Destination
+		incb %ch		# Word count
+		rep			# Copy
+		movsw			#  code
+</programlisting>
+Note that, just like <filename>boot0</filename>, the above code <emphasis>relocates</emphasis> <filename>boot1</filename>, this time to memory address <literal>0x700</literal>. However, unlike <filename>boot0</filename>, it does not jump over there. In fact, <filename>boot1</filename> is linked to execute at address <literal>0x7c00</literal>, effectively where it was loaded in the first place. The reason for this relocation will be discussed shortly.</para>
+<para>
+Next comes a loop that looks for the FreeBSD partition. Note that, although <filename>boot0</filename> in fact loaded <filename>boot1</filename> from the FreeBSD partition, no information was passed to it about this partition<footnote><para>Actually we did pass a pointer to the partition entry in register %si. However, <filename>boot1</filename> does <emphasis>not</emphasis> assume that it was loaded by <filename>boot0</filename> (perhaps other MBR loaded it that did not pass this information), so it assumes nothing.</para></footnote>; <filename>boot1</filename> effectively needs to rescan the partition table to find where the FreeBSD partition starts. Therefore, it rereads the MBR:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+		mov $part4,%si		# Partition
+		cmpb $0x80,%dl		# Hard drive?
+		jb main.4		# No
+		movb $0x1,%dh		# Block count
+		callw nread		# Read MBR</programlisting>
+Note that in the code above, register <literal>%dl</literal> maintains information about the boot device; this is passed on by the BIOS and preserved by the MBR. Numbers <literal>0x80</literal> and greater tells us that we are dealing with a hard drive, so a call is made to <literal>nread</literal>, where the MBR is read. Arguments to <literal>nread</literal> are passed through <literal>%si</literal> and <literal>%dh</literal>. The memory address at label <literal>part4</literal> is copied to <literal>%si</literal>. This memory address holds a 'fake partition', to be used by <literal>nread</literal>. The following is the data found in this fake partition:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+part4:		.byte 0x80, 0x00, 0x01, 0x00
+		.byte 0xa5, 0xfe, 0xff, 0xff
+		.byte 0x00, 0x00, 0x00, 0x00
+		.byte 0x50, 0xc3, 0x00, 0x00
+</programlisting>
+In particular, the LBA for this fake partition is hardcoded to 0; this effectively is used as an argument to the BIOS for reading absolute sector 1 from the hard drive. Let us now proceed to take a look at <literal>nread</literal>:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+nread:		mov $0x8c00,%bx		# Transfer buffer
+		mov 0x8(%si),%ax	# Get
+		mov 0xa(%si),%cx	#  LBA
+		push %cs		# Read from
+		callw xread.1		#  disk
+		jnc return		# If success, return
+</programlisting>
+Recall that <literal>%si</literal> points to the fake partition. The word <footnote><para>In the context of 16-bit real mode, a word is 2 bytes.</para></footnote> at offset <literal>0x8</literal> is copied to register <literal>%ax</literal> and word at offset <literal>0xa</literal> to <literal>%cx</literal>; they are interpreted by the BIOS as a the lower 4-byte value denoting the LBA to be read (the upper 4 bytes are assumed 0). Register <literal>%bx</literal> holds the memory address where the MBR will be loaded. The instruction pushing <literal>%cs</literal> onto the stack is very interesting. In this context, it accomplishes nothing, but, as we will see shortly, <literal>xread.1</literal> makes no assumption about the context in which it is invoked. Particularly, <filename>boot2</filename> 'traps' there; <literal>xread.1</literal> then executes in VM86 (Virtual Real Mode). But, because <filename>boot2</filename> executes in 32-bit protected mode, a long return is executed when <literal>xread.1</literal> finishes execution. This instruction pops out from the stack the code segment (register <literal>%cs</literal>) to return. This mechanism will become more clear later.
+Now, the code at <literal>xread.1</literal> further calls the <literal>read</literal> function, which actually calls the BIOS asking for the disk sector:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+xread.1:				# Starting
+		pushl $0x0		#  absolute
+		push %cx		#  block
+		push %ax		#  number
+		push %es		# Address of
+		push %bx		#  transfer buffer
+		xor %ax,%ax		# Number of
+		movb %dh,%al		#  blocks to
+		push %ax		#  transfer
+		push $0x10		# Size of packet
+		mov %sp,%bp		# Packet pointer
+		callw read		# Read from disk
+		lea 0x10(%bp),%sp	# Clear stack
+		lret			# To far caller
+</programlisting>
+Note the <emphasis>long return</emphasis> instruction at the end of this block. This instruction pops out the <literal>%cs</literal> register pushed by <literal>nread</literal>, and returns. Finally, <literal>nread</literal> also returns.
+
+Now, with the MBR loaded to memory, the actual loop for searching the FreeBSD partition begins. The implementation is as follows:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+		mov $0x1,%cx	 	 # Two passes
+main.1: 	mov $MEM_BUF+PRT_OFF,%si # Partition table
+		movb $0x1,%dh		 # Partition
+main.2: 	cmpb $PRT_BSD,0x4(%si)	 # Our partition type?
+		jne main.3		 # No
+		jcxz main.5		 # If second pass
+		testb $0x80,(%si)	 # Active?
+		jnz main.5		 # Yes
+main.3: 	add $0x10,%si	 	 # Next entry
+		incb %dh		 # Partition
+		cmpb $0x1+PRT_NUM,%dh	 # In table?
+		jb main.2		 # Yes
+		dec %cx			 # Do two
+		jcxz main.1		 #  passes
+</programlisting>
+This code, which follows the call to <literal>nread</literal>, implements the scan in the partition table looking for the FreeBSD partition. If a FreeBSD partition is successfully identified, execution continues at <literal>main.5</literal>; we assume this behaviour, so we continue execution there:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+main.5: 	mov %dx,MEM_ARG			   # Save args
+		movb $0x10,%dh			   # Sector count
+		callw nread			   # Read disk
+		mov $MEM_BTX,%bx		   # BTX
+		mov 0xa(%bx),%si		   # Get BTX length and set
+		add %bx,%si			   #  %si to start of boot2.bin
+		mov $MEM_USR+SIZ_PAG*2,%di	   # Client page 2
+		mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx # Byte
+		sub %si,%cx			   #  count
+		rep				   # Relocate
+		movsb				   #  client
+</programlisting>
+</para>
+<para>
+Recall that at this point, register <literal>%si</literal> points to the FreeBSD partition entry in the MBR partition table, so a call to <literal>nread</literal> will effectively read sectors at the beginning of this partition. The argument passed on register <literal>%dh</literal> tells <literal>nread</literal> to read 16 disk sectors. Recall that the first 512 bytes, (the first sector of the FreeBSD partition), coincides with the <filename>boot1</filename> program. Also recall that the file written to the beginning of the FreeBSD partition is not <filename>/boot/boot1</filename>, but <filename>/boot/boot</filename>. Let's take a look at the size of these files in the filesystem:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+-r--r--r--  1 root  wheel   512B Jan  8 00:15 /boot/boot0
+-r--r--r--  1 root  wheel   512B Jan  8 00:15 /boot/boot1
+-r--r--r--  1 root  wheel   7.5K Jan  8 00:15 /boot/boot2
+-r--r--r--  1 root  wheel   8.0K Jan  8 00:15 /boot/boot
+</programlisting>
+As you can see from this output, both <filename>boot0</filename> and <filename>boot1</filename> are 512 bytes each, so they fit <emphasis>exactly</emphasis> in 1 disk sector. Note that <filename>boot2</filename> is much bigger; <filename>boot2</filename> holds the BTX program and the <filename>boot2</filename> client. Finally, a file called simply <filename>boot</filename> is 512 bytes larger than <filename>boot2</filename>; it is a concatenation of <filename>boot1</filename> and <filename>boot2</filename>. As already noted, <filename>boot0</filename> is the file written to the absolute first disk sector (the MBR), and <filename>boot</filename> is the file written to the first sector of the FreeBSD partition; <filename>boot1</filename> and <filename>boot2</filename> are <emphasis>not</emphasis> written to disk. Following is the command used to concatenate <filename>boot1</filename> and <filename>boot2</filename> into a single file <filename>boot</filename>:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+cat boot1 boot2 > boot
+</programlisting>
+So, effectively, <filename>boot1</filename> occupies <emphasis>exactly</emphasis> the first 512 of the <filename>boot</filename> file and, because <filename>boot</filename> is written to the first sector of the FreeBSD partition, <filename>boot1</filename> fits <emphasis>exactly</emphasis> in this first sector. Now, returning to our execution, because <literal>nread</literal> reads the first 16 sectors of the FreeBSD partition, it effectively is reading the entire <filename>boot</filename> file. <footnote><para>512*16=8192 bytes, exactly the size of <filename>boot</filename></para></footnote>
+We will see more details about how <filename>boot</filename> is formed from <filename>boot1</filename> and <filename>boot2</filename> in the next section.
+</para>
+<para>
+Recall that <literal>nread</literal> uses memory address <literal>0x8c00</literal> as the transfer buffer to hold the sectors read. This address is conveniently chosen. Indeed, because <filename>boot1</filename> belongs to the first 512 bytes, it is stored at address range <literal>0x8c00</literal>-<literal>0x8dff</literal>. The 512 bytes that follows (range <literal>0x8e00</literal>-<literal>0x8fff</literal>) is used to store the <emphasis>disklabel</emphasis><footnote><para>If you ever wondered where FreeBSD stored this information, it is in this region.</para></footnote>. Starting at address <literal>0x9000</literal> is the beginning of the <literal>BTX server</literal>, and immediately following the <literal>BTX</literal> is the <filename>boot2</filename> client. As we will see, the <literal>BTX server</literal> acts as a kernel, and executes in protected mode in the most privileged level. In contrast, the BTX clients (<filename>boot2</filename>, for example), execute in user mode; we will see how this is accomplished in the next section. The code after the call to <literal>nread</literal> locates the beginning of <filename>boot2</filename> in the memory buffer, and copies it to memory address <literal>0xc000</literal>. This is because the BTX arranges <filename>boot2</filename> to execute in a segment starting at <literal>0xa000</literal>. We explore this in detail in the following section.
+</para>
+<para>
+The last code block of <filename>boot1</filename> enables access to memory above 1MB<footnote><para>This is necessary for legacy reasons; the interested reader is remitted to http://en.wikipedia.org/wiki/A20_line.</para></footnote> and concludes with a jump to the starting point of the <literal>BTX server</literal>:
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+seta20: 	cli			# Disable interrupts
+seta20.1:	dec %cx			# Timeout?
+		jz seta20.3		# Yes
+		inb $0x64,%al		# Get status
+		testb $0x2,%al		# Busy?
+		jnz seta20.1		# Yes
+		movb $0xd1,%al		# Command: Write
+		outb %al,$0x64		#  output port
+seta20.2:	inb $0x64,%al		# Get status
+		testb $0x2,%al		# Busy?
+		jnz seta20.2		# Yes
+		movb $0xdf,%al		# Enable
+		outb %al,$0x60		#  A20
+seta20.3:	sti			# Enable interrupts
+
+		jmp start+MEM_JMP-MEM_ORG# Start BTX
+</programlisting>
+Note that right before the jump, interrupts are enabled.
+</para>
+  </sect1>
+  <sect1 xml:id="btx-server">
+    <title>The BTX Server</title>
+<para>
+Next in our boot sequence is the <literal>BTX Server</literal>. Let's quickly remember how we got here:
+    <itemizedlist>
       <listitem>
-	<para>loads the first sector of the selected partition (slice)
-	  into memory and jumps there</para>
+	<para>BIOS loads the absolute sector 1 (the MBR, or <filename>boot0</filename>), to address <literal>0x7c00</literal> and jumps over there.</para>
       </listitem>
+
+      <listitem>
+	<para><filename>boot0</filename> relocates to <literal>0x600</literal>, the address it was linked to execute, and jumps over there. It then reads the first sector of the FreeBSD partition (which consists of <filename>boot1</filename>) into address <literal>0x7c00</literal> and jumps over there.</para>
+      </listitem>
+
+      <listitem>
+	<para><filename>boot1</filename> loads the first 16 sectors of the FreeBSD partition into address <literal>0x8c00</literal>. This 16 sectors, or <literal>8192</literal> bytes, consists <emphasis>exactly</emphasis> of the <filename>boot</filename> file. The <filename>boot</filename> file consists of a concatenation of <filename>boot1</filename> and <filename>boot2</filename> and, <filename>boot2</filename>, in turn, contains the <literal>BTX server</literal> and the <filename>boot2</filename> client. Finally, a jump is made to address <literal>0x9010</literal>, the entry point of the <literal>BTX server</literal>.</para>
+      </listitem>
     </itemizedlist>
+Let us further review how the single, all-in-one <filename>boot</filename> file is created. The commands stated in the <filename>Makefile</filename> are quite long, so let's go step by step. The way <filename>boot</filename> is built is defined in its <filename>Makefile</filename> (<filename>/usr/src/sys/boot/i386/boot2/Makefile</filename>). Let's look at the rule that creates the <filename>boot</filename> file:
+<programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot: boot1 boot2
+	cat boot1 boot2 > boot
+</programlisting>
+This tells us that <filename>boot1</filename> and <filename>boot2</filename> are needed, and the rule simply concatenates them to produce a single file called <filename>boot</filename>. The rules for creating <filename>boot1</filename> are also quite simple:
+<programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot1: boot1.out
+	objcopy -S -O binary boot1.out ${.TARGET}
 
-    <para>What kind of data should reside on the very first sector of
-      a bootable partition (slice), in our case, a FreeBSD slice?  As
-      you may have already guessed, it is
-      <filename>boot2</filename>.</para>
-  </sect1>
+boot1.out: boot1.o
+	ld -e start -Ttext 0x7c00 -o boot1.out boot1.o
+</programlisting>
+In order to apply the rule for creating <filename>boot1</filename>, <filename>boot1.out</filename> must be resolved. This, in turn, depends on the existence of <filename>boot1.o</filename>. This last file is simply the result of assembling our familiar <filename>boot1.S</filename>, without linking. Now, the rule for creating <filename>boot1.out</filename> is applied. This tells us that <filename>boot1.o</filename> should be linked with <literal>start</literal> as its entry point, and starting at address <literal>0x7c00</literal>. Finally, <filename>boot1</filename> is created from <filename>boot1.out</filename> applying the appropiate rule. This rule is the <filename>objcopy</filename> command applied to <filename>boot1.out</filename>. Note the flags passed to <filename>objcopy</filename>: -S tells it to strip all relocation and symbolic information; -O binary indicates the output format, that is, a simple, unformatted binary file. We now have <filename>boot1</filename>. Let's take a look at how <filename>boot2</filename> is constructed:
+<programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot2: boot2.ld
+	@set -- `ls -l boot2.ld`; x=$$((7680-$$5)); \
+	    echo "$$x bytes available"; test $$x -ge 0
+	dd if=boot2.ld of=${.TARGET} obs=7680 conv=osync
 
-  <sect1 xml:id="boot-boot2">
-    <title><literal>boot2</literal> Stage</title>
+boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
+	btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
+	    -o ${.TARGET} -P 1 boot2.bin
 
-    <para>You might wonder, why <literal>boot2</literal> comes after
-      <literal>boot0</literal>, and not boot1.  Actually, there is a
-      512-byte file called <filename>boot1</filename> in the directory
-      <filename>/boot</filename> as well.  It is used for booting from
-      a floppy.  When booting from a floppy,
-      <filename>boot1</filename> plays the same role as
-      <filename>boot0</filename> for a harddisk: it locates
-      <filename>boot2</filename> and runs it.</para>
+boot2.ldr:
+	dd if=/dev/zero of=${.TARGET} bs=512 count=1
 
-    <para>You may have realized that a file
-      <filename>/boot/mbr</filename> exists as well.  It is a
-      simplified version of <filename>boot0</filename>.  The code in
-      <filename>mbr</filename> does not provide a menu for the user,
-      it just blindly boots the partition marked active.</para>
+boot2.bin: boot2.out
+	objcopy -S -O binary boot2.out ${.TARGET}
 
+boot2.out: ${BTXDIR}/lib/crt0.o boot2.o sio.o
+	ld -Ttext 0x2000 -o boot2.out
+
+boot2.o: boot2.s
+	${CC} ${ACFLAGS} -c boot2.s
+
+SRCS=	boot2.c boot2.h
+
+boot2.s: boot2.c boot2.h ${.CURDIR}/../../common/ufsread.c
+	${CC} ${CFLAGS} -S -o boot2.s.tmp ${.CURDIR}/boot2.c
+	sed -e '/align/d' -e '/nop/d' "MISSING" boot2.s.tmp > boot2.s
+	rm -f boot2.s.tmp
+
+boot2.h: boot1.out
+	${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
+	    { x = $$1 - ORG1; \
+	    printf("#define XREADORG %#x\n", REL1 + x) }' \
+	    ORG1=`printf "%d" ${ORG1}` \
+	    REL1=`printf "%d" ${REL1}` > ${.TARGET}
+</programlisting>
+The mechanism for building <filename>boot2</filename> is far more elaborated. Let's point out the most relevant facts. The dependency list is as follows:
+<programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot2: boot2.ld
+boot2.ld: boot2.ldr boot2.bin ${BTXDIR}/btx/btx
+boot2.bin: boot2.out
+boot2.out: ${BTXDIR}/lib/crt0.o boot2.o sio.o
+boot2.o: boot2.s
+boot2.s: boot2.c boot2.h ${.CURDIR}/../../common/ufsread.c
+boot2.h: boot1.out
+</programlisting>
+
+Note that initially there is no header <filename>boot2.h</filename> file; its creation depends on <filename>boot1.out</filename>, which we already have. The rule for its creation is a bit terse, but the important thing is that the output, <filename>boot2.h</filename>, is something like this:
+<programlisting><filename>sys/boot/i386/boot2/boot2.h:</filename>
+#define XREADORG 0x725
+</programlisting>
+Recall that <filename>boot1</filename> was relocated (i.e copied from <literal>0x7c00</literal> to <literal>0x700</literal>). Now this relocation will make sense, because as we will see, the <literal>BTX server</literal> reclaims some memory, including where <filename>boot1</filename> was originally loaded. However, it needs access to <filename>boot1</filename>'s <literal>xread</literal> function; this function, according to the output of <filename>boot2.h</filename>, is at location <literal>0x725</literal>. Indeed, the <literal>BTX server</literal> uses the <literal>xread</literal> function from the relocated <filename>boot1</filename>'s code. This function is now accesible from within the <filename>boot2</filename> client.</para>
+<para>
+Having <filename>boot2.h</filename>, we next build <filename>boot2.s</filename> from <filename>boot2.h</filename>, <filename>boot2.c</filename> and <filename>/usr/src/sys/boot/common/ufsread.c</filename>. The rule for this is to compile the code in <filename>boot2.c</filename> into assembly code. The next rule assembles <filename>boot2.s</filename> creating the object file <filename>boot2.o</filename>. After this, the following rule directs the linker to link various files (<filename>crt0.o</filename>, <filename>boot2.o</filename> and <filename>sio.o</filename>). Note that the output file, <filename>boot2.out</filename>, is linked to execute at address <literal>0x2000</literal>. Recall that <filename>boot2</filename> will be executed in user mode, within a special user segment set up by the <literal>BTX server</literal>. This segment starts at <literal>0xa000</literal>. Also, remember that the <filename>boot2</filename> portion of <filename>boot</filename> was copied by <filename>boot1</filename> into address <literal>0xc000</literal>, that is, offset <literal>0x2000</literal> from the start of the user segment, so <filename>boot2</filename> will work properly when we transfer control to it. Next, <filename>boot2.bin</filename> is created from <filename>boot2.out</filename> by stripping its symbols and format information; boot2.bin is a <emphasis>raw</emphasis>. Now, note that a file <filename>boot2.ldr</filename> is created as a 512-byte file full
+</para>
+  </sect1>
+ 
+<sect1 xml:id="boot2">
+    <title><literal>boot2</literal> Stage</title>
+
     <para>The code implementing <filename>boot2</filename> resides in
       <filename>sys/boot/i386/boot2/</filename>, and the executable
       itself is in <filename>/boot</filename>.  The files
@@ -449,10 +815,13 @@
     __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
 	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
 	   0, 0, 0, VTOP(&bootinfo));</programlisting>
+
   </sect1>
 
+
+
   <sect1 xml:id="boot-loader">
-    <title><application>loader</application> Stage</title>
+    <title><literal>loader</literal> Stage</title>
 
     <para><application>loader</application> is a BTX client as well.
       I will not describe it here in detail, there is a comprehensive


More information about the freebsd-doc mailing list