Boot Code
-
UFS boot code hardly changes
-
ZFS requires an up-to-date boot code as new features require support from yhe boot code.
-
The ESP is created as a file system, so it’s bo longer possible to update it with
gpart bootcode -
The ESP is mointed as default since FreeBSD 13.
Boot Code Types
We have quite a few possible boot configurations options:
- MBR
- MBR EFI
- GPT Legacy
- GPT EFI
- GPT Both
Update Obstacles
- Different partition layouts mean the boot code is not always in the same place.
- Multiple disks - if disks are mirroed/raid, we need to update all copies.
- Are EFI files always in the FreeBSad namespace on the ESP?
- Legacy boot code may be larger than the provided freebsd-boot GPT partition prior to FreeBSD 10? I thnk - you could reduce swap size to make it bigger? GPT partition ordwr doesn’t matter - doesnt need to be first on thr list.
ZFS Upgrades
- Enabling new zpool features requires updated bootcode
- Prior to 13.0 running
zpool upgradeprovided instructions for updating - which were often wrong and dangerous - lost as part of the switch to OpenZFS 2.0
We need to know what to update, note linux doesnt have thks issue because it doesn’t do root on ZFS.
Since FreeBSD does use root on zfs, we unfortunately have to make sure the boot code supports the zfs features used by the pool.
Which one did we boot with? We can run the command sysctl machdep.bootmethod as shown here:
$ sysctl machdep.bootmethod
machdep.bootmethod: BIOS
The above console command shows the system was booted with BIOS mode
If you have both, you should probably update both to avoid having a second boot option that becomes stale.
Gpart show to find partitions
$ gpart show
=> 34 1953525101 ada0 GPT (932G)
34 6 - free - (3.0K)
40 1024 1 freebsd-boot (512K)
1064 50331648 2 freebsd-swap (24G)
50332712 1903192416 3 freebsd-zfs (908G)
1953525128 7 - free - (3.5K)
=> 34 1953525101 ada1 GPT (932G)
34 6 - free - (3.0K)
40 1024 1 freebsd-boot (512K)
1064 50331648 2 freebsd-swap (24G)
50332712 1903192416 3 freebsd-zfs (908G)
1953525128 7 - free - (3.5K)
The above shows we have to drives with boot, swap, and zfs partitions. We can verify with
$ ls -l /dev/ada0*
crw-r----- 1 root operator 0x58 Oct 16 04:55 /dev/ada0
crw-r----- 1 root operator 0x5a Oct 16 04:55 /dev/ada0p1
crw-r----- 1 root operator 0x5c Oct 16 04:55 /dev/ada0p2
crw-r----- 1 root operator 0x5e Oct 16 04:55 /dev/ada0p3
We can confirm running zfs
$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 442G 434G 144K none
zroot@2014-06-08_06.00.00--1m 0 - 144K -
zroot@2014-06-15_06.00.00--1m 0 - 144K -
zroot@2014-06-22_06.00.00--1m 0 - 144K -
zroot@2014-06-29_06.00.00--1m 0 - 144K -
zroot@2014-07-06_06.00.00--1m 0 - 144K -
zroot/ROOT 434G 434G 144K none
zroot/ROOT/10.0-STABLE 8K 434G 409G /
zroot/ROOT/10.4-RELEASE 8K 434G 410G /
zroot/ROOT/12-3 197G 434G 411G /
zroot/ROOT/12-3@2025-10-16-02:42:13 6.50M - 409G -
zroot/ROOT/12-3@2025-10-16-05:12:58 8.93M - 410G -
zroot/ROOT/default 237G 434G 237G /
zroot/ROOT/default@2022-05-26-20:52:39 512K - 237G -
zroot/tmp 390M 434G 390M /tmp
zroot/usr 2.68G 434G 144K /usr
zroot/usr/home 127M 434G 127M /usr/home
zroot/usr/ports 1.34G 434G 1.34G /usr/ports
zroot/usr/src 1.21G 434G 1.21G /usr/src
zroot/var 4.71G 434G 4.61G /var
zroot/var/crash 148K 434G 148K /var/crash
zroot/var/log 101M 434G 101M /var/log
zroot/var/mail 216K 434G 216K /var/mail
zroot/var/tmp 424K 434G 424K /var/tmp
Or
$ mount | grep zfs
zroot/ROOT/12-3 on / (zfs, local, noatime, nfsv4acls)
zroot/tmp on /tmp (zfs, local, noatime, nosuid, nfsv4acls)
zroot/usr/home on /usr/home (zfs, local, noatime, nfsv4acls)
zroot/usr/ports on /usr/ports (zfs, local, noatime, nosuid, nfsv4acls)
zroot/usr/src on /usr/src (zfs, local, noatime, noexec, nosuid, nfsv4acls)
zroot/var on /var (zfs, local, noatime, nfsv4acls)
zroot/var/crash on /var/crash (zfs, local, noatime, noexec, nosuid, nfsv4acls)
zroot/var/log on /var/log (zfs, local, noatime, noexec, nosuid, nfsv4acls)
zroot/var/mail on /var/mail (zfs, local, nfsv4acls)
zroot/var/tmp on /var/tmp (zfs, local, noatime, nosuid, nfsv4acls)
Backup existing
BIOS
$ gpart show
=> 34 1953525101 ada0 GPT (932G)
34 6 - free - (3.0K)
40 1024 1 freebsd-boot (512K)
1064 50331648 2 freebsd-swap (24G)
50332712 1903192416 3 freebsd-zfs (908G)
1953525128 7 - free - (3.5K)
=> 34 1953525101 ada1 GPT (932G)
34 6 - free - (3.0K)
40 1024 1 freebsd-boot (512K)
1064 50331648 2 freebsd-swap (24G)
50332712 1903192416 3 freebsd-zfs (908G)
1953525128 7 - free - (3.5K)
This shows:
- LBA 0: pmbr (protective MBR bootcode)
- LBA 1–33: Primary GPT header + table
- LBA 34–39: padding
- LBA 40–1063: freebsd-boot (1024 sectors = 512 KiB) — this contains gptzfsboot
- Rest: swap + ZFS
So, if you copy sectors 0…1063, you capture pmbr + GPT primary + the entire freebsd-boot partition:
dd if=/dev/ada0 of=/root/ada0.bootcode+gpt.img bs=512 count=1064
Repeat for the mirrored disk:
dd if=/dev/ada1 of=/root/ada1.bootcode+gpt.img bs=512 count=1064
(Better) separated backups
If you want more flexibility (recommended), back up the pieces separately:
- GPT partition layout (primary + backup tables)
gpart backup ada0 > /root/ada0.gpt
gpart backup ada1 > /root/ada1.gpt
- Only the pmbr (LBA 0)
dd if=/dev/ada0 of=/root/ada0.pmbr bs=512 count=1
dd if=/dev/ada1 of=/root/ada1.pmbr bs=512 count=1
- The freebsd-boot partition content itself
dd if=/dev/ada0p1 of=/root/ada0p1.freebsd-boot.img bs=512 conv=sync,noerror
dd if=/dev/ada1p1 of=/root/ada1p1.freebsd-boot.img bs=512 conv=sync,noerror
(Using ada0p1 reads just that 512 KiB boot partition, without touching the rest of the disk.)
Quick verification
Sizes you should see:
ada0p1.freebsd-boot.img ≈ 524,288 bytes (512 KiB)
ada0.pmbr = 512 bytes
ada0.bootcode+gpt.img = 1064 * 512 = 545,792 bytes
Sanity checks:
file /root/ada0p1.freebsd-boot.img
strings /root/ada0p1.freebsd-boot.img | head
sha256 /root/ada0p1.freebsd-boot.img
Restore recipes (pick one style)
A) “Single blob” (what dd count=1064 created)
# DANGER: writes to the start of the disk – choose the correct disk!
dd if=/root/ada0.bootcode+gpt.img of=/dev/NEWDISK bs=512 count=1064
# Then recreate the rest from your normal backups (zpool, etc.) or restore GPT fully:
gpart restore NEWDISK < /root/ada0.gpt
B) Separated (safer & clearer)
- Recreate partitioning:
gpart destroy -F NEWDISK # only if needed on a replacement disk
gpart create -s gpt NEWDISK
gpart restore NEWDISK < /root/ada0.gpt
- Restore boot partition contents:
dd if=/root/ada0p1.freebsd-boot.img of=/dev/NEWDISKp1 bs=512
- (Alternative to step 2) Reinstall from system files instead of an image:
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 NEWDISK
Tip: Do this on both mirror members (e.g., ada0 and ada1) so either disk can boot if the other fails.
For a cleaner, restorable setup, back up GPT, pmbr, and /dev/ada?p1 separately as shown above, and you’ll be able to restore either by raw copy or by re-installing standard bootcode.
UEFI (Do not do this for BIOS)
Since 13.0 the EFI partition should be mounted already:
# mount | grep efi
/dev/gpt/efiboot0 on /boot/efi (msdosfs, local)
Check efi directory
# cd /boot/efi
# ls
efi
Backup:
# cd efi
# ls
boot freebsd
# cd freebsd
# cp loader.efi /root/loader.efi.bak
To restore copy back
Install BIOS bootloader
# gpart bootcode -b /boot/pmbr /boot/gptzfsboot -i 1 /dev/ada0
# gpart bootcode -b /boot/pmbr /boot/gptzfsboot -i 1 /dev/ada1
Index (-i) is freebsd-boot from gpart show
If not using zfs and want to update boot code, simply remove zfs:
# gpart bootcode -b /boot/pmbr /boot/gptboot -i 1 /dev/ada0
# gpart bootcode -b /boot/pmbr /boot/gptboot -i 1 /dev/ada1
Alternatively, easier:
# bsdinstall bootconfig
Other options
sysutils/loaders-update