docs/185780: Rewrite of Bootstrapping in FreeBSD Architecture Handbook

Sergio Andrés Gómez del Real Sergio.G.DelReal at gmail.com
Tue Jan 14 20:50:00 UTC 2014


>Number:         185780
>Category:       docs
>Synopsis:       Rewrite of Bootstrapping in FreeBSD Architecture Handbook
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-doc
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jan 14 20:50:00 UTC 2014
>Closed-Date:
>Last-Modified:
>Originator:     Sergio Andrés Gómez del Real
>Release:        10.0-RC5
>Organization:
>Environment:
FreeBSD UIX 10.0-RC5 FreeBSD 10.0-RC5 #0 r260430: Wed Jan  8 05:10:04 UTC 2014     root at snap.freebsd.org:/usr/obj/usr/src/sys/GENERIC  amd64
>Description:
Documentation of the boot process in the FreeBSD Architecture Handbook is currently both outdated and wrong. I've been working on a more precise and detailed version; it is not yet complete, but here is something to start.
>How-To-Repeat:

>Fix:
Apply patch.

Patch attached with submission follows:

Index: boot/chapter.xml
===================================================================
--- boot/chapter.xml	(revision 43469)
+++ boot/chapter.xml	(working copy)
@@ -3,89 +3,123 @@
 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$
 -->
-<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="boot">
-  <info><title>Bootstrapping and Kernel Initialization</title>
+<chapter xmlns="http://docbook.org/ns/docbook"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+  version="5.0" xml:id="boot">
+  <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>Sergey</firstname><surname>Lyubka</surname></personname><contrib>Contributed by</contrib></author> <!-- devnull at uptsoft.com  12 Jun 2002 -->
     </authorgroup>
-  </info>
-  
+    <authorgroup>
+      <author><personname><firstname>Sergio Andrés</firstname><surname>Gómez del Real</surname></personname></author> <!-- Jan 2014 -->
+    </authorgroup></info>
+    <sect1 xml:id="boot-synopsis">
+      <title>Synopsis</title>
 
-  <sect1 xml:id="boot-synopsis">
-    <title>Synopsis</title>
+      <indexterm><primary>BIOS</primary></indexterm>
+      <indexterm><primary>firmware</primary></indexterm>
+      <indexterm><primary>POST</primary></indexterm>
+      <indexterm><primary>IA-32</primary></indexterm>
+      <indexterm><primary>booting</primary></indexterm>
+      <indexterm><primary>system initialization</primary></indexterm>
 
-    <indexterm><primary>BIOS</primary></indexterm>
-    <indexterm><primary>firmware</primary></indexterm>
-    <indexterm><primary>POST</primary></indexterm>
-    <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
-      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
-      architecture is used as an example.</para>
-  </sect1>
+      <para>The &os;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 architecture is used as an example.</para>
+    </sect1>
 
-  <sect1 xml:id="boot-overview">
-    <title>Overview</title>
+    <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.
+	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 &os; 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>
+      <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>
+	    <row>
+	      <entry><para>Output (may vary)</para></entry>
+	      <entry><para>BIOS (firmware) messages</para></entry>
+	    </row>
 
-    <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>
-
-    <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>
-
-    <informaltable frame="none" pgwide="0">
-      <tgroup cols="2">
-	<tbody>
-	  <row>
-	    <entry><para>Output (may vary)</para></entry>
-	    <entry><para>BIOS (firmware) messages</para></entry>
-	  </row>
-
-	  <row>
-	    <entry><para><screen>F1    FreeBSD
+	    <row>
+	      <entry><para><screen>F1    FreeBSD
 F2    BSD
 F5    Disk 2</screen></para></entry>
-	    <entry><para><literal>boot0</literal></para></entry>
-	  </row>
+	      <entry><para><literal>boot0</literal></para></entry>
+	    </row>
 
-	  <row>
-	    <entry><para><screen>>>FreeBSD/i386 BOOT
+	    <row>
+	      <entry><para><screen>>>FreeBSD/i386 BOOT
 Default: 1:ad(1,a)/boot/loader
 boot:</screen></para></entry>
