The FreeBSD Boot Process                                         r0.2
----------------------------------------------------------------------
Michael Smith

Overview

"Bootstrapping" refers to the process which takes the system from an
initial state and makes it ready for use.  The name is derived from
the expression "to lift oneself by ones' own bootstraps", which refers 
to the fact that the boot software has to do everything by itself.

This document describes the process of bootstrapping FreeBSD 3.1 on
the i386 platform.  There are similarities but also significant
differences between this process and that used in earlier releases.
Future releases will expand on this process to add extra
functionality.

Discussion here is limited to booting from local disk, as there is no
supported diskless boot mechanism for the 3.1 release.

BIOS

When booting from a floppy or fixed disk, the boot process begins with
the system BIOS reading the first sector from the boot disk.  This
sector is loaded into low memory at 0x7c00, and the BIOS then passes
execution to the code that is presumed to live there.

The CDROM boot process is similar, but slightly more complex.  The
BIOS uses a region placed at the beginning of the CDROM by the disk's
manufacturer to emulate a read-only floppy or hard disk drive.  Apart
from this emulation, the boot process is otherwise identical.

Boot0 and the MBR

Hard disks may be "sliced" or "unsliced".  Unsliced disks don't have a 
boot0 stage, nor a real MBR.  Sliced disks, however, are compatible
with other operating systems (eg. Linux, Windows, etc.), and they
thus must contain a compatible Master Boot Record.

The Master Boot Record contains the slice table, which can be used to
divide the disk into separate regions for use by different operating
systems.  FreeBSD normally only requires one slice.

Note: Other operating systems call slices "partitions".  FreeBSD uses
      this term to refer to the regions contained within a slice.

Traditionally, the code in the MBR immediately reads the first sector
of the slice marked 'active' into memory, and passes control to it in
the same fashion that the BIOS did.  For systems with more than one
operating system installed, it is convenient to be able to select a
slice to boot from without having to change the 'active' marking.

This is accomplished using a tool known as a "boot manager".  In the
past, FreeBSD used the BootEasy boot manager.  FreeBSD 3.1 now uses a
replacement for BootEasy known simply as boot0.

Boot0 presents a short menu listing the slice types on the current
disk, and waits for the user to select one.  After a timeout, it will
automatically proceed to load from the default (usually the slice
selected last time).  If there are other disks in the system, it will
list those on the menu as well.

Boot1/2

At the base of every FreeBSD slice, and every unsliced disk, is an 8k
region set aside for bootstrap code.  If the disk/slice is being
booted, the first sector (512 bytes) of this block will be read and
executed.  This region is known as boot1, and its purpose is to
locate and read the remaining 7.5k, known as boot2.

Together, boot1 and boot2 contain the core functionality required to
read FreeBSD filesystems, and interact with the system console.  Their
normal task is to locate the next stage of the boot process, the
loader, from within the 'a' partition of the disk or FreeBSD slice
that has been booted.

In addition, boot1/2 are capable of booting both a.out and ELF kernels 
directly, as well as a non-default loader, in order to effect
emergency repairs.  Boot1/2 are documented in the boot(8) manpage.

The Loader

As its name suggests, the goal of the loader is to load the FreeBSD
kernel and related components.  By default it will pause for 10
seconds, load the file /kernel as a FreeBSD kernel and start it.

There are, however, many additional features provided by the loader.
Some are designed to work around difficulties presented by the PC
environment, while others add functionality to the boot process which
enables the administrator to customise their environment.

Advanced use of the loader

To allow for customised boot behaviour, the loader contains a command
interpreter and commandline interface.  At startup, commands are read
from /boot/loader.rc.  By default, once the processing of loader.rc is
completed, the automatic boot process resumes.  This can be overridden 
as described below under the 'autoboot_delay' variable.

The command interpreter understands the following commands:

autoboot [<delay> [<prompt>]]

	Displays <prompt> or a default prompt, and counts down <delay>
	seconds before attempting to boot.  If <delay> is not
	specified, the default value is 10.

boot [-<arg> ...] [<kernelname>]

	Start the loaded kernel.  If arguments are specified, they are
	added to the arguments for the kernel.  If <kernelname> is
	specified, and a kernel has not already been loaded, it will
	be booted instead of the default kernel.

echo [-n] [<message>]

	Emits <message>, with no trailing newline if -n is specified.
	This is most useful in conjunction with scripts and the '@'
	line prefix.

	Variables are substituted by prefixing them with $, eg.

		echo Current device is $currdev

	will print the current device.

help [topic [subtopic]]
?

	The help command displays help on commands and their usage.

	In command help, a term enclosed with <...> indicates a value
	as described by the term.  A term enclosed with [...] is
	optional, and may not be required by all forms of the command.

	Some commands may not be availalble.  Use the '?' command to
	list most available commands.

include <filename>

	The entire contents of <filename> are read into memory before
	executing commands, so it is safe to source a file from
	removable media.

load [-t <type>] <filename>

	Loads the module contained in <filename> into memory.  If no
	other modules are loaded, <filename> must be a kernel or the
	command will fail.

	If -t is specified, the module is loaded as raw data of
	<type>, for later use by the kernel or other modules.  <type>
	may be any string.

	Common types include:

	userconfig_script	The file contains commands for the 
				kernel's UserConfig interface, eg. to
				assign PnP resources.

ls [-l] [<path>]

	Displays a listing of files in the directory <path>, or the
	root directory of the current device if <path> is not
	specified.

	The -l argument displays file sizes as well; the process of
	obtaining file sizes on some media may be very slow.

