UEFI

Because FreeBSD UEFI support is not yet finished, the following will only describe how FreeBSD boots on a BIOS-based machine.
For more information on UEFI booting on FreeBSD look here, and for information on UEFI in general here.

BIOS-Based System

On a BIOS-based system, the BIOS tries to find the MBR in the first 512 bytes of the installed hard disks. On the first hard disk where it finds an MBR it will start to execute the MBR’s boot code. The BIOS usually identifies the MBR by a special boot signature at the end of the MBR. More about that on Wikipedia.

The different MBRs available in FreeBSD are:

  • /boot/boot0: MBR with boot menu
  • /boot/mbr: MBR that just boots the active partition
  • /boot/pmbr: a protective MBR that’s part of a GPT-based disk layout. It’s the one we’re going to use here.

So what are the steps for getting the kernel into memory?
On a plain MBR x86 system, the boot chain ususually looks like this:

BIOS -> MBR (stage1) -> VBR (stage2) -> Boot Loader (stage3) on filesystem -> FreeBSD Kernel on filesystem

MBR = Master Boot Record = stage 1
VBR = Volume Boot Record = stage 2
The 3rd-stage boot loader is the one with the ASCII art splash screen.

And specifically in a non-ZFS FreeBSD system:

BIOS -> /boot/boot0 -> /boot/boot2 -> /boot/loader -> FreeBSD Kernel

The MBR - /boot/boot0 - is stored in the first block of the hard disk and the VBR - /boot/boot2 - in the first block of the partition.

|MBR     | Partition 1                       | Partition 2                       |
|MBR     | VBR | Filesystem of Partition 1   | VBR | Filesystem of Partition 2   |
|1 block | 1 b.|                             | 1 b.|                             |

I just want to note that when I say /boot/boot0, I’m referring to the copy of the MBR or VBR that you can find in /boot.
What I mean by this is that the file in /boot is not the actual MBR, but during installation of the OS, the installer copies the file /boot/boot0 into the first block of the hard disk.

GPT

Since an MBR-based partitioning scheme can only address drives smaller than 2TB a replacement was needed. That replacement is GPT (GUID Partition Table).

But BIOS-based systems still need an MBR. Thus, part of a GPT setup is the PMBR or protective MBR, which contains the boot code to load the 2nd boot loader stage, as well as a partition table that contains only one entry spanning the whole disk. That’s how the protective MBR got its name, because a non-GPT-aware OS will think there is a partition spanning the whole drive and I shouldn’t mess with it.

So for a GPT partition scheme the boot chain looks like:

BIOS -> /boot/pmbr -> /boot/gptboot -> /boot/loader -> FreeBSD Kernel

And the disk layout:

|PMBR    | GPT Header | Partition 1 of type freebsd-boot | Partition 2 | Partition n... |
|1 block | 33 blocks  | /boot/gptboot                    | filesystem  | filesystem     |

The BIOS loads the protective MBR, which looks at GPT header / partition table, finds the first FreeBSD boot partition, and starts executing the 2nd-stage boot loader (which is stored in the beginning of the partition): /boot/gptboot or with ZFS /boot/gptzfsboot.

The 2nd-stage boot loader tries to load the 3rd stage, which is /boot/loader in the case of FreeBSD.
I haven’t checked the other 2nd-stage boot loaders, but /boot/gptboot at least tries to load a kernel directly at /boot/kernel/kernel if it cannot find the 3rd-stage boot loader.

ZFS

With a ZFS-based system the 3rd stage would be /boot/zfsloader and the chain looks like:

BIOS -> /boot/pmbr -> /boot/gptzfsboot -> /boot/zfsloader -> FreeBSD Kernel

Both /boot/gptzfsboot and /boot/zfsloader are already able to read ZFS partitions and locate files necessary for booting.

After installing the boot stages to disk the disk would look like:

|MBR     | GPT Header | Partition 1                               | Partition 2                  |
|PMBR    | GPT Header | /boot/gptzfsboot (freebsd-boot partition) | freebsd-zfs containing /boot |
|1 block | 33 blocks  | usually not very big, e.g., 512k          | probably the zroot volume    |

1 block = 512 bytes

/boot/gptzfsboot starts at block 0 of partition 1

In a real setup you would probably have a freebsd-swap partition before or after the freebsd-zfs partition.

Some more information on the FreeBSD boot process on x86.