-	    <entry><para><literal>boot2</literal><footnote><para>This
-		prompt will appear if the user presses a key just
-		after selecting an OS to boot at the
-		<literal>boot0</literal>
-		stage.</para></footnote></para></entry>
-	  </row>
+	      <entry><para><literal>boot2</literal><footnote><para>This prompt will appear if the user presses a key just after selecting an OS to boot at the <literal>boot0</literal>stage.</para></footnote></para></entry></row>
 
-	  <row>
-	    <entry><para><screen>BTX loader 1.0 BTX version is 1.01
+	    <row>
+	      <entry><para><screen>BTX loader 1.0 BTX version is 1.01
 BIOS drive A: is disk0
 BIOS drive C: is disk1
 BIOS 639kB/64512kB available memory
@@ -95,41 +129,66 @@
 /kernel text=0x1234 data=0x2345 syms=[0x4+0x3456]
 Hit [Enter] to boot immediately, or any other key for command prompt
 Booting [kernel] in 9 seconds..._</screen></para></entry>
-	    <entry><para>loader</para></entry>
-	  </row>
+	      <entry><para>loader</para></entry>
+	    </row>
 
-	  <row>
-	    <entry><para><screen>Copyright (c) 1992-2002 The FreeBSD Project.
+	    <row>
+	      <entry><para><screen>Copyright (c) 1992-2002 The FreeBSD Project.
 Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
         The Regents of the University of California. All rights reserved.
 FreeBSD 4.6-RC #0: Sat May  4 22:49:02 GMT 2002
     devnull at kukas:/usr/obj/usr/src/sys/DEVNULL
 Timecounter "i8254"  frequency 1193182 Hz</screen></para></entry>
-	    <entry><para>kernel</para></entry>
-	  </row>
-	</tbody>
-      </tgroup>
-    </informaltable>
+	      <entry><para>kernel</para></entry>
+	    </row>
+	  </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 +198,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 +213,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,145 +225,997 @@
 	  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
-      installation procedure wrote to your harddisk's MBR if you chose
-      the <quote>bootmanager</quote> option at installation
-      time.</para>
+<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
+  &os;'s installation procedure wrote to your
+  harddisk's MBR if you chose the <quote>bootmanager</quote>
+  option at installation 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
-      Nordier.</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 <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
-      <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
-      terminology, sliced.  One byte of those 16 says whether a
-      partition (slice) is bootable or not.  Exactly one record must
-      have that flag set, otherwise <filename>boot0</filename>'s code
-      will refuse to proceed.</para>
 
-    <para>A partition record has the following fields:</para>
+<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 &os;'s
+  terminology, sliced.  One byte of those 16 says whether a
+  partition (slice) is bootable or not.  Exactly one record must
+  have that flag set, otherwise <filename>boot0</filename>'s code
+  will refuse to proceed.</para>
 
-    <itemizedlist>
-      <listitem>
-	<para>the 1-byte filesystem type</para>
-      </listitem>
+<para>A partition record has the following fields:</para>
+<itemizedlist>
+  <listitem>
+    <para>the 1-byte filesystem type</para>
+  </listitem>
 
-      <listitem>
-	<para>the 1-byte bootable flag</para>
-      </listitem>
+  <listitem>
+    <para>the 1-byte bootable flag</para>
+  </listitem>
 
-      <listitem>
-	<para>the 6 byte descriptor in CHS format</para>
-      </listitem>
+  <listitem>
+    <para>the 6 byte descriptor in CHS format</para>
+  </listitem>
 
-      <listitem>
-	<para>the 8 byte descriptor in LBA format</para>
-      </listitem>
-    </itemizedlist>
+  <listitem>
+    <para>the 8 byte descriptor in LBA format</para>
+  </listitem>
+</itemizedlist>
 