lsdev [-v]

	List all of the devices from which it may be possible to load
	modules. If -v is specified, print more details.

lsmod [-v]

	List loaded modules. If [-v] is specified, print more details.

pnpscan [-v]

	Scan for Plug-and-Play devices.  This command is normally
	automatically run as part of the boot process, in order to
	dynamically load modules required for system operation.
	(Note: automatic loading of modules is not implemented as of
	this writing.)

	If the -v argument is specified, details on the devices found
	will be printed.

read [-t <value>] [-p <prompt>] [<variable name>]

	The read command reads a line of input from the terminal.  If
	the -t argument is specified, it will return nothing if no
	input has been received after <value> seconds.  (Any keypress
	will cancel the timeout).

	If -p is specified, <prompt> is printed before reading
	input. No newline is emitted after the prompt.

	If a variable name is supplied, the variable is set to the
	value read, less any terminating newline.

reboot

	Causes the system to immediately reboot.

set <variable name>
set <variable name>=<value>

	The set command is used to set variables.

show [<variable>]

	Displays the value of <variable>, or all variables if not
	specified. Multiple paths can be separated with a semicolon.

unload

	This command removes any kernel and all loaded modules from
	memory.

unset <variable name>

	If allowed, the named variable's value is discarded and the
	variable is removed.

Variables

The loader provides support for variables in a fashion similar to
environment variables.  They may be set and displayed with the 'set'
and 'show' commands, as well as substituted in other commands by
prefixing the variable name with $.

Integer variables have a numeric value, string variables may contain
any character string, while boolean variables are true if set and
false if unset.

autoboot_delay		(integer)

	Sets the default delay for the autoboot command to its value
	in seconds.  If set to 0 in while loader.rc is being
	processed, the implicit autoboot at the completion of
	loader.rc will be disabled.

boot_askname		(boolean)

	If set, instructs the kernel to prompt the user for the name
	of the root device when the kernel is booted. (Requires
	support	in the kernel.)

boot_ddb		(boolean)

	If set, instructs the kernel to start in the DDB debugger,
	rather than proceeding to initialise when booted.

boot_gdb		(boolean)

	If set, selects gdb-remote mode for the kernel debugger by
	default.

boot_single		(boolean)

	If set, prevents the kernel from initiating a multi-user
	startup, single-user mode will be entered when the kernel has
	finished device probes.

boot_userconfig		(boolean)

	Requests that the kernel's interactive device configuration
	program be run when the kernel is booted.

boot_verbose		(boolean)

	Setting this variable causes extra debugging information to be
	printed by the kernel during the boot phase.

bootfile		(string)

	The default search path for bootable kernels is
	/kernel;/kernel.old.  It may be overridden by setting this
	variable to a semicolon-separated list of paths, which will be
	searched for in turn.

console			(string)

	Sets the current console.  If a value is omitted, a list of
	valid consoles will be displayed.  Typically the 'vidconsole'
	(primary video card) and 'comconsole' (first serial port)
	consoles will be available.

currdev			(string)

	Selects the default device.  Syntax for devices is odd.

module_path		(string)

	Sets the list of directories which will be searched in for
	modules named in a load command or implicitly required by a
	dependancy.

prompt			(string)

	The command prompt is displayed when the loader is waiting for
	input. Variable substitution is performed on the prompt when
	it is displayed.  The default prompt can be set with:

		set prompt=\$currdev>

root_disk_unit		(integer)

	If the code which detects the disk unit number for the root
	disk is confused, eg. by a mix of SCSI and IDE disks, or IDE
	disks with gaps in the sequence (eg. no primary slave), the
	unit number can be forced by setting this variable.

	Common values would be 0 (for the first SCSI disk in a system
	with one or more IDE disks) or 2 (for a system with FreeBSD on 
	the secondary master IDE disk and no primary slave disk).

Parser

Commands are parsed when they are read from a file or the
commandline.  The parsing process performs a number of straightforward 
substitutions which make writing scripts easier.

 - Variable substitution is available; use ${name} to have the value
   of the variable 'name' inserted.

 - Items containing spaces may be quoted, eg. set variable="some text"

 - A comprehensive set of escapes are available:
	\\	backslash
	\"	quote
	\$	dollar symbol
	\b	backspace
	\f	formfeed
	\r	carriage return
	\n	newline
	\s	space
	\v	vertical tab
	\z	ignored
	\OOO	octal character value
	\x00	hexadecical character value
	
BootForth

The loader also contains a Forth interpreter, derived from the popular 
Ficl implementation.  Programming in Forth is beyond the scope of this 
document, however Ficl implements a nearly complete ANS forth, with
access to full loader functionality.

Work on BootForth is ongoing; expect a BootForth tutorial in the
future.

Kernel

Once the kernel and any required modules are loaded, control is
transferred to the kernel, which proceeds to size memory, probe for
devices, and mount the root filesystem in the traditional fashion.

Tunable Values

Various kernel tunable parameters can be overriden by specifying new
values in the loader's environment.

kern.ipc.nmbclusters	(integer)

	Set the number of mbuf clusters to be allocated.  The value
	cannot be set below the default determined when the kernel
	was compiled.

kern.vm.kmem.size	(integer)

	Sets the size of kernel memory (bytes).  This overrides
	completely the value determined when the kernel was
	compiled.


Installing/Repairing the Bootstrap Code

[This section under construction]
