1.5. `boot2` Stage
Prev | Chapter 1. Bootstrapping and Kernel Initialization | Next

## 1.5. `boot2` Stage

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.bin

This 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 0x1

BTX 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_TSS

The 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:

mov $SEL_SCODE,%dh # Segment selector init.2: shr %bx # Handle this int? jnc init.3 # No mov %ax,(%di) # Set handler offset mov %dh,0x2(%di) # and selector mov %dl,0x5(%di) # Set P:DPL:type add $0x4,%ax # Next handler

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));

Prev | Up | Next
1.4. `boot0` Stage | Home | 1.6. loader Stage

All FreeBSD documents are available for download at http://ftp.FreeBSD.org/pub/FreeBSD/doc/

Questions that are not answered by the documentation may be sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.