-    <para>A partition record descriptor has the information about
-      where exactly the partition resides on the drive.  Both
-      descriptors, LBA and CHS, describe the same information, but in
-      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>
+<para>A partition record descriptor has the information about
+  where exactly the partition resides on the drive.  Both
+  descriptors, LBA and CHS, describe the same information, but in
+  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.  The partition table ends with the special signature <literal>0xaa55</literal>.</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 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>It is also worth looking at the Makefile for
+  <filename>boot0</filename>
+  (<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):</para>
+
+<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>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>:</para>
+<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>
+
+<para>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:</para>
+<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>
+
+<para>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>:</para>
+<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>
+
+<para>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:</para>
+<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>
+
+<para>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
+  (&os;; ID 0xa5).  The implementation is fairly simple:</para>
+
+<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>
+
+<para>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:</para>
+<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>
+
+<para>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:</para>
+<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>
+
+<para>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:</para>
+<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>
+
+<para>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:</para>
+<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>
+
+<para>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 &os; partition.
+  The transfer buffer is set to <literal>0x7c00</literal>
+  (register <literal>%bx</literal>), and a read for
+  the first sector of the &os; 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 &os; partition (slice) to address <literal>0x7c00</literal>.  Execution control was passed to that location.</para>
       </listitem>
-
-      <listitem>
-	<para>loads the first sector of the selected partition (slice)
-	  into memory and jumps there</para>
-      </listitem>
     </itemizedlist>
 
-    <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>
+    <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 &os; 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 &os; slice (512 bytes),
+    it is actually loading <filename>boot1</filename> and
+    transferring 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>
 
-  <sect1 xml:id="boot-boot2">
-    <title><literal>boot2</literal> Stage</title>
+  <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:</para>
+  <programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+start:
+ jmp main</programlisting>
 
-    <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>
+<para>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:</para>
+<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>
 
-    <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>
+<para>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>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
-      <filename>boot0</filename> and <filename>boot2</filename> that
-      are in <filename>/boot</filename> are not used by the bootstrap,
-      but by utilities such as <application>boot0cfg</application>.
-      The actual position for <filename>boot0</filename> is in the
-      MBR.  For <filename>boot2</filename> it is the beginning of a
-      bootable FreeBSD slice.  These locations are not under the
-      filesystem's control, so they are invisible to commands like
-      <application>ls</application>.</para>
+<para>Next comes a loop that looks for the &os; partition.
+  Note that, although <filename>boot0</filename> in fact
+  loaded <filename>boot1</filename> from the &os; 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 &os; partition starts.
+  Therefore, it rereads the MBR:</para>
+<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>
 
-    <para>The main task for <literal>boot2</literal> is to load the
-      file <filename>/boot/loader</filename>, which is the third stage
-      in the bootstrapping procedure.  The code in
-      <literal>boot2</literal> cannot use any services like
-      <function>open()</function> and <function>read()</function>,
-      since the kernel is not yet loaded.  It must scan the harddisk,
-      knowing about the filesystem structure, find the file
-      <filename>/boot/loader</filename>, read it into memory using a
-      BIOS service, and then pass the execution to the loader's entry
-      point.</para>
+<para>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:</para>
 
-    <para>Besides that, <literal>boot2</literal> prompts for user
-      input so the loader can be booted from different disk, unit,
-      slice and partition.</para>
+<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>
 
-    <para>The <literal>boot2</literal> binary is created in special
-      way:</para>
+<para>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>:</para>
+<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>
 
-    <programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+<para>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:</para>
+<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>
+
+<para>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 &os; partition begins.  The implementation
+  is as follows:</para>
+<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>
+
+<para>This code, which follows the call to
+  <literal>nread</literal>, implements the scan in the
+  partition table looking for the &os;
+  partition.  If a &os; partition is successfully identified,
+  execution continues at <literal>main.5</literal>;
+  we assume this behaviour, so we continue execution
+  there:</para>
+<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>Recall that at this point, register
+  <literal>%si</literal> points to the &os; 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 &os; partition), coincides with
+  the <filename>boot1</filename> program.  Also recall that the
+  file written to the beginning of the &os; 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:</para>
+<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>
+
+<para>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 &os; 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>:</para>
+<programlisting><filename>sys/boot/i386/boot2/boot1.S:</filename>
+cat boot1 boot2 > boot</programlisting>
+
+<para>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 &os; 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 &os; 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 &os; 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>:</para>
+<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>
+
+<para>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:</para>
+  <itemizedlist>
+    <listitem>
+      <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 &os; 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 &os; 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>
+  <para>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:</para>
+  <programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot: boot1 boot2
+	cat boot1 boot2 > boot</programlisting>
+
+<para>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:</para>
+<programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot1: boot1.out
+	objcopy -S -O binary boot1.out ${.TARGET}
+
+boot1.out: boot1.o
+	ld -e start -Ttext 0x7c00 -o boot1.out boot1.o</programlisting>
+
+<para>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:</para>
+<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
+
 boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
 	btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
