Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/boot/install-boot.sh
39475 views
1
#!/bin/sh
2
3
#
4
# Installs/updates the necessary boot blocks for the desired boot environment
5
#
6
# Lightly tested.. Intended to be installed, but until it matures, it will just
7
# be a boot tool for regression testing.
8
9
# insert code here to guess what you have -- yikes!
10
11
# Minimum size of FAT filesystems, in KB.
12
fat32min=33292
13
fat16min=2100
14
15
die() {
16
echo $*
17
exit 1
18
}
19
20
doit() {
21
echo $*
22
eval $*
23
}
24
25
find_part() {
26
dev=$1
27
part=$2
28
29
gpart show $dev | tail +2 | awk '$4 == "'$part'" { print $3; }'
30
}
31
32
get_uefi_bootname() {
33
34
case ${TARGET:-$(uname -m)} in
35
amd64) echo bootx64 ;;
36
arm64) echo bootaa64 ;;
37
i386) echo bootia32 ;;
38
arm) echo bootarm ;;
39
riscv) echo bootriscv64 ;;
40
*) die "machine type $(uname -m) doesn't support UEFI" ;;
41
esac
42
}
43
44
make_esp_file() {
45
local file sizekb device stagedir fatbits efibootname
46
47
file=$1
48
sizekb=$2
49
50
if [ "$sizekb" -ge "$fat32min" ]; then
51
fatbits=32
52
elif [ "$sizekb" -ge "$fat16min" ]; then
53
fatbits=16
54
else
55
fatbits=12
56
fi
57
58
stagedir=$(mktemp -d /tmp/stand-test.XXXXXX)
59
mkdir -p "${stagedir}/EFI/BOOT"
60
61
# Allow multiple files to be copied.
62
# We do this in pairs, e.g:
63
# make_esp_file ... loader1.efi bootx64 loader2.efi bootia32
64
#
65
# If the second argument is left out,
66
# determine it automatically.
67
shift; shift # Skip $file and $sizekb
68
while [ ! -z $1 ]; do
69
if [ ! -z $2 ]; then
70
efibootname=$2
71
else
72
efibootname=$(get_uefi_bootname)
73
fi
74
cp "$1" "${stagedir}/EFI/BOOT/${efibootname}.efi"
75
76
shift; shift || : # Ignore failure to shift
77
done
78
79
makefs -t msdos \
80
-o fat_type=${fatbits} \
81
-o sectors_per_cluster=1 \
82
-o volume_label=EFISYS \
83
-s ${sizekb}k \
84
"${file}" "${stagedir}"
85
rm -rf "${stagedir}"
86
}
87
88
make_esp_device() {
89
local dev file dst mntpt fstype efibootname kbfree loadersize efibootfile
90
local isboot1 existingbootentryloaderfile bootorder bootentry
91
92
# ESP device node
93
dev=$1
94
file=$2
95
# Allow caller to override the default
96
if [ ! -z $3 ]; then
97
efibootname=$3
98
else
99
efibootname=$(get_uefi_bootname)
100
fi
101
102
dst=$(basename ${file%.efi})
103
mntpt=$(mktemp -d /tmp/stand-test.XXXXXX)
104
105
# See if we're using an existing (formatted) ESP
106
fstype=$(fstyp "${dev}")
107
108
if [ "${fstype}" != "msdosfs" ]; then
109
newfs_msdos -F 32 -c 1 -L EFISYS "${dev}" > /dev/null 2>&1
110
fi
111
112
mount -t msdosfs "${dev}" "${mntpt}"
113
if [ $? -ne 0 ]; then
114
die "Failed to mount ${dev} as an msdosfs filesystem"
115
fi
116
117
echo "Mounted ESP ${dev} on ${mntpt}"
118
119
kbfree=$(df -k "${mntpt}" | tail -1 | cut -w -f 4)
120
loadersize=$(stat -f %z "${file}")
121
loadersize=$((loadersize / 1024))
122
123
# Check if /EFI/BOOT/BOOTxx.EFI is the FreeBSD boot1.efi
124
# If it is, remove it to avoid leaving stale files around
125
efibootfile="${mntpt}/EFI/BOOT/${efibootname}.efi"
126
if [ -f "${efibootfile}" ]; then
127
isboot1=$(strings "${efibootfile}" | grep "FreeBSD EFI boot block")
128
129
if [ -n "${isboot1}" ] && [ "$kbfree" -lt "${loadersize}" ]; then
130
echo "Only ${kbfree}KB space remaining: removing old FreeBSD boot1.efi file /EFI/BOOT/${efibootname}.efi"
131
rm "${efibootfile}"
132
rmdir "${mntpt}/EFI/BOOT"
133
else
134
echo "${kbfree}KB space remaining on ESP: renaming old boot1.efi file /EFI/BOOT/${efibootname}.efi /EFI/BOOT/${efibootname}-old.efi"
135
mv "${efibootfile}" "${mntpt}/EFI/BOOT/${efibootname}-old.efi"
136
fi
137
fi
138
139
if [ ! -f "${mntpt}/EFI/freebsd/${dst}.efi" ] && [ "$kbfree" -lt "$loadersize" ]; then
140
umount "${mntpt}"
141
rmdir "${mntpt}"
142
echo "Failed to update the EFI System Partition ${dev}"
143
echo "Insufficient space remaining for ${file}"
144
echo "Run e.g \"mount -t msdosfs ${dev} /mnt\" to inspect it for files that can be removed."
145
die
146
fi
147
148
mkdir -p "${mntpt}/EFI/freebsd"
149
150
# Keep a copy of the existing loader.efi in case there's a problem with the new one
151
if [ -f "${mntpt}/EFI/freebsd/${dst}.efi" ] && [ "$kbfree" -gt "$((loadersize * 2))" ]; then
152
cp "${mntpt}/EFI/freebsd/${dst}.efi" "${mntpt}/EFI/freebsd/${dst}-old.efi"
153
fi
154
155
echo "Copying loader to /EFI/freebsd on ESP"
156
cp "${file}" "${mntpt}/EFI/freebsd/${dst}.efi"
157
158
# efibootmgr won't work on systems with ia32 UEFI firmware
159
# since we only use it to boot the 64-bit kernel
160
if [ -n "${updatesystem}" ] && [ ${efibootname} != "bootia32" ]; then
161
existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}//EFI/freebsd/${dst}.efi")
162
163
if [ -z "$existingbootentryloaderfile" ]; then
164
# Try again without the double forward-slash in the path
165
existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}/EFI/freebsd/${dst}.efi")
166
fi
167
168
if [ -z "$existingbootentryloaderfile" ]; then
169
echo "Creating UEFI boot entry for FreeBSD"
170
efibootmgr --create --label FreeBSD --loader "${mntpt}/EFI/freebsd/${dst}.efi" > /dev/null
171
if [ $? -ne 0 ]; then
172
die "Failed to create new boot entry"
173
fi
174
175
# When creating new entries, efibootmgr doesn't mark them active, so we need to
176
# do so. It doesn't make it easy to find which entry it just added, so rely on
177
# the fact that it places the new entry first in BootOrder.
178
bootorder=$(efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex | head -1)
179
bootentry=$(echo "${bootorder}" | cut -w -f 3)$(echo "${bootorder}" | cut -w -f 2)
180
echo "Marking UEFI boot entry ${bootentry} active"
181
efibootmgr --activate "${bootentry}" > /dev/null
182
else
183
echo "Existing UEFI FreeBSD boot entry found: not creating a new one"
184
fi
185
else
186
# Configure for booting from removable media
187
if [ ! -d "${mntpt}/EFI/BOOT" ]; then
188
mkdir -p "${mntpt}/EFI/BOOT"
189
fi
190
cp "${file}" "${mntpt}/EFI/BOOT/${efibootname}.efi"
191
fi
192
193
umount "${mntpt}"
194
rmdir "${mntpt}"
195
echo "Finished updating ESP"
196
}
197
198
make_esp() {
199
local file loaderfile
200
201
file=$1
202
loaderfile=$2
203
204
if [ -f "$file" ]; then
205
make_esp_file ${file} ${fat32min} ${loaderfile}
206
else
207
make_esp_device ${file} ${loaderfile}
208
fi
209
}
210
211
make_esp_mbr() {
212
dev=$1
213
dst=$2
214
215
s=$(find_part $dev "!239")
216
if [ -z "$s" ] ; then
217
s=$(find_part $dev "efi")
218
if [ -z "$s" ] ; then
219
die "No ESP slice found"
220
fi
221
fi
222
make_esp /dev/${dev}s${s} ${dst}/boot/loader.efi
223
}
224
225
make_esp_gpt() {
226
dev=$1
227
dst=$2
228
229
idx=$(find_part $dev "efi")
230
if [ -z "$idx" ] ; then
231
die "No ESP partition found"
232
fi
233
make_esp /dev/${dev}p${idx} ${dst}/boot/loader.efi
234
}
235
236
boot_nogeli_gpt_ufs_legacy() {
237
dev=$1
238
dst=$2
239
240
idx=$(find_part $dev "freebsd-boot")
241
if [ -z "$idx" ] ; then
242
die "No freebsd-boot partition found"
243
fi
244
doit gpart bootcode -b ${gpt0} -p ${gpt2} -i $idx $dev
245
}
246
247
boot_nogeli_gpt_ufs_uefi() {
248
make_esp_gpt $1 $2
249
}
250
251
boot_nogeli_gpt_ufs_both() {
252
boot_nogeli_gpt_ufs_legacy $1 $2 $3
253
boot_nogeli_gpt_ufs_uefi $1 $2 $3
254
}
255
256
boot_nogeli_gpt_zfs_legacy() {
257
dev=$1
258
dst=$2
259
260
idx=$(find_part $dev "freebsd-boot")
261
if [ -z "$idx" ] ; then
262
die "No freebsd-boot partition found"
263
fi
264
doit gpart bootcode -b ${gpt0} -p ${gptzfs2} -i $idx $dev
265
}
266
267
boot_nogeli_gpt_zfs_uefi() {
268
make_esp_gpt $1 $2
269
}
270
271
boot_nogeli_gpt_zfs_both() {
272
boot_nogeli_gpt_zfs_legacy $1 $2 $3
273
boot_nogeli_gpt_zfs_uefi $1 $2 $3
274
}
275
276
boot_nogeli_mbr_ufs_legacy() {
277
dev=$1
278
dst=$2
279
280
doit gpart bootcode -b ${mbr0} ${dev}
281
s=$(find_part $dev "freebsd")
282
if [ -z "$s" ] ; then
283
die "No freebsd slice found"
284
fi
285
doit gpart bootcode -p ${mbr2} ${dev}s${s}
286
}
287
288
boot_nogeli_mbr_ufs_uefi() {
289
make_esp_mbr $1 $2
290
}
291
292
boot_nogeli_mbr_ufs_both() {
293
boot_nogeli_mbr_ufs_legacy $1 $2 $3
294
boot_nogeli_mbr_ufs_uefi $1 $2 $3
295
}
296
297
# ZFS+MBR+BIOS is not a supported configuration
298
boot_nogeli_mbr_zfs_legacy() {
299
exit 1
300
}
301
302
boot_nogeli_mbr_zfs_uefi() {
303
make_esp_mbr $1 $2
304
}
305
306
boot_nogeli_mbr_zfs_both() {
307
boot_nogeli_mbr_zfs_uefi $1 $2 $3
308
}
309
310
boot_geli_gpt_ufs_legacy() {
311
boot_nogeli_gpt_ufs_legacy $1 $2 $3
312
}
313
314
boot_geli_gpt_ufs_uefi() {
315
boot_nogeli_gpt_ufs_uefi $1 $2 $3
316
}
317
318
boot_geli_gpt_ufs_both() {
319
boot_nogeli_gpt_ufs_both $1 $2 $3
320
}
321
322
boot_geli_gpt_zfs_legacy() {
323
boot_nogeli_gpt_zfs_legacy $1 $2 $3
324
}
325
326
boot_geli_gpt_zfs_uefi() {
327
boot_nogeli_gpt_zfs_uefi $1 $2 $3
328
}
329
330
boot_geli_gpt_zfs_both() {
331
boot_nogeli_gpt_zfs_both $1 $2 $3
332
}
333
334
# GELI+MBR is not a valid configuration
335
boot_geli_mbr_ufs_legacy() {
336
exit 1
337
}
338
339
boot_geli_mbr_ufs_uefi() {
340
exit 1
341
}
342
343
boot_geli_mbr_ufs_both() {
344
exit 1
345
}
346
347
boot_geli_mbr_zfs_legacy() {
348
exit 1
349
}
350
351
boot_geli_mbr_zfs_uefi() {
352
exit 1
353
}
354
355
boot_geli_mbr_zfs_both() {
356
exit 1
357
}
358
359
usage() {
360
printf 'Usage: %s -b bios [-d destdir] -f fs [-g geli] [-h] [-o optargs] -s scheme <bootdev>\n' "$0"
361
printf 'Options:\n'
362
printf ' bootdev device to install the boot code on\n'
363
printf ' -b bios bios type: legacy, uefi or both\n'
364
printf ' -d destdir destination filesystem root\n'
365
printf ' -f fs filesystem type: ufs or zfs\n'
366
printf ' -g geli yes or no\n'
367
printf ' -h this help/usage text\n'
368
printf ' -u Run commands such as efibootmgr to update the\n'
369
printf ' currently running system\n'
370
printf ' -o optargs optional arguments\n'
371
printf ' -s scheme mbr or gpt\n'
372
exit 0
373
}
374
375
srcroot=/
376
377
# Note: we really don't support geli boot in this script yet.
378
geli=nogeli
379
380
while getopts "b:d:f:g:ho:s:u" opt; do
381
case "$opt" in
382
b)
383
bios=${OPTARG}
384
;;
385
d)
386
srcroot=${OPTARG}
387
;;
388
f)
389
fs=${OPTARG}
390
;;
391
g)
392
case ${OPTARG} in
393
[Yy][Ee][Ss]|geli) geli=geli ;;
394
*) geli=nogeli ;;
395
esac
396
;;
397
u)
398
updatesystem=1
399
;;
400
o)
401
opts=${OPTARG}
402
;;
403
s)
404
scheme=${OPTARG}
405
;;
406
407
?|h)
408
usage
409
;;
410
esac
411
done
412
413
if [ -n "${scheme}" ] && [ -n "${fs}" ] && [ -n "${bios}" ]; then
414
shift $((OPTIND-1))
415
dev=$1
416
fi
417
418
# For gpt, we need to install pmbr as the primary boot loader
419
# it knows about
420
gpt0=${srcroot}/boot/pmbr
421
gpt2=${srcroot}/boot/gptboot
422
gptzfs2=${srcroot}/boot/gptzfsboot
423
424
# For MBR, we have lots of choices, but select mbr, boot0 has issues with UEFI
425
mbr0=${srcroot}/boot/mbr
426
mbr2=${srcroot}/boot/boot
427
428
# sanity check here
429
430
# Check if we've been given arguments. If not, this script is probably being
431
# sourced, so we shouldn't run anything.
432
if [ -n "${dev}" ]; then
433
eval boot_${geli}_${scheme}_${fs}_${bios} $dev $srcroot $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}"
434
fi
435
436