You might wonder, why boot2
comes after
boot0
, and not boot1. Actually, there is a
512-byte file called boot1
in the directory
/boot
as well. It is used for booting from
a floppy. When booting from a floppy,
boot1
plays the same role as
boot0
for a harddisk: it locates
boot2
and runs it.
You may have realized that a file
/boot/mbr
exists as well. It is a
simplified version of boot0
. The code in
mbr
does not provide a menu for the user,
it just blindly boots the partition marked active.
The code implementing boot2
resides in
sys/boot/i386/boot2/
, and the executable
itself is in /boot
. The files
boot0
and boot2
that
are in /boot
are not used by the bootstrap,
but by utilities such as boot0cfg.
The actual position for boot0
is in the
MBR. For boot2
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
ls.
The main task for boot2
is to load the
file /boot/loader
, which is the third stage
in the bootstrapping procedure. The code in
boot2
cannot use any services like
open()
and read()
,
since the kernel is not yet loaded. It must scan the harddisk,
knowing about the filesystem structure, find the file
/boot/loader
, read it into memory using a
BIOS service, and then pass the execution to the loader's entry
point.
Besides that, boot2
prompts for user
input so the loader can be booted from different disk, unit,
slice and partition.
The boot2
binary is created in special
way:
sys/boot/i386/boot2/Makefile:
boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
-o ${.TARGET} -P 1 boot2.binThis Makefile snippet shows that btxld(8) is used to
link the binary. BTX, which stands for BooT eXtender, is a
piece of code that provides a protected mode environment for the
program, called the client, that it is linked with. So
boot2
is a BTX client, i.e., it uses the
service provided by BTX.
The btxld utility is the linker. It links two binaries together. The difference between btxld(8) and ld(1) is that ld usually links object files into a shared object or executable, while btxld links an object file with the BTX, producing the binary file suitable to be put on the beginning of the partition for the system boot.
boot0
passes the execution to BTX's entry
point. BTX then switches the processor to protected mode, and
prepares a simple environment before calling the client. This
includes:
virtual v86 mode. That means, the BTX is a v86 monitor. Real mode instructions like pushf, popf, cli, sti, if called by the client, will work.
Interrupt Descriptor Table (IDT) is set up so all hardware interrupts are routed to the default BIOS's handlers, and interrupt 0x30 is set up to be the syscall gate.
Two system calls: exec
and
exit
, are defined:
sys/boot/i386/btx/lib/btxsys.s:
.set INT_SYS,0x30 # Interrupt number
#
# System call: exit
#
__exit: xorl %eax,%eax # BTX system
int $INT_SYS # call 0x0
#
# System call: exec
#
__exec: movl $0x1,%eax # BTX system
int $INT_SYS # call 0x1BTX creates a Global Descriptor Table (GDT):
sys/boot/i386/btx/btx/btx.s:
gdt: .word 0x0,0x0,0x0,0x0 # Null entry
.word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
.word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
.word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
.word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
.word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
.word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
.word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSSThe client's code and data start from address MEM_USR
(0xa000), and a selector (SEL_UCODE) points to the client's code
segment. The SEL_UCODE descriptor has Descriptor Privilege
Level (DPL) 3, which is the lowest privilege level. But the
INT 0x30
instruction handler resides in a
segment pointed to by the SEL_SCODE (supervisor code) selector,
as shown from the code that creates an IDT:
So, when the client calls __exec()
, the
code will be executed with the highest privileges. This allows
the kernel to change the protected mode data structures, such as
page tables, GDT, IDT, etc later, if needed.
boot2
defines an important structure,
struct bootinfo
. This structure is
initialized by boot2
and passed to the
loader, and then further to the kernel. Some nodes of this
structures are set by boot2
, the rest by the
loader. This structure, among other information, contains the
kernel filename, BIOS harddisk geometry, BIOS drive number for
boot device, physical memory available, envp
pointer etc. The definition for it is:
/usr/include/machine/bootinfo.h:
struct bootinfo {
u_int32_t bi_version;
u_int32_t bi_kernelname; /* represents a char * */
u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */
/* End of fields that are always present. */
#define bi_endcommon bi_n_bios_used
u_int32_t bi_n_bios_used;
u_int32_t bi_bios_geom[N_BIOS_GEOM];
u_int32_t bi_size;
u_int8_t bi_memsizes_valid;
u_int8_t bi_bios_dev; /* bootdev BIOS unit number */
u_int8_t bi_pad[2];
u_int32_t bi_basemem;
u_int32_t bi_extmem;
u_int32_t bi_symtab; /* struct symtab * */
u_int32_t bi_esymtab; /* struct symtab * */
/* Items below only from advanced bootloader */
u_int32_t bi_kernend; /* end of kernel space */
u_int32_t bi_envp; /* environment */
u_int32_t bi_modulep; /* preloaded modules */
};boot2
enters into an infinite loop
waiting for user input, then calls load()
.
If the user does not press anything, the loop breaks by a
timeout, so load()
will load the default
file (/boot/loader
). Functions
ino_t lookup(char *filename)
and
int xfsread(ino_t inode, void *buf, size_t
nbyte)
are used to read the content of a file into
memory. /boot/loader
is an ELF binary, but
where the ELF header is prepended with a.out's struct
exec
structure. load()
scans the
loader's ELF header, loading the content of
/boot/loader
into memory, and passing the
execution to the loader's entry:
sys/boot/i386/boot2/boot2.c:
__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));This, and other documents, can be downloaded from http://ftp.FreeBSD.org/pub/FreeBSD/doc/
For questions about FreeBSD, read the
documentation before
contacting <questions@FreeBSD.org>.
For questions about this documentation, e-mail <doc@FreeBSD.org>.