19. apr 2018 13:30 UTC
FreeBSD got UEFI support not too long ago, including in the installer. This means you can install on servers without BIOS or where boot mode is set to UEFI. Legacy booting is full of surprises and UEFI will hopefully turn out to be a nice replacement. The only problem I've encountered has been recreating the UEFI partition after replacing a disk, which is what this blogpost is about.
The installer takes care of creating the initial partitions, the default layout looks like this:
[tsr@svaneke ~]$ gpart show da2 => 40 976773088 da2 GPT (466G) 40 1600 1 efi (800K) 1640 1024 2 freebsd-boot (512K) 2664 1432 - free - (716K) 4096 20971520 3 freebsd-swap (10G) 20975616 955797504 4 freebsd-zfs (456G) 976773120 8 - free - (4.0K) [tsr@svaneke ~]$
As you can see, the installer created a small 1600 blocks / 800 kilobytes partition of type
efi before the
freebsd-boot partition. It works well until you need to replace a disk for some reason. Usually I would insert the new disk,
gpart create/add the partitions, run
zpool replace, and run
gpart bootcode on the new disk. But this was before
I physically yank out the old disk and insert the new one. I don't bother to
zpool offline it first. It is
da1 that has been replaced.
These days we use
GPT partitions and the
gpart(8) tool to manage them. First I need to create the
[tsr@svaneke ~]$ sudo gpart create -s GPT da1 Password: da1 created [tsr@svaneke ~]$
Then I need to add the same partitions the installer created when I installed the server. The complete partition layout is shown above. I add the partitions one by one with the same sizes and offsets the installer used:
[tsr@svaneke ~]$ sudo gpart add -t efi -s 1600 da1 da1p1 added [tsr@svaneke ~]$ sudo gpart add -t freebsd-boot -s 1024 da1 da1p2 added [tsr@svaneke ~]$ sudo gpart add -b 4096 -t freebsd-swap -s 10G da1 da1p3 added [tsr@svaneke ~]$ sudo gpart add -t freebsd-zfs -l disk0 da1 da1p4 added [tsr@svaneke ~]$ gpart show da1 => 40 23437770672 da1 GPT (11T) 40 1600 1 efi (800K) 1640 1024 2 freebsd-boot (512K) 2664 1432 - free - (716K) 4096 20971520 3 freebsd-swap (10G) 20975616 23416795096 4 freebsd-zfs (11T) [tsr@svaneke ~]$
So, to sum up: First a small
efi partition of 800K, then the regular
freebsd-boot partition of 512K, then a bit of free space due to 4k alignment, then a 10G
freebsd-swap partition, and finally "the rest" for the main
Also worth noting is that I always give the
ZFS partition a
GPT label with
-l, and then I use that label instead of the
daX device name in the
GPT label the disk according to the numbering of the physical slot on the server chassis. So the label
disk0 means that this disk sits in slot 0 of the chassis.
The result of this is that the disks are named so I can easily replace the correct one at 3am in the datacenter - even if the controller or driver decides to renumber the drives so da0 becomes da17 suddently (yes, that can happen).
This is the easy part.
zpool status shows the current status of the pool:
[tsr@svaneke ~]$ zpool status pool: zroot state: DEGRADED status: One or more devices has been removed by the administrator. Sufficient replicas exist for the pool to continue functioning in a degraded state. action: Online the device using 'zpool online' or replace the device with 'zpool replace'. scan: resilvered 448G in 1h51m with 0 errors on Thu Apr 19 10:54:39 2018 config: NAME STATE READ WRITE CKSUM zroot DEGRADED 0 0 0 mirror-0 DEGRADED 0 0 0 7452263023654412841 REMOVED 0 0 0 was /dev/gpt/disk0 gpt/disk1 ONLINE 0 0 0 [tsr@svaneke ~]$ zpool status
All I need to do is to tell ZFS to replace the missing device with my new
[tsr@svaneke ~]$ sudo zpool replace zroot 7452263023654412841 /dev/gpt/disk0 Password: Make sure to wait until resilver is done before rebooting. If you boot from pool 'zroot', you may need to update boot code on newly attached disk '/dev/gpt/disk0'. Assuming you use GPT partitioning and 'da0' is your new boot disk you may use the following command: gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0 [tsr@svaneke ~]$
ZFS is now resilvering the
mirror-0 vdev. Note the
gpart bootcode line which must still be used to fix the freebsd-boot partition, UEFI or not.
When it is done my
ZPOOL is healthy again (and quite a bit larger :)). Check the resilver status:
[tsr@svaneke ~]$ zpool status pool: zroot state: DEGRADED status: One or more devices is currently being resilvered. The pool will continue to function, possibly in a degraded state. action: Wait for the resilver to complete. scan: resilver in progress since Thu Apr 19 13:17:49 2018 3.01G scanned out of 7.20T at 154M/s, 13h34m to go 164M resilvered, 0.04% done config: NAME STATE READ WRITE CKSUM zroot DEGRADED 0 0 0 mirror-0 DEGRADED 0 0 0 replacing-0 REMOVED 0 0 0 7452263023654412841 REMOVED 0 0 0 was /dev/gpt/disk0 gpt/disk0 ONLINE 0 0 0 (resilvering) gpt/disk1 ONLINE 0 0 0 [tsr@svaneke ~]$
Resilver (and scrub as well) always start out slow, but give it 30 minutes or so and the estimated time to go should be more accurate.
The main point of this post was the realization that I have no idea how to fix the
efi partition so it boots the system. Looking into it I found out that the builtin
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0 command I usually run doesn't deal with the
efi partition at all. The
efi partition is a regular
msdos partition with a couple of files:
[tsr@svaneke ~]$ sudo mount -t msdosfs /dev/da0p1 /mnt/ [tsr@svaneke ~]$ find /mnt/ /mnt/ /mnt/efi /mnt/efi/boot /mnt/efi/boot/BOOTX64.EFI /mnt/efi/boot/STARTUP.NSH [tsr@svaneke ~]$ cp -prf /mnt/efi/ . [tsr@svaneke ~]$ ls -l boot/ total 41 -rwxr-xr-x 1 tsr tsr 131072 Apr 12 2016 BOOTX64.EFI -rwxr-xr-x 1 tsr tsr 12 Apr 12 2016 STARTUP.NSH [tsr@svaneke ~]$ sudo umount /mnt/
So the manual way would be to format the
efi partition on the new disk, and create the same folder structure and copy the files over:
[tsr@svaneke ~]$ sudo newfs_msdos /dev/da1p1 newfs_msdos: trim 25 sectors to adjust to a multiple of 63 /dev/da1p1: 1532 sectors in 1532 FAT12 clusters (512 bytes/cluster) BytesPerSec=512 SecPerClust=1 ResSectors=1 FATs=2 RootDirEnts=512 Sectors=1575 Media=0xf0 FATsecs=5 SecPerTrack=63 Heads=255 HiddenSecs=0 [tsr@svaneke ~]$ sudo mount -t msdosfs /dev/da1p1 /mnt/ [tsr@svaneke ~]$ sudo mkdir -p /mnt/efi/boot [tsr@svaneke ~]$ sudo cp -v boot/* /mnt/efi/boot/ boot/BOOTX64.EFI -> /mnt/efi/boot/BOOTX64.EFI boot/STARTUP.NSH -> /mnt/efi/boot/STARTUP.NSH [tsr@svaneke ~]$ sudo umount /mnt/
I could also have used
dd from the other drive in the mirror to achieve the same result:
[tsr@svaneke ~]$ sudo dd if=/dev/da0p1 of=/dev/da1p1 bs=1M 0+1 records in 0+1 records out 819200 bytes transferred in 0.200710 secs (4081518 bytes/sec) [tsr@svaneke ~]$
Or I could even
dd from the
/boot/boot1.efifat device on the running efi booted system. This method can also work to fix an unbootable system, by uefi booting a live installer usbstick to do this from:
[tsr@svaneke ~]$ sudo dd if=/boot/boot1.efifat of=/dev/da0p1 Password: 1600+0 records in 1600+0 records out 819200 bytes transferred in 1.131642 secs (723904 bytes/sec) [tsr@svaneke ~]$
All three ways work and the server booted up nicely the next time I restarted it.