+	    -o ${.TARGET} -P 1 boot2.bin
+
+boot2.ldr:
+	dd if=/dev/zero of=${.TARGET} bs=512 count=1
+
+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>
+
+<para>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:</para>
+<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>
+
+<para>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:</para>
+<programlisting><filename>sys/boot/i386/boot2/boot2.h:</filename>
+#define XREADORG 0x725</programlisting>
+
+<para>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
+    <filename>boot0</filename> and <filename>boot2</filename> that
+    are in <filename>/boot</filename> are not used by the bootstrap,
+    but by utilities such as <application>boot0cfg</application>.
+    The actual position for <filename>boot0</filename> is in the
+    MBR.  For <filename>boot2</filename> it is the beginning of a
+    bootable &os; slice.  These locations are not under the
+    filesystem's control, so they are invisible to commands like
+    <application>ls</application>.</para>
+
+  <para>The main task for <literal>boot2</literal> is to load the
+    file <filename>/boot/loader</filename>, which is the third stage
+    in the bootstrapping procedure.  The code in
+    <literal>boot2</literal> cannot use any services like
+    <function>open()</function> and <function>read()</function>,
+    since the kernel is not yet loaded.  It must scan the harddisk,
+    knowing about the filesystem structure, find the file
+    <filename>/boot/loader</filename>, read it into memory using a
+    BIOS service, and then pass the execution to the loader's entry
+    point.</para>
+
+  <para>Besides that, <literal>boot2</literal> prompts for user
+    input so the loader can be booted from different disk, unit,
+    slice and partition.</para>
+
+  <para>The <literal>boot2</literal> binary is created in special
+    way:</para>
+
+  <programlisting><filename>sys/boot/i386/boot2/Makefile:</filename>
+boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
+	btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
 	    -o ${.TARGET} -P 1 boot2.bin</programlisting>
 
     <indexterm><primary>BTX</primary></indexterm>
@@ -440,7 +1351,7 @@
       nbyte)</function> are used to read the content of a file into
       memory.  <filename>/boot/loader</filename> is an ELF binary, but
       where the ELF header is prepended with a.out's <literal>struct
-      exec</literal> structure. <function>load()</function> scans the
+      exec</literal> structure.  <function>load()</function> scans the
       loader's ELF header, loading the content of
       <filename>/boot/loader</filename> into memory, and passing the
       execution to the loader's entry:</para>
@@ -449,10 +1360,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
@@ -703,7 +1617,7 @@
 	virtual address for this example would just be the value of
 	EIP. Segment registers such as CS, DS etc are the selectors,
 	i.e., indexes, into GDT (to be more precise, an index is not a
-	selector itself, but the INDEX field of a selector). FreeBSD's
+	selector itself, but the INDEX field of a selector).  &os;'s
 	GDT holds descriptors for 15 selectors per CPU:</para>
 
       <programlisting><filename>sys/i386/i386/machdep.c:</filename>
@@ -911,7 +1825,7 @@
 	structure, and that is what the second
 	<literal>__asm</literal> is.  The third
 	<literal>__asm</literal> instruction marks the end of a
-	section. If a directive with the same section name occurred
+	section.  If a directive with the same section name occurred
 	before, the content, i.e., the 32-bit value, will be appended
 	to the existing section, so forming an array of 32-bit
 	pointers.</para>
Index: driverbasics/chapter.xml
===================================================================
--- driverbasics/chapter.xml	(revision 43469)
+++ driverbasics/chapter.xml	(working copy)
@@ -13,7 +13,6 @@
       <author><personname><firstname>Jörg</firstname><surname>Wunsch</surname></personname><contrib>Based on intro(4) manual page by </contrib></author>
     </authorgroup>
   </info>
-  
 
   <sect1 xml:id="driverbasics-intro">
     <title>Introduction</title>


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-doc mailing list