6 # Installs/updates the necessary boot blocks for the desired boot environment
8 # Lightly tested.. Intended to be installed, but until it matures, it will just
9 # be a boot tool for regression testing.
11 # insert code here to guess what you have -- yikes!
13 # Minimum size of FAT filesystems, in KB.
31 gpart show $dev | tail +2 | awk '$4 == "'$part'" { print $3; }'
36 case ${TARGET:-$(uname -m)} in
37 amd64) echo bootx64 ;;
38 arm64) echo bootaa64 ;;
39 i386) echo bootia32 ;;
41 riscv) echo bootriscv64 ;;
42 *) die "machine type $(uname -m) doesn't support UEFI" ;;
47 local file sizekb loader device stagedir fatbits efibootname
53 if [ "$sizekb" -ge "$fat32min" ]; then
55 elif [ "$sizekb" -ge "$fat16min" ]; then
61 stagedir=$(mktemp -d /tmp/stand-test.XXXXXX)
62 mkdir -p "${stagedir}/EFI/BOOT"
63 efibootname=$(get_uefi_bootname)
64 cp "${loader}" "${stagedir}/EFI/BOOT/${efibootname}.efi"
66 -o fat_type=${fatbits} \
67 -o sectors_per_cluster=1 \
68 -o volume_label=EFISYS \
70 "${file}" "${stagedir}"
75 local dev file mntpt fstype efibootname kbfree loadersize efibootfile
76 local isboot1 existingbootentryloaderfile bootorder bootentry
82 mntpt=$(mktemp -d /tmp/stand-test.XXXXXX)
84 # See if we're using an existing (formatted) ESP
85 fstype=$(fstyp "${dev}")
87 if [ "${fstype}" != "msdosfs" ]; then
88 newfs_msdos -F 32 -c 1 -L EFISYS "${dev}" > /dev/null 2>&1
91 mount -t msdosfs "${dev}" "${mntpt}"
93 die "Failed to mount ${dev} as an msdosfs filesystem"
96 echo "Mounted ESP ${dev} on ${mntpt}"
98 efibootname=$(get_uefi_bootname)
99 kbfree=$(df -k "${mntpt}" | tail -1 | cut -w -f 4)
100 loadersize=$(stat -f %z "${file}")
101 loadersize=$((loadersize / 1024))
103 # Check if /EFI/BOOT/BOOTxx.EFI is the FreeBSD boot1.efi
104 # If it is, remove it to avoid leaving stale files around
105 efibootfile="${mntpt}/EFI/BOOT/${efibootname}.efi"
106 if [ -f "${efibootfile}" ]; then
107 isboot1=$(strings "${efibootfile}" | grep "FreeBSD EFI boot block")
109 if [ -n "${isboot1}" ] && [ "$kbfree" -lt "${loadersize}" ]; then
110 echo "Only ${kbfree}KB space remaining: removing old FreeBSD boot1.efi file /EFI/BOOT/${efibootname}.efi"
112 rmdir "${mntpt}/EFI/BOOT"
114 echo "${kbfree}KB space remaining on ESP: renaming old boot1.efi file /EFI/BOOT/${efibootname}.efi /EFI/BOOT/${efibootname}-old.efi"
115 mv "${efibootfile}" "${mntpt}/EFI/BOOT/${efibootname}-old.efi"
119 if [ ! -f "${mntpt}/EFI/freebsd/loader.efi" ] && [ "$kbfree" -lt "$loadersize" ]; then
122 echo "Failed to update the EFI System Partition ${dev}"
123 echo "Insufficient space remaining for ${file}"
124 echo "Run e.g \"mount -t msdosfs ${dev} /mnt\" to inspect it for files that can be removed."
128 mkdir -p "${mntpt}/EFI/freebsd"
130 # Keep a copy of the existing loader.efi in case there's a problem with the new one
131 if [ -f "${mntpt}/EFI/freebsd/loader.efi" ] && [ "$kbfree" -gt "$((loadersize * 2))" ]; then
132 cp "${mntpt}/EFI/freebsd/loader.efi" "${mntpt}/EFI/freebsd/loader-old.efi"
135 echo "Copying loader to /EFI/freebsd on ESP"
136 cp "${file}" "${mntpt}/EFI/freebsd/loader.efi"
138 if [ -n "${updatesystem}" ]; then
139 existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}//EFI/freebsd/loader.efi")
141 if [ -z "$existingbootentryloaderfile" ]; then
142 # Try again without the double forward-slash in the path
143 existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}/EFI/freebsd/loader.efi")
146 if [ -z "$existingbootentryloaderfile" ]; then
147 echo "Creating UEFI boot entry for FreeBSD"
148 efibootmgr --create --label FreeBSD --loader "${mntpt}/EFI/freebsd/loader.efi" > /dev/null
149 if [ $? -ne 0 ]; then
150 die "Failed to create new boot entry"
153 # When creating new entries, efibootmgr doesn't mark them active, so we need to
154 # do so. It doesn't make it easy to find which entry it just added, so rely on
155 # the fact that it places the new entry first in BootOrder.
156 bootorder=$(efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex | head -1)
157 bootentry=$(echo "${bootorder}" | cut -w -f 3)$(echo "${bootorder}" | cut -w -f 2)
158 echo "Marking UEFI boot entry ${bootentry} active"
159 efibootmgr --activate "${bootentry}" > /dev/null
161 echo "Existing UEFI FreeBSD boot entry found: not creating a new one"
164 # Configure for booting from removable media
165 if [ ! -d "${mntpt}/EFI/BOOT" ]; then
166 mkdir -p "${mntpt}/EFI/BOOT"
168 cp "${file}" "${mntpt}/EFI/BOOT/${efibootname}.efi"
173 echo "Finished updating ESP"
177 local file loaderfile
182 if [ -f "$file" ]; then
183 make_esp_file ${file} ${fat32min} ${loaderfile}
185 make_esp_device ${file} ${loaderfile}
193 s=$(find-part $dev "!239")
194 if [ -z "$s" ] ; then
195 s=$(find-part $dev "efi")
196 if [ -z "$s" ] ; then
197 die "No ESP slice found"
200 make_esp /dev/${dev}s${s} ${dst}/boot/loader.efi
207 idx=$(find-part $dev "efi")
208 if [ -z "$idx" ] ; then
209 die "No ESP partition found"
211 make_esp /dev/${dev}p${idx} ${dst}/boot/loader.efi
214 boot_nogeli_gpt_ufs_legacy() {
218 idx=$(find-part $dev "freebsd-boot")
219 if [ -z "$idx" ] ; then
220 die "No freebsd-boot partition found"
222 doit gpart bootcode -b ${gpt0} -p ${gpt2} -i $idx $dev
225 boot_nogeli_gpt_ufs_uefi() {
229 boot_nogeli_gpt_ufs_both() {
230 boot_nogeli_gpt_ufs_legacy $1 $2 $3
231 boot_nogeli_gpt_ufs_uefi $1 $2 $3
234 boot_nogeli_gpt_zfs_legacy() {
238 idx=$(find-part $dev "freebsd-boot")
239 if [ -z "$idx" ] ; then
240 die "No freebsd-boot partition found"
242 doit gpart bootcode -b ${gpt0} -p ${gptzfs2} -i $idx $dev
245 boot_nogeli_gpt_zfs_uefi() {
249 boot_nogeli_gpt_zfs_both() {
250 boot_nogeli_gpt_zfs_legacy $1 $2 $3
251 boot_nogeli_gpt_zfs_uefi $1 $2 $3
254 boot_nogeli_mbr_ufs_legacy() {
258 doit gpart bootcode -b ${mbr0} ${dev}
259 s=$(find-part $dev "freebsd")
260 if [ -z "$s" ] ; then
261 die "No freebsd slice found"
263 doit gpart bootcode -p ${mbr2} ${dev}s${s}
266 boot_nogeli_mbr_ufs_uefi() {
270 boot_nogeli_mbr_ufs_both() {
271 boot_nogeli_mbr_ufs_legacy $1 $2 $3
272 boot_nogeli_mbr_ufs_uefi $1 $2 $3
275 boot_nogeli_mbr_zfs_legacy() {
279 # search to find the BSD slice
280 s=$(find-part $dev "freebsd")
281 if [ -z "$s" ] ; then
282 die "No BSD slice found"
284 idx=$(find-part ${dev}s${s} "freebsd-zfs")
285 if [ -z "$idx" ] ; then
286 die "No freebsd-zfs slice found"
288 # search to find the freebsd-zfs partition within the slice
289 # Or just assume it is 'a' because it has to be since it fails otherwise
290 doit gpart bootcode -b ${dst}/boot/mbr ${dev}
291 dd if=${dst}/boot/zfsboot of=/tmp/zfsboot1 count=1
292 doit gpart bootcode -b /tmp/zfsboot1 ${dev}s${s} # Put boot1 into the start of part
293 sysctl kern.geom.debugflags=0x10 # Put boot2 into ZFS boot slot
294 doit dd if=${dst}/boot/zfsboot of=/dev/${dev}s${s}a skip=1 seek=1024
295 sysctl kern.geom.debugflags=0x0
298 boot_nogeli_mbr_zfs_uefi() {
302 boot_nogeli_mbr_zfs_both() {
303 boot_nogeli_mbr_zfs_legacy $1 $2 $3
304 boot_nogeli_mbr_zfs_uefi $1 $2 $3
307 boot_geli_gpt_ufs_legacy() {
308 boot_nogeli_gpt_ufs_legacy $1 $2 $3
311 boot_geli_gpt_ufs_uefi() {
312 boot_nogeli_gpt_ufs_uefi $1 $2 $3
315 boot_geli_gpt_ufs_both() {
316 boot_nogeli_gpt_ufs_both $1 $2 $3
319 boot_geli_gpt_zfs_legacy() {
320 boot_nogeli_gpt_zfs_legacy $1 $2 $3
323 boot_geli_gpt_zfs_uefi() {
324 boot_nogeli_gpt_zfs_uefi $1 $2 $3
327 boot_geli_gpt_zfs_both() {
328 boot_nogeli_gpt_zfs_both $1 $2 $3
331 # GELI+MBR is not a valid configuration
332 boot_geli_mbr_ufs_legacy() {
336 boot_geli_mbr_ufs_uefi() {
340 boot_geli_mbr_ufs_both() {
344 boot_geli_mbr_zfs_legacy() {
348 boot_geli_mbr_zfs_uefi() {
352 boot_geli_mbr_zfs_both() {
356 boot_nogeli_vtoc8_ufs_ofw() {
360 # For non-native builds, ensure that geom_part(4) supports VTOC8.
361 kldload geom_part_vtoc8.ko
362 doit gpart bootcode -p ${vtoc8} ${dev}
366 printf 'Usage: %s -b bios [-d destdir] -f fs [-g geli] [-h] [-o optargs] -s scheme <bootdev>\n' "$0"
368 printf ' bootdev device to install the boot code on\n'
369 printf ' -b bios bios type: legacy, uefi or both\n'
370 printf ' -d destdir destination filesystem root\n'
371 printf ' -f fs filesystem type: ufs or zfs\n'
372 printf ' -g geli yes or no\n'
373 printf ' -h this help/usage text\n'
374 printf ' -u Run commands such as efibootmgr to update the\n'
375 printf ' currently running system\n'
376 printf ' -o optargs optional arguments\n'
377 printf ' -s scheme mbr or gpt\n'
383 # Note: we really don't support geli boot in this script yet.
386 while getopts "b:d:f:g:ho:s:u" opt; do
399 [Yy][Ee][Ss]|geli) geli=geli ;;
419 if [ -n "${scheme}" ] && [ -n "${fs}" ] && [ -n "${bios}" ]; then
424 # For gpt, we need to install pmbr as the primary boot loader
426 gpt0=${srcroot}/boot/pmbr
427 gpt2=${srcroot}/boot/gptboot
428 gptzfs2=${srcroot}/boot/gptzfsboot
430 # For MBR, we have lots of choices, but select mbr, boot0 has issues with UEFI
431 mbr0=${srcroot}/boot/mbr
432 mbr2=${srcroot}/boot/boot
435 vtoc8=${srcroot}/boot/boot1
439 # Check if we've been given arguments. If not, this script is probably being
440 # sourced, so we shouldn't run anything.
441 if [ -n "${dev}" ]; then
442 eval boot_${geli}_${scheme}_${fs}_${bios} $dev $srcroot $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}"