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