Index: head/release/amd64/make-memstick.sh =================================================================== --- head/release/amd64/make-memstick.sh (revision 342282) +++ head/release/amd64/make-memstick.sh (revision 342283) @@ -1,46 +1,54 @@ #!/bin/sh # # This script generates a "memstick image" (image that can be copied to a # USB memory stick) from a directory tree. Note that the script does not # clean up after itself very well for error conditions on purpose so the # problem can be diagnosed (full filesystem most likely but ...). # # Usage: make-memstick.sh # # $FreeBSD$ # set -e +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH if [ $# -ne 2 ]; then echo "make-memstick.sh /path/to/directory /path/to/image/file" exit 1 fi if [ ! -d ${1} ]; then echo "${1} must be a directory" exit 1 fi if [ -e ${2} ]; then echo "won't overwrite ${2}" exit 1 fi echo '/dev/ufs/FreeBSD_Install / ufs ro,noatime 1 1' > ${1}/etc/fstab echo 'root_rw_mount="NO"' > ${1}/etc/rc.conf.local makefs -B little -o label=FreeBSD_Install -o version=2 ${2}.part ${1} rm ${1}/etc/fstab rm ${1}/etc/rc.conf.local +# Make an ESP in a file. +espfilename=$(mktemp /tmp/efiboot.XXXXXX) +make_esp_file ${espfilename} ${fat32min} ${1}/boot/loader.efi + mkimg -s mbr \ -b ${1}/boot/mbr \ - -p efi:=${1}/boot/boot1.efifat \ + -p efi:=${espfilename} \ -p freebsd:-"mkimg -s bsd -b ${1}/boot/boot -p freebsd-ufs:=${2}.part" \ -a 2 \ -o ${2} +rm ${espfilename} rm ${2}.part Index: head/release/amd64/mkisoimages.sh =================================================================== --- head/release/amd64/mkisoimages.sh (revision 342282) +++ head/release/amd64/mkisoimages.sh (revision 342283) @@ -1,102 +1,99 @@ #!/bin/sh # # Module: mkisoimages.sh # Author: Jordan K Hubbard # Date: 22 June 2001 # # $FreeBSD$ # # This script is used by release/Makefile to build the (optional) ISO images # for a FreeBSD release. It is considered architecture dependent since each # platform has a slightly unique way of making bootable CDs. This script # is also allowed to generate any number of images since that is more of # publishing decision than anything else. # # Usage: # # mkisoimages.sh [-b] image-label image-name base-bits-dir [extra-bits-dir] # # Where -b is passed if the ISO image should be made "bootable" by # whatever standards this architecture supports (may be unsupported), # image-label is the ISO image label, image-name is the filename of the # resulting ISO image, base-bits-dir contains the image contents and # extra-bits-dir, if provided, contains additional files to be merged # into base-bits-dir as part of making the image. set -e +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + if [ -z $ETDUMP ]; then ETDUMP=etdump fi if [ -z $MAKEFS ]; then MAKEFS=makefs fi if [ -z $MKIMG ]; then MKIMG=mkimg fi if [ "$1" = "-b" ]; then BASEBITSDIR="$4" # This is highly x86-centric and will be used directly below. bootable="-o bootimage=i386;$BASEBITSDIR/boot/cdboot -o no-emul-boot" # Make EFI system partition (should be done with makefs in the future) - dd if=/dev/zero of=efiboot.img bs=4k count=200 - device=`mdconfig -a -t vnode -f efiboot.img` - newfs_msdos -F 12 -m 0xf8 /dev/$device - mkdir efi - mount -t msdosfs /dev/$device efi - mkdir -p efi/efi/boot - cp "$BASEBITSDIR/boot/loader.efi" efi/efi/boot/bootx64.efi - umount efi - rmdir efi - mdconfig -d -u $device - bootable="$bootable -o bootimage=i386;efiboot.img -o no-emul-boot -o platformid=efi" - + # The ISO file is a special case, in that it only has a maximum of + # 800 KB available for the boot code. So make an 800 KB ESP + espfilename=$(mktemp /tmp/efiboot.XXXXXX) + make_esp_file ${espfilename} 800 ${BASEBITSDIR}/boot/loader.efi + bootable="$bootable -o bootimage=i386;${espfilename} -o no-emul-boot -o platformid=efi" + shift else BASEBITSDIR="$3" bootable="" fi if [ $# -lt 3 ]; then echo "Usage: $0 [-b] image-label image-name base-bits-dir [extra-bits-dir]" exit 1 fi LABEL=`echo "$1" | tr '[:lower:]' '[:upper:]'`; shift NAME="$1"; shift publisher="The FreeBSD Project. https://www.FreeBSD.org/" echo "/dev/iso9660/$LABEL / cd9660 ro 0 0" > "$BASEBITSDIR/etc/fstab" $MAKEFS -t cd9660 $bootable -o rockridge -o label="$LABEL" -o publisher="$publisher" "$NAME" "$@" rm -f "$BASEBITSDIR/etc/fstab" -rm -f efiboot.img +rm -f ${espfilename} if [ "$bootable" != "" ]; then # Look for the EFI System Partition image we dropped in the ISO image. for entry in `$ETDUMP --format shell $NAME`; do eval $entry if [ "$et_platform" = "efi" ]; then espstart=`expr $et_lba \* 2048` espsize=`expr $et_sectors \* 512` espparam="-p efi::$espsize:$espstart" break fi done # Create a GPT image containing the partitions we need for hybrid boot. imgsize=`stat -f %z "$NAME"` $MKIMG -s gpt \ --capacity $imgsize \ -b "$BASEBITSDIR/boot/pmbr" \ $espparam \ -p freebsd-boot:="$BASEBITSDIR/boot/isoboot" \ -o hybrid.img # Drop the PMBR, GPT, and boot code into the System Area of the ISO. dd if=hybrid.img of="$NAME" bs=32k count=1 conv=notrunc rm -f hybrid.img fi Index: head/release/arm64/make-memstick.sh =================================================================== --- head/release/arm64/make-memstick.sh (revision 342282) +++ head/release/arm64/make-memstick.sh (revision 342283) @@ -1,44 +1,52 @@ #!/bin/sh # # This script generates a "memstick image" (image that can be copied to a # USB memory stick) from a directory tree. Note that the script does not # clean up after itself very well for error conditions on purpose so the # problem can be diagnosed (full filesystem most likely but ...). # # Usage: make-memstick.sh # # $FreeBSD$ # set -e PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + if [ $# -ne 2 ]; then echo "make-memstick.sh /path/to/directory /path/to/image/file" exit 1 fi if [ ! -d ${1} ]; then echo "${1} must be a directory" exit 1 fi if [ -e ${2} ]; then echo "won't overwrite ${2}" exit 1 fi echo '/dev/ufs/FreeBSD_Install / ufs ro,noatime 1 1' > ${1}/etc/fstab echo 'root_rw_mount="NO"' > ${1}/etc/rc.conf.local makefs -B little -o label=FreeBSD_Install -o version=2 ${2}.part ${1} rm ${1}/etc/fstab rm ${1}/etc/rc.conf.local +# Make an ESP in a file. +espfilename=$(mktemp /tmp/efiboot.XXXXXX) +make_esp_file ${espfilename} ${fat32min} ${1}/boot/loader.efi + mkimg -s gpt \ - -p efi:=${1}/boot/boot1.efifat \ + -p efi:=${espfilename} \ -p freebsd:=${2}.part \ -o ${2} +rm ${espfilename} rm ${2}.part Index: head/release/i386/make-memstick.sh =================================================================== --- head/release/i386/make-memstick.sh (revision 342282) +++ head/release/i386/make-memstick.sh (revision 342283) @@ -1,44 +1,53 @@ #!/bin/sh # # This script generates a "memstick image" (image that can be copied to a # USB memory stick) from a directory tree. Note that the script does not # clean up after itself very well for error conditions on purpose so the # problem can be diagnosed (full filesystem most likely but ...). # # Usage: make-memstick.sh # # $FreeBSD$ # set -e +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH if [ $# -ne 2 ]; then echo "make-memstick.sh /path/to/directory /path/to/image/file" exit 1 fi if [ ! -d ${1} ]; then echo "${1} must be a directory" exit 1 fi if [ -e ${2} ]; then echo "won't overwrite ${2}" exit 1 fi echo '/dev/ufs/FreeBSD_Install / ufs ro,noatime 1 1' > ${1}/etc/fstab echo 'root_rw_mount="NO"' > ${1}/etc/rc.conf.local makefs -B little -o label=FreeBSD_Install -o version=2 ${2}.part ${1} rm ${1}/etc/fstab rm ${1}/etc/rc.conf.local +# Make an ESP in a file. +espfilename=$(mktemp /tmp/efiboot.XXXXXX) +make_esp_file ${espfilename} ${fat32min} ${1}/boot/loader.efi + mkimg -s mbr \ -b ${1}/boot/mbr \ + -p efi:=${espfilename} -p freebsd:-"mkimg -s bsd -b ${1}/boot/boot -p freebsd-ufs:=${2}.part" \ -o ${2} +rm ${espfilename} rm ${2}.part Index: head/release/tools/vmimage.subr =================================================================== --- head/release/tools/vmimage.subr (revision 342282) +++ head/release/tools/vmimage.subr (revision 342283) @@ -1,250 +1,257 @@ #!/bin/sh # # $FreeBSD$ # # # Common functions for virtual machine image build scripts. # +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + export PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" trap "cleanup" INT QUIT TRAP ABRT TERM write_partition_layout() { if [ -z "${NOSWAP}" ]; then SWAPOPT="-p freebsd-swap/swapfs::${SWAPSIZE}" fi BOOTFILES="$(env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ WITH_UNIFIED_OBJDIR=yes \ make -C ${WORLDDIR}/stand -V .OBJDIR)" BOOTFILES="$(realpath ${BOOTFILES})" case "${TARGET}:${TARGET_ARCH}" in amd64:amd64 | i386:i386) mkimg -s gpt -f ${VMFORMAT} \ -b ${BOOTFILES}/i386/pmbr/pmbr \ -p freebsd-boot/bootfs:=${BOOTFILES}/i386/gptboot/gptboot \ ${SWAPOPT} \ -p freebsd-ufs/rootfs:=${VMBASE} \ -o ${VMIMAGE} ;; arm64:aarch64) + # Create an ESP + espfilename=$(mktemp /tmp/efiboot.XXXXXX) + make_esp_file ${espfilename} ${fat32min} ${BOOTFILES}/efi/loader_lua/loader_lua.efi mkimg -s mbr -f ${VMFORMAT} \ - -p efi:=${BOOTFILES}/efi/boot1/boot1.efifat \ + -p efi:=${espfilename} \ -p freebsd:=${VMBASE} \ -o ${VMIMAGE} + rm ${espfilename} ;; powerpc:powerpc*) mkimg -s apm -f ${VMFORMAT} \ -p apple-boot/bootfs:=${BOOTFILES}/powerpc/boot1.chrp/boot1.hfs \ ${SWAPOPT} \ -p freebsd-ufs/rootfs:=${VMBASE} \ -o ${VMIMAGE} ;; *) # ENOTSUPP return 1 ;; esac return 0 } err() { printf "${@}\n" cleanup return 1 } cleanup() { if [ -c "${DESTDIR}/dev/null" ]; then umount_loop ${DESTDIR}/dev 2>/dev/null fi umount_loop ${DESTDIR} if [ ! -z "${mddev}" ]; then mdconfig -d -u ${mddev} fi return 0 } vm_create_base() { # Creates the UFS root filesystem for the virtual machine disk, # written to the formatted disk image with mkimg(1). mkdir -p ${DESTDIR} truncate -s ${VMSIZE} ${VMBASE} mddev=$(mdconfig -f ${VMBASE}) newfs -L rootfs /dev/${mddev} mount /dev/${mddev} ${DESTDIR} return 0 } vm_copy_base() { # Creates a new UFS root filesystem and copies the contents of the # current root filesystem into it. This produces a "clean" disk # image without any remnants of files which were created temporarily # during image-creation and have since been deleted (e.g., downloaded # package archives). mkdir -p ${DESTDIR}/old mdold=$(mdconfig -f ${VMBASE}) mount /dev/${mdold} ${DESTDIR}/old truncate -s ${VMSIZE} ${VMBASE}.tmp mkdir -p ${DESTDIR}/new mdnew=$(mdconfig -f ${VMBASE}.tmp) newfs -L rootfs /dev/${mdnew} mount /dev/${mdnew} ${DESTDIR}/new tar -cf- -C ${DESTDIR}/old . | tar -xUf- -C ${DESTDIR}/new umount_loop /dev/${mdold} rmdir ${DESTDIR}/old mdconfig -d -u ${mdold} umount_loop /dev/${mdnew} rmdir ${DESTDIR}/new tunefs -n enable /dev/${mdnew} mdconfig -d -u ${mdnew} mv ${VMBASE}.tmp ${VMBASE} } vm_install_base() { # Installs the FreeBSD userland/kernel to the virtual machine disk. cd ${WORLDDIR} && \ make DESTDIR=${DESTDIR} \ installworld installkernel distribution || \ err "\n\nCannot install the base system to ${DESTDIR}." # Bootstrap etcupdate(8) and mergemaster(8) databases. mkdir -p ${DESTDIR}/var/db/etcupdate etcupdate extract -B \ -M "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \ -s ${WORLDDIR} -d ${DESTDIR}/var/db/etcupdate sh ${WORLDDIR}/release/scripts/mm-mtree.sh -m ${WORLDDIR} \ -F "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \ -D ${DESTDIR} echo '# Custom /etc/fstab for FreeBSD VM images' \ > ${DESTDIR}/etc/fstab echo "/dev/${ROOTLABEL}/rootfs / ufs rw 1 1" \ >> ${DESTDIR}/etc/fstab if [ -z "${NOSWAP}" ]; then echo '/dev/gpt/swapfs none swap sw 0 0' \ >> ${DESTDIR}/etc/fstab fi local hostname hostname="$(echo $(uname -o) | tr '[:upper:]' '[:lower:]')" echo "hostname=\"${hostname}\"" >> ${DESTDIR}/etc/rc.conf mkdir -p ${DESTDIR}/dev mount -t devfs devfs ${DESTDIR}/dev chroot ${DESTDIR} /usr/bin/newaliases chroot ${DESTDIR} /etc/rc.d/ldconfig forcestart umount_loop ${DESTDIR}/dev cp /etc/resolv.conf ${DESTDIR}/etc/resolv.conf return 0 } vm_extra_install_base() { # Prototype. When overridden, runs extra post-installworld commands # as needed, based on the target virtual machine image or cloud # provider image target. return 0 } vm_extra_enable_services() { if [ ! -z "${VM_RC_LIST}" ]; then for _rcvar in ${VM_RC_LIST}; do echo ${_rcvar}_enable="YES" >> ${DESTDIR}/etc/rc.conf done fi if [ -z "${VMCONFIG}" -o -c "${VMCONFIG}" ]; then echo 'ifconfig_DEFAULT="DHCP inet6 accept_rtadv"' >> \ ${DESTDIR}/etc/rc.conf fi return 0 } vm_extra_install_packages() { if [ -z "${VM_EXTRA_PACKAGES}" ]; then return 0 fi mkdir -p ${DESTDIR}/dev mount -t devfs devfs ${DESTDIR}/dev chroot ${DESTDIR} env ASSUME_ALWAYS_YES=yes \ /usr/sbin/pkg bootstrap -y chroot ${DESTDIR} env ASSUME_ALWAYS_YES=yes \ /usr/sbin/pkg install -y ${VM_EXTRA_PACKAGES} umount_loop ${DESTDIR}/dev return 0 } vm_extra_install_ports() { # Prototype. When overridden, installs additional ports within the # virtual machine environment. return 0 } vm_extra_pre_umount() { # Prototype. When overridden, performs additional tasks within the # virtual machine environment prior to unmounting the filesystem. # Note: When overriding this function, removing resolv.conf in the # disk image must be included. rm -f ${DESTDIR}/etc/resolv.conf return 0 } vm_extra_pkg_rmcache() { if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then chroot ${DESTDIR} env ASSUME_ALWAYS_YES=yes \ /usr/local/sbin/pkg clean -y -a fi return 0 } umount_loop() { DIR=$1 i=0 sync while ! umount ${DIR}; do i=$(( $i + 1 )) if [ $i -ge 10 ]; then # This should never happen. But, it has happened. echo "Cannot umount(8) ${DIR}" echo "Something has gone horribly wrong." return 1 fi sleep 1 done return 0 } vm_create_disk() { echo "Creating image... Please wait." echo write_partition_layout || return 1 return 0 } vm_extra_create_disk() { return 0 } Index: head/share/man/man8/uefi.8 =================================================================== --- head/share/man/man8/uefi.8 (revision 342282) +++ head/share/man/man8/uefi.8 (revision 342283) @@ -1,167 +1,148 @@ .\" Copyright (c) 2014 The FreeBSD Foundation .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd January 25, 2018 +.Dd December 14, 2018 .Dt UEFI 8 .Os .Sh NAME .Nm UEFI .Nd Unified Extensible Firmware Interface bootstrapping procedures .Sh DESCRIPTION The .Nm Unified Extensible Firmware Interface provides boot- and run-time services to operating systems. .Nm is a replacement for the legacy BIOS on the i386 and amd64 CPU architectures, and is also used on arm, arm64 and ia64. .Pp The UEFI specification is the successor to the Extensible Firmware Interface (EFI) specification. The terms are often used interchangeably. .Pp The .Nm boot process loads system bootstrap code located in an EFI System Partition (ESP). The ESP is a GPT or MBR partition with a specific identifier that contains an .Xr msdosfs 5 FAT file system with a specified file hierarchy. .Bl -column -offset indent ".Sy Partition Scheme" ".Sy ESP Identifier" .It Sy "Partition Scheme" Ta Sy "ESP Identifier" .It GPT Ta C12A7328-F81F-11D2-BA4B-00A0C93EC93B .It MBR Ta 0xEF .El .Pp The .Nm boot process proceeds as follows: .Bl -enum -offset indent -compact .It .Nm firmware runs at power up and searches for an OS loader in the EFI system partition. The path to the loader may be set by an EFI environment variable. If not set, an architecture-specific default is used. .Bl -column -offset indent "Architecture" "Default Path" .It Sy Architecture Ta Sy Default Path .It amd64 Ta Pa /EFI/BOOT/BOOTX64.EFI .It arm64 Ta Pa /EFI/BOOT/BOOTAA64.EFI .El .Pp The default .Nm boot configuration for .Fx installs -.Pa boot1.efi +.Pa loader.efi in the default path. .It -.Pa boot1.efi +.Pa loader.efi reads boot configuration from .Pa /boot.config or .Pa /boot/config . -Unlike other first-stage boot loaders, -.Pa boot1.efi -passes the configuration to the next stage boot loader and does not -itself act on the contents of the file. .It -.Pa boot1.efi +.Pa loader.efi searches partitions of type .Li freebsd-ufs and .Li freebsd-zfs for .Pa loader.efi . The search begins with partitions on the device from which -.Pa boot1.efi +.Pa loader.efi was loaded, and continues with other available partitions. If both .Li freebsd-ufs and .Li freebsd-zfs partitions exist on the same device the .Li freebsd-zfs partition is preferred. -.Pa boot1.efi -then loads and executes -.Pa loader.efi . .It .Pa loader.efi loads and boots the kernel, as described in .Xr loader 8 . .El .Pp The .Xr vt 4 system console is automatically selected when booting via .Nm . .Sh FILES .Bl -tag -width /boot/loader -compact -.It Pa /boot/boot1.efi -First stage .Nm bootstrap -.It Pa /boot/boot1.efifat -.Xr msdosfs 5 -FAT file system image containing -.Pa boot1.efi -for use by -.Xr bsdinstall 8 -and the -.Ar bootcode -argument to -.Xr gpart 8 . .It Pa /boot/loader.efi Final stage bootstrap .It Pa /boot/kernel/kernel default kernel .It Pa /boot/kernel.old/kernel typical non-default kernel (optional) .El .Sh SEE ALSO .Xr vt 4 , .Xr boot.config 5 , .Xr msdosfs 5 , .Xr boot 8 , .Xr efibootmgr 8 , .Xr efidp 8 , .Xr efivar 8 , .Xr gpart 8 , .Xr uefisign 8 .Sh HISTORY EFI boot support for the ia64 architecture first appeared in .Fx 5.0 . .Nm boot support for amd64 first appeared in .Fx 10.1 and for arm64 in .Fx 11.0 . .Sh CAVEATS EFI environment variables are not supported by .Xr loader 8 or the kernel. Index: head/tools/boot/install-boot.sh =================================================================== --- head/tools/boot/install-boot.sh (revision 342282) +++ head/tools/boot/install-boot.sh (revision 342283) @@ -1,270 +1,429 @@ #!/bin/sh # $FreeBSD$ # # Installs/updates the necessary boot blocks for the desired boot environment # # Lightly tested.. Intended to be installed, but until it matures, it will just # be a boot tool for regression testing. # insert code here to guess what you have -- yikes! +# Minimum size of FAT filesystems, in KB. +fat32min=33292 +fat16min=2100 + die() { echo $* exit 1 } doit() { echo $* eval $* } find-part() { dev=$1 part=$2 gpart show $dev | tail +2 | awk '$4 == "'$part'" { print $3; }' } -make_esp() { - local dev dst mntpt +get_uefi_bootname() { + case ${TARGET:-$(uname -m)} in + amd64) echo bootx64 ;; + arm64) echo bootaa64 ;; + i386) echo bootia32 ;; + arm) echo bootarm ;; + *) die "machine type $(uname -m) doesn't support UEFI" ;; + esac +} + +make_esp_file() { + local file sizekb loader device mntpt fatbits efibootname + + file=$1 + sizekb=$2 + loader=$3 + + if [ "$sizekb" -ge "$fat32min" ]; then + fatbits=32 + elif [ "$sizekb" -ge "$fat16min" ]; then + fatbits=16 + else + fatbits=12 + fi + + dd if=/dev/zero of="${file}" bs=1k count="${sizekb}" + device=$(mdconfig -a -t vnode -f "${file}") + newfs_msdos -F "${fatbits}" -c 1 -L EFISYS "/dev/${device}" > /dev/null 2>&1 + mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) + mount -t msdosfs "/dev/${device}" "${mntpt}" + mkdir -p "${mntpt}/EFI/BOOT" + efibootname=$(get_uefi_bootname) + cp "${loader}" "${mntpt}/EFI/BOOT/${efibootname}.efi" + umount "${mntpt}" + rmdir "${mntpt}" + mdconfig -d -u "${device}" +} + +make_esp_device() { + local dev file mntpt fstype efibootname kbfree loadersize efibootfile + local isboot1 existingbootentryloaderfile bootorder bootentry + + # ESP device node dev=$1 - dst=$2 + file=$2 - newfs_msdos -a 32 ${dev} mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) - mount -t msdos ${dev} ${mntpt} - mkdir -p ${mntpt}/efi/boot - cp ${dst}/boot/loader.efi ${mntpt}/efi/boot/bootx64.efi - umount ${mntpt} - rmdir ${mntpt} + + # See if we're using an existing (formatted) ESP + fstype=$(fstyp "${dev}") + + if [ "${fstype}" != "msdosfs" ]; then + newfs_msdos -F 32 -c 1 -L EFISYS "${dev}" > /dev/null 2>&1 + fi + + mount -t msdosfs "${dev}" "${mntpt}" + if [ $? -ne 0 ]; then + die "Failed to mount ${dev} as an msdosfs filesystem" + fi + + echo "Mounted ESP ${dev} on ${mntpt}" + + efibootname=$(get_uefi_bootname) + kbfree=$(df -k "${mntpt}" | tail -1 | cut -w -f 4) + loadersize=$(stat -f %z "${file}") + loadersize=$((loadersize / 1024)) + + # Check if /EFI/BOOT/BOOTxx.EFI is the FreeBSD boot1.efi + # If it is, remove it to avoid leaving stale files around + efibootfile="${mntpt}/EFI/BOOT/${efibootname}.efi" + if [ -f "${efibootfile}" ]; then + isboot1=$(strings "${efibootfile}" | grep "FreeBSD EFI boot block") + + if [ -n "${isboot1}" ] && [ "$kbfree" -lt "${loadersize}" ]; then + echo "Only ${kbfree}KB space remaining: removing old FreeBSD boot1.efi file /EFI/BOOT/${efibootname}.efi" + rm "${efibootfile}" + rmdir "${mntpt}/EFI/BOOT" + else + echo "${kbfree}KB space remaining on ESP: renaming old boot1.efi file /EFI/BOOT/${efibootname}.efi /EFI/BOOT/${efibootname}-old.efi" + mv "${efibootfile}" "${mntpt}/EFI/BOOT/${efibootname}-old.efi" + fi + fi + + if [ ! -f "${mntpt}/EFI/freebsd/loader.efi" ] && [ "$kbfree" -lt "$loadersize" ]; then + umount "${mntpt}" + rmdir "${mntpt}" + echo "Failed to update the EFI System Partition ${dev}" + echo "Insufficient space remaining for ${file}" + echo "Run e.g \"mount -t msdosfs ${dev} /mnt\" to inspect it for files that can be removed." + die + fi + + mkdir -p "${mntpt}/EFI/freebsd" + + # Keep a copy of the existing loader.efi in case there's a problem with the new one + if [ -f "${mntpt}/EFI/freebsd/loader.efi" ] && [ "$kbfree" -gt "$((loadersize * 2))" ]; then + cp "${mntpt}/EFI/freebsd/loader.efi" "${mntpt}/EFI/freebsd/loader-old.efi" + fi + + echo "Copying loader to /EFI/freebsd on ESP" + cp "${file}" "${mntpt}/EFI/freebsd/loader.efi" + + existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}//EFI/freebsd/loader.efi") + + if [ -z "$existingbootentryloaderfile" ]; then + # Try again without the double forward-slash in the path + existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}/EFI/freebsd/loader.efi") + fi + + if [ -z "$existingbootentryloaderfile" ]; then + echo "Creating UEFI boot entry for FreeBSD" + efibootmgr --create --label FreeBSD --loader "${mntpt}/EFI/freebsd/loader.efi" > /dev/null + if [ $? -ne 0 ]; then + die "Failed to create new boot entry" + fi + + # When creating new entries, efibootmgr doesn't mark them active, so we need to + # do so. It doesn't make it easy to find which entry it just added, so rely on + # the fact that it places the new entry first in BootOrder. + bootorder=$(efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex | head -1) + bootentry=$(echo "${bootorder}" | cut -w -f 3)$(echo "${bootorder}" | cut -w -f 2) + echo "Marking UEFI boot entry ${bootentry} active" + efibootmgr --activate "${bootentry}" > /dev/null + else + echo "Existing UEFI FreeBSD boot entry found: not creating a new one" + fi + + umount "${mntpt}" + rmdir "${mntpt}" + echo "Finished updating ESP" } +make_esp() { + local file loaderfile + + file=$1 + loaderfile=$2 + + if [ -f "$file" ]; then + make_esp_file ${file} ${fat32min} ${loaderfile} + else + make_esp_device ${file} ${loaderfile} + fi +} + make_esp_mbr() { dev=$1 dst=$2 s=$(find-part $dev "!239") if [ -z "$s" ] ; then s=$(find-part $dev "efi") if [ -z "$s" ] ; then die "No ESP slice found" fi fi - make_esp /dev/${dev}s${s} ${dst} + make_esp /dev/${dev}s${s} ${dst}/boot/loader.efi } make_esp_gpt() { dev=$1 dst=$2 idx=$(find-part $dev "efi") if [ -z "$idx" ] ; then die "No ESP partition found" fi - make_esp /dev/${dev}p${idx} ${dst} + make_esp /dev/${dev}p${idx} ${dst}/boot/loader.efi } boot_nogeli_gpt_ufs_legacy() { dev=$1 dst=$2 idx=$(find-part $dev "freebsd-boot") if [ -z "$idx" ] ; then die "No freebsd-boot partition found" fi doit gpart bootcode -b ${gpt0} -p ${gpt2} -i $idx $dev } boot_nogeli_gpt_ufs_uefi() { make_esp_gpt $1 $2 } boot_nogeli_gpt_ufs_both() { boot_nogeli_gpt_ufs_legacy $1 $2 $3 boot_nogeli_gpt_ufs_uefi $1 $2 $3 } boot_nogeli_gpt_zfs_legacy() { dev=$1 dst=$2 idx=$(find-part $dev "freebsd-boot") if [ -z "$idx" ] ; then die "No freebsd-boot partition found" fi doit gpart bootcode -b ${gpt0} -p ${gptzfs2} -i $idx $dev } boot_nogeli_gpt_zfs_uefi() { make_esp_gpt $1 $2 } boot_nogeli_gpt_zfs_both() { boot_nogeli_gpt_zfs_legacy $1 $2 $3 boot_nogeli_gpt_zfs_uefi $1 $2 $3 } boot_nogeli_mbr_ufs_legacy() { dev=$1 dst=$2 doit gpart bootcode -b ${mbr0} ${dev} s=$(find-part $dev "freebsd") if [ -z "$s" ] ; then die "No freebsd slice found" fi doit gpart bootcode -p ${mbr2} ${dev}s${s} } boot_nogeli_mbr_ufs_uefi() { make_esp_mbr $1 $2 } boot_nogeli_mbr_ufs_both() { boot_nogeli_mbr_ufs_legacy $1 $2 $3 boot_nogeli_mbr_ufs_uefi $1 $2 $3 } boot_nogeli_mbr_zfs_legacy() { dev=$1 dst=$2 # search to find the BSD slice s=$(find-part $dev "freebsd") if [ -z "$s" ] ; then die "No BSD slice found" fi idx=$(find-part ${dev}s${s} "freebsd-zfs") if [ -z "$idx" ] ; then die "No freebsd-zfs slice found" fi # search to find the freebsd-zfs partition within the slice # Or just assume it is 'a' because it has to be since it fails otherwise doit gpart bootcode -b ${dst}/boot/mbr ${dev} dd if=${dst}/boot/zfsboot of=/tmp/zfsboot1 count=1 doit gpart bootcode -b /tmp/zfsboot1 ${dev}s${s} # Put boot1 into the start of part sysctl kern.geom.debugflags=0x10 # Put boot2 into ZFS boot slot doit dd if=${dst}/boot/zfsboot of=/dev/${dev}s${s}a skip=1 seek=1024 sysctl kern.geom.debugflags=0x0 } boot_nogeli_mbr_zfs_uefi() { make_esp_mbr $1 $2 } boot_nogeli_mbr_zfs_both() { boot_nogeli_mbr_zfs_legacy $1 $2 $3 boot_nogeli_mbr_zfs_uefi $1 $2 $3 } boot_geli_gpt_ufs_legacy() { boot_nogeli_gpt_ufs_legacy $1 $2 $3 } boot_geli_gpt_ufs_uefi() { boot_nogeli_gpt_ufs_uefi $1 $2 $3 } boot_geli_gpt_ufs_both() { boot_nogeli_gpt_ufs_both $1 $2 $3 } boot_geli_gpt_zfs_legacy() { boot_nogeli_gpt_zfs_legacy $1 $2 $3 } boot_geli_gpt_zfs_uefi() { boot_nogeli_gpt_zfs_uefi $1 $2 $3 } boot_geli_gpt_zfs_both() { boot_nogeli_gpt_zfs_both $1 $2 $3 } # GELI+MBR is not a valid configuration boot_geli_mbr_ufs_legacy() { exit 1 } boot_geli_mbr_ufs_uefi() { exit 1 } boot_geli_mbr_ufs_both() { exit 1 } boot_geli_mbr_zfs_legacy() { exit 1 } boot_geli_mbr_zfs_uefi() { exit 1 } boot_geli_mbr_zfs_both() { exit 1 } boot_nogeli_vtoc8_ufs_ofw() { dev=$1 dst=$2 # For non-native builds, ensure that geom_part(4) supports VTOC8. kldload geom_part_vtoc8.ko doit gpart bootcode -p ${vtoc8} ${dev} } -DESTDIR=/ +usage() { + printf 'Usage: %s -b bios [-d destdir] -f fs [-g geli] [-h] [-o optargs] -s scheme \n' "$0" + printf 'Options:\n' + printf ' bootdev device to install the boot code on\n' + printf ' -b bios bios type: legacy, uefi or both\n' + printf ' -d destdir destination filesystem root\n' + printf ' -f fs filesystem type: ufs or zfs\n' + printf ' -g geli yes or no\n' + printf ' -h this help/usage text\n' + printf ' -o optargs optional arguments\n' + printf ' -s scheme mbr or gpt\n' + exit 0 +} +srcroot=/ + # Note: we really don't support geli boot in this script yet. geli=nogeli -while getopts "b:d:f:g:o:s:" opt; do +while getopts "b:d:f:g:ho:s:" opt; do case "$opt" in b) bios=${OPTARG} ;; d) - DESTDIR=${OPTARG} + srcroot=${OPTARG} ;; f) fs=${OPTARG} ;; g) case ${OPTARG} in [Yy][Ee][Ss]|geli) geli=geli ;; *) geli=nogeli ;; esac ;; o) opts=${OPTARG} ;; s) scheme=${OPTARG} ;; + + ?|h) + usage + ;; esac done -shift $((OPTIND-1)) -dev=$1 +if [ -n "${scheme}" ] && [ -n "${fs}" ] && [ -n "${bios}" ]; then + shift $((OPTIND-1)) + dev=$1 +fi # For gpt, we need to install pmbr as the primary boot loader # it knows about -gpt0=${DESTDIR}/boot/pmbr -gpt2=${DESTDIR}/boot/gptboot -gptzfs2=${DESTDIR}/boot/gptzfsboot +gpt0=${srcroot}/boot/pmbr +gpt2=${srcroot}/boot/gptboot +gptzfs2=${srcroot}/boot/gptzfsboot # For MBR, we have lots of choices, but select mbr, boot0 has issues with UEFI -mbr0=${DESTDIR}/boot/mbr -mbr2=${DESTDIR}/boot/boot +mbr0=${srcroot}/boot/mbr +mbr2=${srcroot}/boot/boot # VTOC8 -vtoc8=${DESTDIR}/boot/boot1 +vtoc8=${srcroot}/boot/boot1 # sanity check here -eval boot_${geli}_${scheme}_${fs}_${bios} $dev $DESTDIR $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}" +# Check if we've been given arguments. If not, this script is probably being +# sourced, so we shouldn't run anything. +if [ -n "${dev}" ]; then + eval boot_${geli}_${scheme}_${fs}_${bios} $dev $srcroot $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}" +fi Index: head/tools/boot/rootgen.sh =================================================================== --- head/tools/boot/rootgen.sh (revision 342282) +++ head/tools/boot/rootgen.sh (revision 342283) @@ -1,889 +1,867 @@ #!/bin/sh # $FreeBSD$ passphrase=passphrase iterations=50000 -do_boot1_efi=0 +# The smallest FAT32 filesystem is 33292 KB +espsize=33292 # # Builds all the bat-shit crazy combinations we support booting from, # at least for amd64. It assume you have a ~sane kernel in /boot/kernel # and copies that into the ~150MB root images we create (we create the du # size of the kernel + 20MB # # Sad panda sez: this runs as root, but could be userland if someone # creates userland geli and zfs tools. # -# This assumes an external prograam install-boot.sh which will install +# This assumes an external program install-boot.sh which will install # the appropriate boot files in the appropriate locations. # # These images assume ada0 will be the root image. We should likely # use labels, but we don't. # -# ASsumes you've already rebuilt... maybe bad? Also maybe bad: the env +# Assumes you've already rebuilt... maybe bad? Also maybe bad: the env # vars should likely be conditionally set to allow better automation. # cpsys() { src=$1 dst=$2 # Copy kernel + boot loader (cd $src ; tar cf - .) | (cd $dst; tar xf -) } -make_esp() -{ - local src dst md mntpt - src=$1 - dst=$2 - - if [ "${do_boot1_efi}" -eq 1 ]; then - cp ${src}/boot/boot1.efifat ${dst} - else - dd if=/dev/zero of=${dst} count=1 seek=$((100 * 1024 * 1024 / 512)) - md=$(mdconfig -f ${dst}) - newfs_msdos -a 32 /dev/${md} - mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) - mount -t msdos /dev/${md} ${mntpt} -# mkdir -p ${mntpt}/efi/freebsd # not yet - mkdir -p ${mntpt}/efi/boot - cp ${src}/boot/loader.efi ${mntpt}/efi/boot/bootx64.efi - umount ${mntpt} - rmdir ${mntpt} - mdconfig -d -u ${md} - fi -} - mk_nogeli_gpt_ufs_legacy() { src=$1 img=$2 cat > ${src}/etc/fstab < ${src}/etc/fstab < ${src}/etc/fstab <> ${mntpt}/boot/loader.conf <> ${mntpt}/boot/loader.conf <> ${mntpt}/boot/loader.conf < ${src}/etc/fstab < ${src}/etc/fstab < ${src}/etc/fstab <> ${mntpt}/boot/loader.conf <> ${mntpt}/boot/loader.conf <> ${mntpt}/boot/loader.conf < ${mntpt}/boot/loader.conf < ${mntpt}/etc/fstab < ${mntpt}/boot/loader.conf < ${mntpt}/etc/fstab < ${mntpt}/boot/loader.conf < ${mntpt}/etc/fstab <> ${mntpt}/boot/loader.conf <> ${mntpt}/boot/loader.conf < ${mntpt}/boot/loader.conf < ${src}/etc/fstab < $sh chmod 755 $sh # https://wiki.freebsd.org/arm64/QEMU also has # -device virtio-net-device,netdev=net0 # -netdev user,id=net0 } # Amd64 qemu qemu_amd64_legacy() { img=$1 sh=$2 echo "qemu-system-x86_64 -m 256m --drive file=${img},format=raw ${qser}" > $sh chmod 755 $sh } qemu_amd64_uefi() { img=$1 sh=$2 echo "qemu-system-x86_64 -m 256m -bios ~/bios/OVMF-X64.fd --drive file=${img},format=raw ${qser}" > $sh chmod 755 $sh } qemu_amd64_both() { img=$1 sh=$2 echo "qemu-system-x86_64 -m 256m --drive file=${img},format=raw ${qser}" > $sh echo "qemu-system-x86_64 -m 256m -bios ~/bios/OVMF-X64.fd --drive file=${img},format=raw ${qser}" >> $sh chmod 755 $sh } # arm # nothing listed? # i386 qemu_i386_legacy() { img=$1 sh=$2 echo "qemu-system-i386 --drive file=${img},format=raw ${qser}" > $sh chmod 755 $sh } # Not yet supported qemu_i386_uefi() { img=$1 sh=$2 echo "qemu-system-i386 -bios ~/bios/OVMF-X32.fd --drive file=${img},format=raw ${qser}" > $sh chmod 755 $sh } # Needs UEFI to be supported qemu_i386_both() { img=$1 sh=$2 echo "qemu-system-i386 --drive file=${img},format=raw ${qser}" > $sh echo "qemu-system-i386 -bios ~/bios/OVMF-X32.fd --drive file=${img},format=raw ${qser}" >> $sh chmod 755 $sh } make_one_image() { local arch=${1?} local geli=${2?} local scheme=${3?} local fs=${4?} local bios=${5?} # Create sparse file and mount newly created filesystem(s) on it img=${IMGDIR}/${arch}-${geli}-${scheme}-${fs}-${bios}.img sh=${IMGDIR}/${arch}-${geli}-${scheme}-${fs}-${bios}.sh echo "vvvvvvvvvvvvvv Creating $img vvvvvvvvvvvvvvv" rm -f ${img}* eval mk_${geli}_${scheme}_${fs}_${bios} ${DESTDIR} ${img} ${MNTPT} ${geli} ${scheme} ${fs} ${bios} eval qemu_${arch}_${bios} ${img} ${sh} [ -n "${SUDO_USER}" ] && chown ${SUDO_USER} ${img}* echo "^^^^^^^^^^^^^^ Created $img ^^^^^^^^^^^^^^^" } # mips # qemu-system-mips -kernel /path/to/rootfs/boot/kernel/kernel -nographic -hda /path/to/disk.img -m 2048 # Powerpc -- doesn't work but maybe it would enough for testing -- needs details # powerpc64 # qemu-system-ppc64 -drive file=/path/to/disk.img,format=raw # sparc64 # qemu-system-sparc64 -drive file=/path/to/disk.img,format=raw # Misc variables SRCTOP=$(make -v SRCTOP) cd ${SRCTOP}/stand OBJDIR=$(make -v .OBJDIR) IMGDIR=${OBJDIR}/boot-images mkdir -p ${IMGDIR} MNTPT=$(mktemp -d /tmp/stand-test.XXXXXX) # Setup the installed tree... DESTDIR=${OBJDIR}/boot-tree rm -rf ${DESTDIR} mkdir -p ${DESTDIR}/boot/defaults mkdir -p ${DESTDIR}/boot/kernel cp /boot/kernel/kernel ${DESTDIR}/boot/kernel echo -h -D -S115200 > ${DESTDIR}/boot.config cat > ${DESTDIR}/boot/loader.conf < ${DESTDIR}/etc/rc < ${NANO_LOG}/_.di 2>&1 ) die( ) { echo "$*" exit 1 } # Automatically include the packaging port here so it is always first so it # builds the port and adds the package so we can add other packages. #XXX Doesn't work for cross build, so punting until I can integreate qemu-static #XXX or poudriere, both of which require priv'd execution. Or qemu-system, #XXX which is super, super slow. #add_port ports-mgmt/pkg #add_port security/sudo #add_port ftp/curl rp=$(realpath ${NANO_OBJ}/) __a=`mount | grep ${rp} | awk '{print length($3), $3;}' | sort -rn | awk '{$1=""; print;}'` if [ -n "$__a" ]; then echo "unmounting $__a" umount $__a fi if [ "$DEBUG" = 1 ]; then DEBUG_BUILD=" DEBUG_FLAGS= -g " else DEBUG_INSTALL=" INSTALL_NODEBUG= t " fi CONF_INSTALL="$CONF_BUILD ${DEBUG_BUILD} " CONF_INSTALL="$CONF_INSTALL ${DEBUG_INSTALL} " if [ "${NANO_PACKAGE_ONLY}" -eq 1 ]; then CONF_INSTALL="${CONF_INSTALL} ${PKG_ONLY_MAKE_CONF} " echo "Automatically building a thin image with packages" else echo "Automatically building a * * F A T * * image so we can build ports" fi VARS="MASTER_SITE_BACKUP MASTER_SITE_OVERRIDE PACKAGEROOT PACKAGESITE" for var in $VARS; do val=$(eval echo "\$$var") if [ -n "$val" ]; then CONF_INSTALL="${CONF_INSTALL} $var=$val" fi done typical_embedded ( ) ( # Need to create rc.conf before we copy over /etc to /conf/base/etc # so now's a good time. local rc=${NANO_WORLDDIR}/etc/rc.conf echo "hostname=nanobsd-${NANO_NAME}" > $rc echo "growfs_enable=YES" >> $rc echo "growfs_type=nanobsd-pingpong" >> $rc echo "ntpdate_enable=YES" >> $rc echo "ifconfig_DEFAULT=DHCP" >> $rc echo "ntpdate_hosts='0.freebsd.pool.ntp.org 1.freebsd.pool.ntp.org'" >> $rc # Make sure that firstboot scripts run so growfs works. # Note: still some issues remvoing this XXX touch ${NANO_WORLDDIR}/firstboot ) customize_cmd typical_embedded fix_pkg ( ) ( chdir ${NANO_WORLDDIR} mkdir -p pkg mkdir -p pkg/db mkdir -p pkg/cache mkdir -p pkg/tmp # Needed for pkg bootstrap mkdir -p usr/local/etc # Will get moved to local/etc ( echo 'PKG_DBDIR = "/pkg/db"' echo 'PKG_CACHEDIR = "/pkg/cache"' echo 'DEFAULT_ALWAYS_YES = "yes"' echo 'ASSUME_ALWAYS_YES = "yes"' ) >> usr/local/etc/pkg.conf [ -z ${NANO_NOPRIV_BUILD} ] || ( echo "./pkg type=dir uname=root gname=wheel mode=0755" echo "./pkg/cache type=dir uname=root gname=wheel mode=0755" echo "./pkg/db type=dir uname=root gname=wheel mode=0755" echo "./pkg/tmp type=dir uname=root gname=wheel mode=0755" ) >> ${NANO_METALOG} ) customize_cmd fix_pkg save_build ( ) ( VERSION_FILE=${NANO_WORLDDIR}/etc/version if [ "${SVNREVISION}" = "${REVISION}" ]; then echo "${NANO_NAME}" > "${VERSION_FILE}" else echo "${NANO_NAME} (${SVNREVISION})" > "${VERSION_FILE}" fi ) customize_cmd save_build shrink_md_fbsize ( ) ( # We have a lot of little files on our memory disks. Let's decrease # the block and frag size to fit more little files on them (this # halves our space requirement by ~50% on /etc and /var on 8.x -- # and gives us more back on 9.x as the default block and frag size # are 4 times larger). sed -i '' -e 's,-S -i 4096,-S -i 4096 -b 4096 -f 512,' \ ${NANO_WORLDDIR}/etc/rc.initdiskless ) customize_cmd shrink_md_fbsize customize_cmd cust_comconsole dos_boot_part ( ) ( local d=/usr/local/share/u-boot/${NANO_BOOT_PKG} local f=${NANO_FAT_DIR} # For now, just copy all the files. However, for iMX6 and Allwinner, # we'll need to put a special boot block at a fixed location # on the disk as well. rm -rf $f mkdir $f chdir $f cp ${d}/* . # Also copy ubldr. u-boot will load it and it will load the kernel # from the ufs partition cp ${NANO_WORLDDIR}/boot/ubldr . cp ${NANO_WORLDDIR}/boot/ubldr.bin . # We have to touch the saveenv file touch uEnv.txt # Now we need to copy over dtb files from the build. cp ${NANO_WORLDDIR}/boot/dtb/*.dtb . ) if [ -n "$NANO_BOOT_PKG" ]; then d=/usr/local/share/u-boot/${NANO_BOOT_PKG} if [ ! -d ${d} ]; then echo ${NANO_BOOT_PKG} not installed. Sadly, it must be. exit 1 fi if [ ! -x /usr/local/bin/mcopy ]; then echo mtools not installed. Sadly, we gotta have it. exit 1 fi customize_cmd dos_boot_part fi product_custom ( ) ( # not quite ready to tweak these in nopriv build if [ -z ${NANO_NOPRIV_BUILD} ]; then # Last second tweaks -- generally not needed chown -R root:wheel ${NANO_WORLDDIR}/root chmod 0755 ${NANO_WORLDDIR}/root/* chmod 0755 ${NANO_WORLDDIR}/* chown -R root:wheel ${NANO_WORLDDIR}/etc chown -R root:wheel ${NANO_WORLDDIR}/boot chown root:wheel ${NANO_WORLDDIR}/ chown root:wheel ${NANO_WORLDDIR}/usr fi ) late_customize_cmd product_custom # # Convenience routines to bring up many settings for standard environments # # # For each architecture, we have a standard set of settings to the extent # it makes sense. For architectures that don't have a standard environment # cfg files need to either define a lot of settings, provide their own disk # imaging, or set which of the variants we support. No subshells, since we # set global variables # std_aarch64 ( ) { } std_amd64 ( ) { std_i386 } std_arm ( ) { } std_armv6 ( ) { } std_armv7 ( ) { } std_i386 ( ) { # Default values, if not overridden in .cfg file : ${NANO_KERNEL:=GENERIC} : ${NANO_DRIVE:=ada0} : ${NANO_LAYOUT:=std-x86} : ${NANO_BOOT_MBR:=${NANO_WORLDDIR}/boot/boot0sio} : ${NANO_BOOT_BSD:=${NANO_WORLDDIR}/boot/boot} } std_mips ( ) { NANO_ENDIAN=big } std_mipsel ( ) { } std_mips64 ( ) { NANO_ENDIAN=big } std_mips64el ( ) { } std_powerpc ( ) { NANO_ENDIAN=big } std_powerpc64 ( ) { NANO_LAYOUT=powerpc64-ibm NANO_ENDIAN=big } std_sparc64 ( ) { NANO_ENDIAN=big } # # QEMU settings for the standard environments # qemu_env ( ) { NANO_DISKIMAGE_FORMAT=qcow2 } # other standard environments will go here eval std_${NANO_ARCH} # Slice 1: FAT for ROM loading bootstrap # Slice 2: Config partition # Slice 3: FreeBSD partition (sized exactly) # Slice 4: FreeBSD partition (empty) # on first boot, we resize slice 3 & 4 to be 1/2 of what's # left over on the SD card after slice 1 and 2 are taken # off the top. We also resize the 'a' partion on first boot # to the size of the partition for the ping/pong upgrade. # This feature needs support in the rc.d bootup script. # # Ideally, we'd not put BSD labels on the MBR disks. # However, we can't boot off raw MBR disks. First, # boot2 defaults to 'a' partition, and freaks out # unless you tell it to use 'c'. But even if we # hack that, then /boot/loader wants to load off # of 'c' partition. If you fix that, then we'll # try to mount root, but sanity checks prevent # slices from working. # : ${NANO_ENDIAN:=little} # make -V something to figure it out? : ${NANO_LAYOUT:=std-embedded} : ${NANO_MAKEFS_UFS:=makefs -t ffs -B ${NANO_ENDIAN}} : ${NANO_DISK_SCHEME:=mbr} # No others really supported ATM (well, gpt) case ${NANO_LAYOUT} in std-embedded) NANO_SLICE_FAT=s1 NANO_SLICE_CFG=s2 NANO_SLICE_ROOT=s3 NANO_SLICE_ALTROOT=s4 NANO_ROOT=${NANO_SLICE_ROOT}a NANO_ALTROOT=${NANO_SLICE_ALTROOT}a ;; std-x86) NANO_SLICE_CFG=s3 NANO_SLICE_ROOT=s1 NANO_SLICE_ALTROOT=s2 NANO_ROOT=${NANO_SLICE_ROOT}a NANO_ALTROOT=${NANO_SLICE_ALTROOT}a ;; powerpc64-ibm) NANO_SLICE_PPCBOOT=s1 NANO_SLICE_CFG=s2 NANO_SLICE_ROOT=s3 NANO_SLICE_ALTROOT=s4 NANO_ROOT=${NANO_SLICE_ROOT}a NANO_ALTROOT=${NANO_SLICE_ALTROOT}a ;; powerpc64-apple) echo Not yet exit 1 ;; std-uefi) NANO_SLICE_UEFI=s1 NANO_SLICE_CFG=s2 NANO_SLICE_ROOT=s3 NANO_SLICE_ALTROOT=s4 NANO_ROOT=${NANO_SLICE_ROOT} NANO_ALTROOT=${NANO_SLICE_ALTROOT} ;; std-uefi-bios) NANO_DISK_SCHEME=gpt NANO_SLICE_UEFI=p1 NANO_SLICE_BOOT=p2 NANO_SLICE_CFG=p3 NANO_SLICE_ROOT=p4 NANO_SLICE_ALTROOT=p5 NANO_ROOT=${NANO_SLICE_ROOT} NANO_ALTROOT=${NANO_SLICE_ALTROOT} ;; *) echo Unknown Layout ${NANO_LAYOUT} exit 1 ;; esac NANO_SLICE_DATA= # Not included # Each major disk scheme has its own routine. Generally # this is for mbr, gpt, etc. These are generally are widely # shared, but some specialized formats won't be shared. create_diskimage ( ) ( eval create_diskimage_${NANO_DISK_SCHEME} ) # Set the path to the same path we use for buldworld to use latest mkimg NANO_TARGET=$(cd ${NANO_SRC}; ${NANO_MAKE} TARGET_ARCH=${NANO_ARCH} -V _TARGET) NANO_TMPPATH=$(cd ${NANO_SRC}; ${NANO_MAKE} MK_AUTO_OBJ=no TARGET=${NANO_TARGET} TARGET_ARCH=${NANO_ARCH} -f Makefile.inc1 buildenv -V TMPPATH) export PATH="${NANO_TMPPATH}:${PATH}" Index: head/usr.sbin/bsdinstall/partedit/gpart_ops.c =================================================================== --- head/usr.sbin/bsdinstall/partedit/gpart_ops.c (revision 342282) +++ head/usr.sbin/bsdinstall/partedit/gpart_ops.c (revision 342283) @@ -1,1464 +1,1476 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include +#include #include #include #include #include #include #include "partedit.h" #define GPART_FLAGS "x" /* Do not commit changes by default */ static void gpart_show_error(const char *title, const char *explanation, const char *errstr) { char *errmsg; char message[512]; int error; if (explanation == NULL) explanation = ""; error = strtol(errstr, &errmsg, 0); if (errmsg != errstr) { while (errmsg[0] == ' ') errmsg++; if (errmsg[0] != '\0') sprintf(message, "%s%s. %s", explanation, strerror(error), errmsg); else sprintf(message, "%s%s", explanation, strerror(error)); } else { sprintf(message, "%s%s", explanation, errmsg); } dialog_msgbox(title, message, 0, 0, TRUE); } static int scheme_supports_labels(const char *scheme) { if (strcmp(scheme, "APM") == 0) return (1); if (strcmp(scheme, "GPT") == 0) return (1); return (0); } static void newfs_command(const char *fstype, char *command, int use_default) { if (strcmp(fstype, "freebsd-ufs") == 0) { int i; DIALOG_LISTITEM items[] = { {"UFS1", "UFS Version 1", "Use version 1 of the UFS file system instead " "of version 2 (not recommended)", 0 }, {"SU", "Softupdates", "Enable softupdates (default)", 1 }, {"SUJ", "Softupdates journaling", "Enable file system journaling (default - " "turn off for SSDs)", 1 }, {"TRIM", "Enable SSD TRIM support", "Enable TRIM support, useful on solid-state drives", 0 }, }; if (!use_default) { int choice; choice = dlg_checklist("UFS Options", "", 0, 0, 0, nitems(items), items, NULL, FLAG_CHECK, &i); if (choice == 1) /* Cancel */ return; } strcpy(command, "newfs "); for (i = 0; i < (int)nitems(items); i++) { if (items[i].state == 0) continue; if (strcmp(items[i].name, "UFS1") == 0) strcat(command, "-O1 "); else if (strcmp(items[i].name, "SU") == 0) strcat(command, "-U "); else if (strcmp(items[i].name, "SUJ") == 0) strcat(command, "-j "); else if (strcmp(items[i].name, "TRIM") == 0) strcat(command, "-t "); } } else if (strcmp(fstype, "freebsd-zfs") == 0) { int i; DIALOG_LISTITEM items[] = { {"fletcher4", "checksum algorithm: fletcher4", "Use fletcher4 for data integrity checking. " "(default)", 1 }, {"fletcher2", "checksum algorithm: fletcher2", "Use fletcher2 for data integrity checking. " "(not recommended)", 0 }, {"sha256", "checksum algorithm: sha256", "Use sha256 for data integrity checking. " "(not recommended)", 0 }, {"atime", "Update atimes for files", "Disable atime update", 0 }, }; if (!use_default) { int choice; choice = dlg_checklist("ZFS Options", "", 0, 0, 0, nitems(items), items, NULL, FLAG_CHECK, &i); if (choice == 1) /* Cancel */ return; } strcpy(command, "zpool create -f -m none "); if (getenv("BSDINSTALL_TMPBOOT") != NULL) { char zfsboot_path[MAXPATHLEN]; snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs", getenv("BSDINSTALL_TMPBOOT")); mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); sprintf(command, "%s -o cachefile=%s/zpool.cache ", command, zfsboot_path); } for (i = 0; i < (int)nitems(items); i++) { if (items[i].state == 0) continue; if (strcmp(items[i].name, "fletcher4") == 0) strcat(command, "-O checksum=fletcher4 "); else if (strcmp(items[i].name, "fletcher2") == 0) strcat(command, "-O checksum=fletcher2 "); else if (strcmp(items[i].name, "sha256") == 0) strcat(command, "-O checksum=sha256 "); else if (strcmp(items[i].name, "atime") == 0) strcat(command, "-O atime=off "); } } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0 || strcmp(fstype, "ms-basic-data") == 0) { int i; DIALOG_LISTITEM items[] = { {"FAT32", "FAT Type 32", "Create a FAT32 filesystem (default)", 1 }, {"FAT16", "FAT Type 16", "Create a FAT16 filesystem", 0 }, {"FAT12", "FAT Type 12", "Create a FAT12 filesystem", 0 }, }; if (!use_default) { int choice; choice = dlg_checklist("FAT Options", "", 0, 0, 0, nitems(items), items, NULL, FLAG_RADIO, &i); if (choice == 1) /* Cancel */ return; } strcpy(command, "newfs_msdos "); for (i = 0; i < (int)nitems(items); i++) { if (items[i].state == 0) continue; - if (strcmp(items[i].name, "FAT16") == 0) + if (strcmp(items[i].name, "FAT32") == 0) + strcat(command, "-F 32 -c 1"); + else if (strcmp(items[i].name, "FAT16") == 0) strcat(command, "-F 16 "); else if (strcmp(items[i].name, "FAT12") == 0) strcat(command, "-F 12 "); } } else { if (!use_default) dialog_msgbox("Error", "No configurable options exist " "for this filesystem.", 0, 0, TRUE); command[0] = '\0'; } } const char * choose_part_type(const char *def_scheme) { int cancel, choice; const char *scheme = NULL; DIALOG_LISTITEM items[] = { {"APM", "Apple Partition Map", "Bootable on PowerPC Apple Hardware", 0 }, {"BSD", "BSD Labels", "Bootable on most x86 systems", 0 }, {"GPT", "GUID Partition Table", "Bootable on most x86 systems and EFI aware ARM64", 0 }, {"MBR", "DOS Partitions", "Bootable on most x86 systems", 0 }, {"VTOC8", "Sun VTOC8 Partition Table", "Bootable on Sun SPARC systems", 0 }, }; parttypemenu: dialog_vars.default_item = __DECONST(char *, def_scheme); cancel = dlg_menu("Partition Scheme", "Select a partition scheme for this volume:", 0, 0, 0, nitems(items), items, &choice, NULL); dialog_vars.default_item = NULL; if (cancel) return NULL; if (!is_scheme_bootable(items[choice].name)) { char message[512]; sprintf(message, "This partition scheme (%s) is not " "bootable on this platform. Are you sure you want " "to proceed?", items[choice].name); dialog_vars.defaultno = TRUE; cancel = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (cancel) /* cancel */ goto parttypemenu; } scheme = items[choice].name; return scheme; } int gpart_partition(const char *lg_name, const char *scheme) { int cancel; struct gctl_req *r; const char *errstr; schememenu: if (scheme == NULL) { scheme = choose_part_type(default_scheme()); if (scheme == NULL) return (-1); if (!is_scheme_bootable(scheme)) { char message[512]; sprintf(message, "This partition scheme (%s) is not " "bootable on this platform. Are you sure you want " "to proceed?", scheme); dialog_vars.defaultno = TRUE; cancel = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (cancel) { /* cancel */ /* Reset scheme so user can choose another */ scheme = NULL; goto schememenu; } } } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "scheme", -1, scheme); gctl_ro_param(r, "verb", -1, "create"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); scheme = NULL; goto schememenu; } gctl_free(r); if (bootcode_path(scheme) != NULL) get_part_metadata(lg_name, 1)->bootcode = 1; return (0); } static void gpart_activate(struct gprovider *pp) { struct gconfig *gc; struct gctl_req *r; const char *errstr, *scheme; const char *attribute = NULL; intmax_t idx; /* * Some partition schemes need this partition to be marked 'active' * for it to be bootable. */ LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0) attribute = "active"; else return; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "index") == 0) { idx = atoi(gc->lg_val); break; } } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); gctl_ro_param(r, "verb", -1, "set"); gctl_ro_param(r, "attrib", -1, attribute); gctl_ro_param(r, "index", sizeof(idx), &idx); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", "Error marking partition active:", errstr); gctl_free(r); } void gpart_set_root(const char *lg_name, const char *attribute) { struct gctl_req *r; const char *errstr; r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_name); gctl_ro_param(r, "flags", -1, "C"); gctl_ro_param(r, "verb", -1, "set"); gctl_ro_param(r, "attrib", -1, attribute); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", "Error setting parameter on disk:", errstr); gctl_free(r); } static void gpart_bootcode(struct ggeom *gp) { const char *bootcode; struct gconfig *gc; struct gctl_req *r; const char *errstr, *scheme; uint8_t *boot; size_t bootsize, bytes; int bootfd; /* * Write default bootcode to the newly partitioned disk, if that * applies on this platform. */ LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } bootcode = bootcode_path(scheme); if (bootcode == NULL) return; bootfd = open(bootcode, O_RDONLY); if (bootfd < 0) { dialog_msgbox("Bootcode Error", strerror(errno), 0, 0, TRUE); return; } - + bootsize = lseek(bootfd, 0, SEEK_END); boot = malloc(bootsize); lseek(bootfd, 0, SEEK_SET); bytes = 0; while (bytes < bootsize) bytes += read(bootfd, boot + bytes, bootsize - bytes); close(bootfd); r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, gp->lg_name); gctl_ro_param(r, "verb", -1, "bootcode"); gctl_ro_param(r, "bootcode", bootsize, boot); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Bootcode Error", NULL, errstr); gctl_free(r); free(boot); } static void gpart_partcode(struct gprovider *pp, const char *fstype) { struct gconfig *gc; const char *scheme; const char *indexstr; char message[255], command[255]; LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } /* Make sure this partition scheme needs partcode on this platform */ if (partcode_path(scheme, fstype) == NULL) return; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "index") == 0) { indexstr = gc->lg_val; break; } } /* Shell out to gpart for partcode for now */ sprintf(command, "gpart bootcode -p %s -i %s %s", partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name); if (system(command) != 0) { sprintf(message, "Error installing partcode on partition %s", pp->lg_name); dialog_msgbox("Error", message, 0, 0, TRUE); } } void gpart_destroy(struct ggeom *lg_geom) { struct gctl_req *r; struct gprovider *pp; const char *errstr; int force = 1; /* Delete all child metadata */ LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) gpart_delete(pp); /* Revert any local changes to get this geom into a pristine state */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_issue(r); /* Ignore errors -- these are non-fatal */ gctl_free(r); /* Now destroy the geom itself */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "force", sizeof(force), &force); gctl_ro_param(r, "verb", -1, "destroy"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { /* * Check if we reverted away the existence of the geom * altogether. Show all other errors to the user. */ if (strtol(errstr, NULL, 0) != EINVAL) gpart_show_error("Error", NULL, errstr); } gctl_free(r); /* And any metadata associated with the partition scheme itself */ delete_part_metadata(lg_geom->lg_name); } void gpart_edit(struct gprovider *pp) { struct gctl_req *r; struct gconfig *gc; struct gconsumer *cp; struct ggeom *geom; const char *errstr, *oldtype, *scheme; struct partition_metadata *md; char sizestr[32]; char newfs[255]; intmax_t idx; int hadlabel, choice, junk, nitems; unsigned i; DIALOG_FORMITEM items[] = { {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0, FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " "freebsd-swap)", FALSE}, {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0, FALSE, "Partition size. Append K, M, G for kilobytes, " "megabytes or gigabytes.", FALSE}, {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, FALSE, "Path at which to mount this partition (leave blank " "for swap, set to / for root filesystem)", FALSE}, {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, "Partition name. Not all partition schemes support this.", FALSE}, }; /* * Find the PART geom we are manipulating. This may be a consumer of * this provider, or its parent. Check the consumer case first. */ geom = NULL; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { /* Check for zombie geoms, treating them as blank */ scheme = NULL; LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } if (scheme == NULL || strcmp(scheme, "(none)") == 0) { gpart_partition(cp->lg_geom->lg_name, NULL); return; } /* If this is a nested partition, edit as usual */ if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) break; /* Destroy the geom and all sub-partitions */ gpart_destroy(cp->lg_geom); /* Now re-partition and return */ gpart_partition(cp->lg_geom->lg_name, NULL); return; } if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) geom = pp->lg_geom; if (geom == NULL) { /* Disk not partitioned, so partition it */ gpart_partition(pp->lg_name, NULL); return; } LIST_FOREACH(gc, &geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } nitems = scheme_supports_labels(scheme) ? 4 : 3; /* Edit editable parameters of a partition */ hadlabel = 0; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "type") == 0) { oldtype = gc->lg_val; items[0].text = gc->lg_val; } if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { hadlabel = 1; items[3].text = gc->lg_val; } if (strcmp(gc->lg_name, "index") == 0) idx = atoi(gc->lg_val); } TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { if (md->fstab != NULL) items[2].text = md->fstab->fs_file; break; } } humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); items[1].text = sizestr; editpart: choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk); if (choice) /* Cancel pressed */ goto endedit; /* If this is the root partition, check that this fs is bootable */ if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme, items[0].text)) { char message[512]; sprintf(message, "This file system (%s) is not bootable " "on this system. Are you sure you want to proceed?", items[0].text); dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (choice == 1) /* cancel */ goto editpart; } /* Check if the label has a / in it */ if (strchr(items[3].text, '/') != NULL) { dialog_msgbox("Error", "Label contains a /, which is not an " "allowed character.", 0, 0, TRUE); goto editpart; } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "modify"); gctl_ro_param(r, "index", sizeof(idx), &idx); if (hadlabel || items[3].text[0] != '\0') gctl_ro_param(r, "label", -1, items[3].text); gctl_ro_param(r, "type", -1, items[0].text); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); goto editpart; } gctl_free(r); newfs_command(items[0].text, newfs, 1); set_default_part_metadata(pp->lg_name, scheme, items[0].text, items[2].text, (strcmp(oldtype, items[0].text) != 0) ? newfs : NULL); endedit: if (strcmp(oldtype, items[0].text) != 0 && cp != NULL) gpart_destroy(cp->lg_geom); if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text, "freebsd") == 0) gpart_partition(pp->lg_name, "BSD"); for (i = 0; i < nitems(items); i++) if (items[i].text_free) free(items[i].text); } void set_default_part_metadata(const char *name, const char *scheme, const char *type, const char *mountpoint, const char *newfs) { struct partition_metadata *md; char *zpool_name = NULL; const char *default_bootmount = NULL; int i; /* Set part metadata */ md = get_part_metadata(name, 1); if (newfs) { if (md->newfs != NULL) { free(md->newfs); md->newfs = NULL; } if (newfs != NULL && newfs[0] != '\0') { md->newfs = malloc(strlen(newfs) + strlen(" /dev/") + strlen(mountpoint) + 5 + strlen(name) + 1); if (strcmp("freebsd-zfs", type) == 0) { zpool_name = strdup((strlen(mountpoint) == 1) ? "root" : &mountpoint[1]); for (i = 0; zpool_name[i] != 0; i++) if (!isalnum(zpool_name[i])) zpool_name[i] = '_'; sprintf(md->newfs, "%s %s /dev/%s", newfs, zpool_name, name); } else { sprintf(md->newfs, "%s /dev/%s", newfs, name); } } } if (strcmp(type, "freebsd-swap") == 0) mountpoint = "none"; if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) { - if (default_bootmount == NULL) + if (default_bootmount == NULL) { + + int fd = open("/tmp/bsdinstall-esps", O_CREAT | O_WRONLY | O_APPEND, + 0600); + if (fd > 0) { + write(fd, md->name, strlen(md->name)); + close(fd); + } + md->bootcode = 1; + } else if (mountpoint == NULL || strlen(mountpoint) == 0) mountpoint = default_bootmount; } /* VTOC8 needs partcode at the start of partitions */ if (strcmp(scheme, "VTOC8") == 0 && (strcmp(type, "freebsd-ufs") == 0 || strcmp(type, "freebsd-zfs") == 0)) md->bootcode = 1; if (mountpoint == NULL || mountpoint[0] == '\0') { if (md->fstab != NULL) { free(md->fstab->fs_spec); free(md->fstab->fs_file); free(md->fstab->fs_vfstype); free(md->fstab->fs_mntops); free(md->fstab->fs_type); free(md->fstab); md->fstab = NULL; } } else { if (md->fstab == NULL) { md->fstab = malloc(sizeof(struct fstab)); } else { free(md->fstab->fs_spec); free(md->fstab->fs_file); free(md->fstab->fs_vfstype); free(md->fstab->fs_mntops); free(md->fstab->fs_type); } if (strcmp("freebsd-zfs", type) == 0) { md->fstab->fs_spec = strdup(zpool_name); } else { md->fstab->fs_spec = malloc(strlen(name) + strlen("/dev/") + 1); sprintf(md->fstab->fs_spec, "/dev/%s", name); } md->fstab->fs_file = strdup(mountpoint); /* Get VFS from text after freebsd-, if possible */ if (strncmp("freebsd-", type, 8) == 0) md->fstab->fs_vfstype = strdup(&type[8]); else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0 || strcmp("ms-basic-data", type) == 0) md->fstab->fs_vfstype = strdup("msdosfs"); else md->fstab->fs_vfstype = strdup(type); /* Guess */ if (strcmp(type, "freebsd-swap") == 0) { md->fstab->fs_type = strdup(FSTAB_SW); md->fstab->fs_freq = 0; md->fstab->fs_passno = 0; } else if (strcmp(type, "freebsd-zfs") == 0) { md->fstab->fs_type = strdup(FSTAB_RW); md->fstab->fs_freq = 0; md->fstab->fs_passno = 0; } else { md->fstab->fs_type = strdup(FSTAB_RW); if (strcmp(mountpoint, "/") == 0) { md->fstab->fs_freq = 1; md->fstab->fs_passno = 1; } else { md->fstab->fs_freq = 2; md->fstab->fs_passno = 2; } } md->fstab->fs_mntops = strdup(md->fstab->fs_type); } if (zpool_name != NULL) free(zpool_name); } static int part_compare(const void *xa, const void *xb) { struct gprovider **a = (struct gprovider **)xa; struct gprovider **b = (struct gprovider **)xb; intmax_t astart, bstart; struct gconfig *gc; astart = bstart = 0; LIST_FOREACH(gc, &(*a)->lg_config, lg_config) if (strcmp(gc->lg_name, "start") == 0) { astart = strtoimax(gc->lg_val, NULL, 0); break; } LIST_FOREACH(gc, &(*b)->lg_config, lg_config) if (strcmp(gc->lg_name, "start") == 0) { bstart = strtoimax(gc->lg_val, NULL, 0); break; } if (astart < bstart) return -1; else if (astart > bstart) return 1; else return 0; } intmax_t gpart_max_free(struct ggeom *geom, intmax_t *npartstart) { struct gconfig *gc; struct gprovider *pp, **providers; intmax_t sectorsize, stripesize, offset; intmax_t lastend; intmax_t start, end; intmax_t maxsize, maxstart; intmax_t partstart, partend; int i, nparts; /* Now get the maximum free size and free start */ start = end = 0; LIST_FOREACH(gc, &geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "first") == 0) start = strtoimax(gc->lg_val, NULL, 0); if (strcmp(gc->lg_name, "last") == 0) end = strtoimax(gc->lg_val, NULL, 0); } i = nparts = 0; LIST_FOREACH(pp, &geom->lg_provider, lg_provider) nparts++; providers = calloc(nparts, sizeof(providers[0])); LIST_FOREACH(pp, &geom->lg_provider, lg_provider) providers[i++] = pp; qsort(providers, nparts, sizeof(providers[0]), part_compare); lastend = start - 1; maxsize = 0; for (i = 0; i < nparts; i++) { pp = providers[i]; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "start") == 0) partstart = strtoimax(gc->lg_val, NULL, 0); if (strcmp(gc->lg_name, "end") == 0) partend = strtoimax(gc->lg_val, NULL, 0); } if (partstart - lastend > maxsize) { maxsize = partstart - lastend - 1; maxstart = lastend + 1; } lastend = partend; } if (end - lastend > maxsize) { maxsize = end - lastend; maxstart = lastend + 1; } pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; /* * Round the start and size of the largest available space up to * the nearest multiple of the adjusted stripe size. * * The adjusted stripe size is the least common multiple of the * actual stripe size, or the sector size if no stripe size was * reported, and 4096. The reason for this is that contemporary * disks often have 4096-byte physical sectors but report 512 * bytes instead for compatibility with older / broken operating * systems and BIOSes. For the same reasons, virtualized storage * may also report a 512-byte stripe size, or none at all. */ sectorsize = pp->lg_sectorsize; if ((stripesize = pp->lg_stripesize) == 0) stripesize = sectorsize; while (stripesize % 4096 != 0) stripesize *= 2; if ((offset = maxstart * sectorsize % stripesize) != 0) { offset = (stripesize - offset) / sectorsize; maxstart += offset; maxsize -= offset; } if (npartstart != NULL) *npartstart = maxstart; return (maxsize); } static size_t add_boot_partition(struct ggeom *geom, struct gprovider *pp, const char *scheme, int interactive) { struct gconfig *gc; struct gprovider *ppi; int choice; /* Check for existing freebsd-boot partition */ LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) { struct partition_metadata *md; const char *bootmount = NULL; LIST_FOREACH(gc, &ppi->lg_config, lg_config) if (strcmp(gc->lg_name, "type") == 0) break; if (gc == NULL) continue; if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0) continue; /* * If the boot partition is not mountable and needs partcode, * but doesn't have it, it doesn't satisfy our requirements. */ md = get_part_metadata(ppi->lg_name, 0); if (bootmount == NULL && (md == NULL || !md->bootcode)) continue; /* If it is mountable, but mounted somewhere else, remount */ if (bootmount != NULL && md != NULL && md->fstab != NULL && strlen(md->fstab->fs_file) > 0 && strcmp(md->fstab->fs_file, bootmount) != 0) continue; /* If it is mountable, but mountpoint is not set, mount it */ if (bootmount != NULL && md == NULL) set_default_part_metadata(ppi->lg_name, scheme, gc->lg_val, bootmount, NULL); /* Looks good at this point, no added data needed */ return (0); } if (interactive) choice = dialog_yesno("Boot Partition", "This partition scheme requires a boot partition " "for the disk to be bootable. Would you like to " "make one now?", 0, 0); else choice = 0; if (choice == 0) { /* yes */ struct partition_metadata *md; const char *bootmount = NULL; char *bootpartname = NULL; char sizestr[7]; humanize_number(sizestr, 7, bootpart_size(scheme), "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); gpart_create(pp, bootpart_type(scheme, &bootmount), sizestr, bootmount, &bootpartname, 0); if (bootpartname == NULL) /* Error reported to user already */ return 0; /* If the part is not mountable, make sure newfs isn't set */ if (bootmount == NULL) { md = get_part_metadata(bootpartname, 0); if (md != NULL && md->newfs != NULL) { free(md->newfs); md->newfs = NULL; } } free(bootpartname); return (bootpart_size(scheme)); } return (0); } void gpart_create(struct gprovider *pp, const char *default_type, const char *default_size, const char *default_mountpoint, char **partname, int interactive) { struct gctl_req *r; struct gconfig *gc; struct gconsumer *cp; struct ggeom *geom; const char *errstr, *scheme; char sizestr[32], startstr[32], output[64], *newpartname; char newfs[255], options_fstype[64]; intmax_t maxsize, size, sector, firstfree, stripe; uint64_t bytes; int nitems, choice, junk; unsigned i; DIALOG_FORMITEM items[] = { {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0, FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " "freebsd-swap)", FALSE}, {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0, FALSE, "Partition size. Append K, M, G for kilobytes, " "megabytes or gigabytes.", FALSE}, {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, FALSE, "Path at which to mount partition (blank for " "swap, / for root filesystem)", FALSE}, {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, "Partition name. Not all partition schemes support this.", FALSE}, }; if (partname != NULL) *partname = NULL; /* Record sector and stripe sizes */ sector = pp->lg_sectorsize; stripe = pp->lg_stripesize; /* * Find the PART geom we are manipulating. This may be a consumer of * this provider, or its parent. Check the consumer case first. */ geom = NULL; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { geom = cp->lg_geom; break; } if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) geom = pp->lg_geom; /* Now get the partition scheme */ scheme = NULL; if (geom != NULL) { LIST_FOREACH(gc, &geom->lg_config, lg_config) if (strcmp(gc->lg_name, "scheme") == 0) scheme = gc->lg_val; } if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { if (gpart_partition(pp->lg_name, NULL) == 0) dialog_msgbox("", "The partition table has been successfully created." " Please press Create again to create partitions.", 0, 0, TRUE); return; } /* * If we still don't have a geom, either the user has * canceled partitioning or there has been an error which has already * been displayed, so bail. */ if (geom == NULL) return; maxsize = size = gpart_max_free(geom, &firstfree); if (size <= 0) { dialog_msgbox("Error", "No free space left on device.", 0, 0, TRUE); return; } humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); items[1].text = sizestr; /* Special-case the MBR default type for nested partitions */ if (strcmp(scheme, "MBR") == 0) { items[0].text = "freebsd"; items[0].help = "Filesystem type (e.g. freebsd, fat32)"; } nitems = scheme_supports_labels(scheme) ? 4 : 3; if (default_type != NULL) items[0].text = (char *)default_type; if (default_size != NULL) items[1].text = (char *)default_size; if (default_mountpoint != NULL) items[2].text = (char *)default_mountpoint; /* Default options */ strncpy(options_fstype, items[0].text, sizeof(options_fstype)); newfs_command(options_fstype, newfs, 1); addpartform: if (interactive) { dialog_vars.extra_label = "Options"; dialog_vars.extra_button = TRUE; choice = dlg_form("Add Partition", "", 0, 0, 0, nitems, items, &junk); dialog_vars.extra_button = FALSE; switch (choice) { case 0: /* OK */ break; case 1: /* Cancel */ return; case 3: /* Options */ strncpy(options_fstype, items[0].text, sizeof(options_fstype)); newfs_command(options_fstype, newfs, 0); goto addpartform; } } /* * If the user changed the fs type after specifying options, undo * their choices in favor of the new filesystem's defaults. */ if (strcmp(options_fstype, items[0].text) != 0) { strncpy(options_fstype, items[0].text, sizeof(options_fstype)); newfs_command(options_fstype, newfs, 1); } size = maxsize; if (strlen(items[1].text) > 0) { if (expand_number(items[1].text, &bytes) != 0) { char error[512]; sprintf(error, "Invalid size: %s\n", strerror(errno)); dialog_msgbox("Error", error, 0, 0, TRUE); goto addpartform; } size = MIN((intmax_t)(bytes/sector), maxsize); } /* Check if the label has a / in it */ if (strchr(items[3].text, '/') != NULL) { dialog_msgbox("Error", "Label contains a /, which is not an " "allowed character.", 0, 0, TRUE); goto addpartform; } /* Warn if no mountpoint set */ if (strcmp(items[0].text, "freebsd-ufs") == 0 && items[2].text[0] != '/') { choice = 0; if (interactive) { dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", "This partition does not have a valid mountpoint " "(for the partition from which you intend to boot the " "operating system, the mountpoint should be /). Are you " "sure you want to continue?" , 0, 0); dialog_vars.defaultno = FALSE; } if (choice == 1) /* cancel */ goto addpartform; } /* * Error if this scheme needs nested partitions, this is one, and * a mountpoint was set. */ if (strcmp(items[0].text, "freebsd") == 0 && strlen(items[2].text) > 0) { dialog_msgbox("Error", "Partitions of type \"freebsd\" are " "nested BSD-type partition schemes and cannot have " "mountpoints. After creating one, select it and press " "Create again to add the actual file systems.", 0, 0, TRUE); goto addpartform; } /* If this is the root partition, check that this scheme is bootable */ if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) { char message[512]; sprintf(message, "This partition scheme (%s) is not bootable " "on this platform. Are you sure you want to proceed?", scheme); dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (choice == 1) /* cancel */ goto addpartform; } /* If this is the root partition, check that this fs is bootable */ if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme, items[0].text)) { char message[512]; sprintf(message, "This file system (%s) is not bootable " "on this system. Are you sure you want to proceed?", items[0].text); dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (choice == 1) /* cancel */ goto addpartform; } /* * If this is the root partition, and we need a boot partition, ask * the user to add one. */ if ((strcmp(items[0].text, "freebsd") == 0 || strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0) { size_t bytes = add_boot_partition(geom, pp, scheme, interactive); /* Now adjust the part we are really adding forward */ if (bytes > 0) { firstfree += bytes / sector; size -= (bytes + stripe)/sector; if (stripe > 0 && (firstfree*sector % stripe) != 0) firstfree += (stripe - ((firstfree*sector) % stripe)) / sector; } } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "add"); gctl_ro_param(r, "type", -1, items[0].text); snprintf(sizestr, sizeof(sizestr), "%jd", size); gctl_ro_param(r, "size", -1, sizestr); snprintf(startstr, sizeof(startstr), "%jd", firstfree); gctl_ro_param(r, "start", -1, startstr); if (items[3].text[0] != '\0') gctl_ro_param(r, "label", -1, items[3].text); gctl_rw_param(r, "output", sizeof(output), output); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); goto addpartform; } newpartname = strtok(output, " "); gctl_free(r); /* * Try to destroy any geom that gpart picked up already here from * dirty blocks. */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, newpartname); gctl_ro_param(r, "flags", -1, GPART_FLAGS); junk = 1; gctl_ro_param(r, "force", sizeof(junk), &junk); gctl_ro_param(r, "verb", -1, "destroy"); gctl_issue(r); /* Error usually expected and non-fatal */ gctl_free(r); if (strcmp(items[0].text, "freebsd") == 0) gpart_partition(newpartname, "BSD"); else set_default_part_metadata(newpartname, scheme, items[0].text, items[2].text, newfs); for (i = 0; i < nitems(items); i++) if (items[i].text_free) free(items[i].text); if (partname != NULL) *partname = strdup(newpartname); } void gpart_delete(struct gprovider *pp) { struct gconfig *gc; struct ggeom *geom; struct gconsumer *cp; struct gctl_req *r; const char *errstr; intmax_t idx; int is_partition; /* Is it a partition? */ is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); /* Find out if this is the root of a gpart geom */ geom = NULL; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { geom = cp->lg_geom; break; } /* If so, destroy all children */ if (geom != NULL) { gpart_destroy(geom); /* If this is a partition, revert it, so it can be deleted */ if (is_partition) { r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_issue(r); /* Ignore non-fatal errors */ gctl_free(r); } } /* * If this is not a partition, see if that is a problem, complain if * necessary, and return always, since we need not do anything further, * error or no. */ if (!is_partition) { if (geom == NULL) dialog_msgbox("Error", "Only partitions can be deleted.", 0, 0, TRUE); return; } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "delete"); LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "index") == 0) { idx = atoi(gc->lg_val); gctl_ro_param(r, "index", sizeof(idx), &idx); break; } } errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); return; } gctl_free(r); delete_part_metadata(pp->lg_name); } void gpart_revert_all(struct gmesh *mesh) { struct gclass *classp; struct gconfig *gc; struct ggeom *gp; struct gctl_req *r; const char *modified; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "PART") == 0) break; } if (strcmp(classp->lg_name, "PART") != 0) { dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); return; } LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { modified = "true"; /* XXX: If we don't know (kernel too old), * assume there are modifications. */ LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "modified") == 0) { modified = gc->lg_val; break; } } if (strcmp(modified, "false") == 0) continue; r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, gp->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_issue(r); gctl_free(r); } } void gpart_commit(struct gmesh *mesh) { struct partition_metadata *md; struct gclass *classp; struct ggeom *gp; struct gconfig *gc; struct gconsumer *cp; struct gprovider *pp; struct gctl_req *r; const char *errstr; const char *modified; const char *rootfs; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "PART") == 0) break; } /* Figure out what filesystem / uses */ rootfs = "ufs"; /* Assume ufs if nothing else present */ TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) { rootfs = md->fstab->fs_vfstype; break; } } if (strcmp(classp->lg_name, "PART") != 0) { dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); return; } LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { modified = "true"; /* XXX: If we don't know (kernel too old), * assume there are modifications. */ LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "modified") == 0) { modified = gc->lg_val; break; } } if (strcmp(modified, "false") == 0) continue; /* Add bootcode if necessary, before the commit */ md = get_part_metadata(gp->lg_name, 0); if (md != NULL && md->bootcode) gpart_bootcode(gp); /* Now install partcode on its partitions, if necessary */ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { md = get_part_metadata(pp->lg_name, 0); if (md == NULL || !md->bootcode) continue; /* Mark this partition active if that's required */ gpart_activate(pp); /* Check if the partition has sub-partitions */ LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) break; if (cp == NULL) /* No sub-partitions */ gpart_partcode(pp, rootfs); } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, gp->lg_name); gctl_ro_param(r, "verb", -1, "commit"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", NULL, errstr); gctl_free(r); } } Index: head/usr.sbin/bsdinstall/partedit/partedit_arm64.c =================================================================== --- head/usr.sbin/bsdinstall/partedit/partedit_arm64.c (revision 342282) +++ head/usr.sbin/bsdinstall/partedit/partedit_arm64.c (revision 342283) @@ -1,104 +1,100 @@ /* * Copyright (C) 2016 Cavium Inc. * All rights reserved. * * Developed by Semihalf. * Based on work by Nathan Whitehorn. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include "partedit.h" /* EFI partition size in bytes */ -#define EFI_BOOTPART_SIZE (200 * 1024 * 1024) -#define EFI_BOOTPART_PATH "/boot/boot1.efifat" +#define EFI_BOOTPART_SIZE (260 * 1024 * 1024) const char * default_scheme(void) { return ("GPT"); } int is_scheme_bootable(const char *part_type) { if (strcmp(part_type, "GPT") == 0) return (1); return (0); } int is_fs_bootable(const char *part_type, const char *fs) { if (strcmp(fs, "freebsd-ufs") == 0) return (1); return (0); } size_t bootpart_size(const char *scheme) { /* We only support GPT with EFI */ if (strcmp(scheme, "GPT") != 0) return (0); return (EFI_BOOTPART_SIZE); } const char * bootpart_type(const char *scheme, const char **mountpoint) { /* Only EFI is supported as boot partition */ return ("efi"); } const char * bootcode_path(const char *part_type) { return (NULL); } const char * partcode_path(const char *part_type, const char *fs_type) { - if (strcmp(part_type, "GPT") == 0) - return (EFI_BOOTPART_PATH); - - /* No boot partition data for non-GPT */ + /* No boot partition data for ARM64 */ return (NULL); } Index: head/usr.sbin/bsdinstall/partedit/partedit_x86.c =================================================================== --- head/usr.sbin/bsdinstall/partedit/partedit_x86.c (revision 342282) +++ head/usr.sbin/bsdinstall/partedit/partedit_x86.c (revision 342283) @@ -1,156 +1,153 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include "partedit.h" /* EFI partition size in bytes */ -#define EFI_BOOTPART_SIZE (200 * 1024 * 1024) -#define EFI_BOOTPART_PATH "/boot/boot1.efifat" +#define EFI_BOOTPART_SIZE (260 * 1024 * 1024) static const char * x86_bootmethod(void) { static char fw[255] = ""; size_t len = sizeof(fw); int error; if (strlen(fw) == 0) { error = sysctlbyname("machdep.bootmethod", fw, &len, NULL, -1); if (error != 0) return ("BIOS"); } return (fw); } const char * default_scheme(void) { if (strcmp(x86_bootmethod(), "UEFI") == 0) return ("GPT"); else return ("MBR"); } int is_scheme_bootable(const char *part_type) { if (strcmp(part_type, "GPT") == 0) return (1); if (strcmp(x86_bootmethod(), "BIOS") == 0) { if (strcmp(part_type, "BSD") == 0) return (1); if (strcmp(part_type, "MBR") == 0) return (1); } return (0); } int is_fs_bootable(const char *part_type, const char *fs) { if (strcmp(fs, "freebsd-ufs") == 0) return (1); if (strcmp(fs, "freebsd-zfs") == 0 && strcmp(part_type, "GPT") == 0 && strcmp(x86_bootmethod(), "BIOS") == 0) return (1); return (0); } size_t bootpart_size(const char *scheme) { /* No partcode except for GPT */ if (strcmp(scheme, "GPT") != 0) return (0); if (strcmp(x86_bootmethod(), "BIOS") == 0) return (512*1024); else return (EFI_BOOTPART_SIZE); return (0); } const char * bootpart_type(const char *scheme, const char **mountpoint) { if (strcmp(x86_bootmethod(), "UEFI") == 0) return ("efi"); return ("freebsd-boot"); } const char * bootcode_path(const char *part_type) { if (strcmp(x86_bootmethod(), "UEFI") == 0) return (NULL); if (strcmp(part_type, "GPT") == 0) return ("/boot/pmbr"); if (strcmp(part_type, "MBR") == 0) return ("/boot/mbr"); if (strcmp(part_type, "BSD") == 0) return ("/boot/boot"); return (NULL); } const char * partcode_path(const char *part_type, const char *fs_type) { - if (strcmp(part_type, "GPT") == 0) { - if (strcmp(x86_bootmethod(), "UEFI") == 0) - return (EFI_BOOTPART_PATH); - else if (strcmp(fs_type, "zfs") == 0) + if (strcmp(part_type, "GPT") == 0 && strcmp(x86_bootmethod(), "UEFI") != 0) { + if (strcmp(fs_type, "zfs") == 0) return ("/boot/gptzfsboot"); else return ("/boot/gptboot"); } - /* No partcode except for GPT */ + /* No partcode except for non-UEFI GPT */ return (NULL); } Index: head/usr.sbin/bsdinstall/scripts/bootconfig =================================================================== --- head/usr.sbin/bsdinstall/scripts/bootconfig (revision 342282) +++ head/usr.sbin/bsdinstall/scripts/bootconfig (revision 342283) @@ -1,40 +1,155 @@ #!/bin/sh #- +# Copyright (c) 2018 Rebecca Cran # Copyright (c) 2017 Nathan Whitehorn # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ +die() { + echo $* + exit 1 +} + if [ `uname -m` == powerpc ]; then platform=`sysctl -n hw.platform` if [ "$platform" == ps3 -o "$platform" == powernv ]; then rootpart=$(awk '{ if($2 == "/") printf("%s:%s\n", $3, $1); }' $PATH_FSTAB) mkdir -p $BSDINSTALL_CHROOT/boot/etc/ echo FreeBSD=\'/kernel/kernel kernelname=/boot/kernel/kernel vfs.root.mountfrom=${rootpart}\' > $BSDINSTALL_CHROOT/boot/etc/kboot.conf fi fi -# For new-style EFI booting, add code here -# Add boot0cfg for MBR BIOS booting? +# Update the ESP (EFI System Partition) with the new bootloader +if [ "$(uname -m)" = "amd64" ] || [ "$(uname -m)" = "i386" ]; then + X86_BOOTMETHOD=$(sysctl -n machdep.bootmethod) +fi +if [ "$(uname -m)" = "arm64" ] || [ "$X86_BOOTMETHOD" = "UEFI" ]; then + UFSBOOT_ESPS=$(cat /tmp/bsdinstall-esps) + num_esps=0 + + if [ -n "$ZFSBOOT_DISKS" ]; then + # We're in a ZFS install environment + for disk in $ZFSBOOT_DISKS; do + index=$(gpart show "$disk" | cut -w -f 4,5 | grep "efi" | cut -w -f 1) + # Check that $index is an integer + [ -n "$index" ] && [ "$index" -eq "$index" ] && [ "$index" -ge 0 ] 2> /dev/null + if [ $? -ne 0 ]; then + continue + fi + + if [ -e "/dev/${disk}p${index}" ]; then + ESPS="$ESPS ${disk}p${index}" + elif [ -e "/dev/${disk}s${index}" ]; then + ESPS="$ESPS ${disk}s${index}" + else + continue + fi + + num_esps=$((num_esps + 1)) + done + fi + + if [ -n "$UFSBOOT_ESPS" ]; then + # We're in a UFS install environment + for partition in $UFSBOOT_ESPS; do + ESPS="$ESPS $partition" + num_esps=$((num_esps + 1)) + done + fi + + if [ -z "$ESPS" ]; then + # The installer hasn't given us any ESPs to use. + # Try and figure out which to use by looking for an + # unformatted efi partition + for disk in $(sysctl -n kern.disks); do + hasfreebsd=$(gpart show "$disk" | cut -w -f 4,5 | grep "freebsd") + if [ -n "$hasfreebsd" ]; then + index=$(gpart show "$disk" | cut -w -f 4,5 | grep "efi" | cut -w -f 1) + # Check that $index is a valid integer + [ -n "$index" ] && [ "$index" -eq "$index" ] && [ "$index" -ge 0 ] 2> /dev/null + if [ $? -ne 0 ]; then + continue + fi + + mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) + if [ -e "/dev/${disk}p${index}" ]; then + dev=${disk}p${index} + elif [ -e "/dev/${disk}s${index}" ]; then + dev=/${disk}s${index} + else + continue + fi + + # Try and mount it. If it fails, assume it's + # unformatted and should be used. + mount -t msdosfs "/dev/${dev}" "${mntpt}" + if [ $? -ne 0 ]; then + ESPS="$ESPS ${dev}" + num_esps=$((num_esps + 1)) + else + umount "${mntpt}" + fi + rmdir "${mntpt}" + fi + done + fi + + for esp in $ESPS; do + newfs_msdos -F 32 -c 1 -L EFISYS "/dev/$esp" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + die "Failed to format ESP $esp as FAT32" + fi + + mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) + mount -t msdosfs "/dev/${esp}" "${mntpt}" + if [ $? -ne 0 ]; then + die "Failed to mount ESP ${dev} on ${mntpt}" + fi + + mkdir -p "$mntpt/EFI/freebsd" + cp "$BSDINSTALL_CHROOT/boot/loader.efi" "${mntpt}/EFI/freebsd/loader.efi" + + if [ "$num_esps" -gt 1 ]; then + bootlabel="FreeBSD (${esp})" + else + bootlabel="FreeBSD" + fi + + efibootmgr --create --label "$bootlabel" --loader "${mntpt}/EFI/freebsd/loader.efi" > /dev/null + + umount "${mntpt}" + rmdir "${mntpt}" + + # When creating new entries, efibootmgr doesn't mark them active, so we need to + # do so. It doesn't make it easy to find which entry it just added, so rely on + # the fact that it places the new entry first in BootOrder. + bootorder=$(efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex | head -1) + bootentry=$(echo "$bootorder" | cut -w -f 3)$(echo "$bootorder" | cut -w -f 2) + efibootmgr --activate "$bootentry" > /dev/null + done +fi + +# Add boot0cfg for MBR BIOS booting? Index: head/usr.sbin/bsdinstall/scripts/zfsboot =================================================================== --- head/usr.sbin/bsdinstall/scripts/zfsboot (revision 342282) +++ head/usr.sbin/bsdinstall/scripts/zfsboot (revision 342283) @@ -1,1802 +1,1765 @@ #!/bin/sh #- # Copyright (c) 2013-2016 Allan Jude # Copyright (c) 2013-2018 Devin Teske # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # ############################################################ INCLUDES BSDCFG_SHARE="/usr/share/bsdconfig" . $BSDCFG_SHARE/common.subr || exit 1 f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/device.subr f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/password/password.subr f_include $BSDCFG_SHARE/variable.subr ############################################################ CONFIGURATION # # Default name of the boot-pool # : ${ZFSBOOT_POOL_NAME:=zroot} # # Default options to use when creating zroot pool # : ${ZFSBOOT_POOL_CREATE_OPTIONS:=-O compress=lz4 -O atime=off} # # Default name for the boot environment parent dataset # : ${ZFSBOOT_BEROOT_NAME:=ROOT} # # Default name for the primary boot environment # : ${ZFSBOOT_BOOTFS_NAME:=default} # # Default Virtual Device (vdev) type to create # : ${ZFSBOOT_VDEV_TYPE:=stripe} # # Should we use sysctl(8) vfs.zfs.min_auto_ashift=12 to force 4K sectors? # : ${ZFSBOOT_FORCE_4K_SECTORS:=1} # # Should we use geli(8) to encrypt the drives? # NB: Automatically enables ZFSBOOT_BOOT_POOL # : ${ZFSBOOT_GELI_ENCRYPTION=} # # Default path to the geli(8) keyfile used in drive encryption # : ${ZFSBOOT_GELI_KEY_FILE:=/boot/encryption.key} # # Create a separate boot pool? # NB: Automatically set when using geli(8) or MBR # : ${ZFSBOOT_BOOT_POOL=} # # Options to use when creating separate boot pool (if any) # : ${ZFSBOOT_BOOT_POOL_CREATE_OPTIONS:=} # # Default name for boot pool when enabled (e.g., geli(8) or MBR) # : ${ZFSBOOT_BOOT_POOL_NAME:=bootpool} # # Default size for boot pool when enabled (e.g., geli(8) or MBR) # : ${ZFSBOOT_BOOT_POOL_SIZE:=2g} # # Default disks to use (always empty unless being scripted) # : ${ZFSBOOT_DISKS:=} # # Default partitioning scheme to use on disks # : ${ZFSBOOT_PARTITION_SCHEME:=} # # Default boot type to use on disks # : ${ZFSBOOT_BOOT_TYPE:=} # # How much swap to put on each block device in the boot zpool # NOTE: Value passed to gpart(8); which supports SI unit suffixes. # : ${ZFSBOOT_SWAP_SIZE:=2g} # # Should we use geli(8) to encrypt the swap? # : ${ZFSBOOT_SWAP_ENCRYPTION=} # # Should we use gmirror(8) to mirror the swap? # : ${ZFSBOOT_SWAP_MIRROR=} # # Default ZFS datasets for root zpool # # NOTE: Requires /tmp, /var/tmp, /$ZFSBOOT_BOOTFS_NAME/$ZFSBOOT_BOOTFS_NAME # NOTE: Anything after pound/hash character [#] is ignored as a comment. # f_isset ZFSBOOT_DATASETS || ZFSBOOT_DATASETS=" # DATASET OPTIONS (comma or space separated; or both) # Boot Environment [BE] root and default boot dataset /$ZFSBOOT_BEROOT_NAME mountpoint=none /$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME mountpoint=/ # Compress /tmp, allow exec but not setuid /tmp mountpoint=/tmp,exec=on,setuid=off # Don't mount /usr so that 'base' files go to the BEROOT /usr mountpoint=/usr,canmount=off # Home directories separated so they are common to all BEs /usr/home # NB: /home is a symlink to /usr/home # Ports tree /usr/ports setuid=off # Source tree (compressed) /usr/src # Create /var and friends /var mountpoint=/var,canmount=off /var/audit exec=off,setuid=off /var/crash exec=off,setuid=off /var/log exec=off,setuid=off /var/mail atime=on /var/tmp setuid=off " # END-QUOTE # # If interactive and the user has not explicitly chosen a vdev type or disks, # make the user confirm scripted/default choices when proceeding to install. # : ${ZFSBOOT_CONFIRM_LAYOUT:=1} ############################################################ GLOBALS # # Format of a line in printf(1) syntax to add to fstab(5) # FSTAB_FMT="%s\t\t%s\t%s\t%s\t\t%s\t%s\n" # # Command strings for various tasks # COPY='cp "%s" "%s"' CHMOD_MODE='chmod %s "%s"' DD_WITH_OPTIONS='dd if="%s" of="%s" %s' ECHO_APPEND='echo "%s" >> "%s"' ECHO_OVERWRITE='echo "%s" > "%s"' GELI_ATTACH='geli attach -j - -k "%s" "%s"' GELI_ATTACH_NOKEY='geli attach -j - "%s"' GELI_DETACH_F='geli detach -f "%s"' GELI_PASSWORD_INIT='geli init -b -B "%s" -e %s -J - -K "%s" -l 256 -s 4096 "%s"' GELI_PASSWORD_GELIBOOT_INIT='geli init -bg -e %s -J - -l 256 -s 4096 "%s"' GPART_ADD_ALIGN='gpart add %s -t %s "%s"' GPART_ADD_ALIGN_INDEX='gpart add %s -i %s -t %s "%s"' GPART_ADD_ALIGN_INDEX_WITH_SIZE='gpart add %s -i %s -t %s -s %s "%s"' GPART_ADD_ALIGN_LABEL='gpart add %s -l %s -t %s "%s"' GPART_ADD_ALIGN_LABEL_WITH_SIZE='gpart add %s -l %s -t %s -s %s "%s"' GPART_BOOTCODE='gpart bootcode -b "%s" "%s"' GPART_BOOTCODE_PART='gpart bootcode -b "%s" -p "%s" -i %s "%s"' GPART_BOOTCODE_PARTONLY='gpart bootcode -p "%s" -i %s "%s"' GPART_CREATE='gpart create -s %s "%s"' GPART_DESTROY_F='gpart destroy -F "%s"' GPART_SET_ACTIVE='gpart set -a active -i %s "%s"' GPART_SET_LENOVOFIX='gpart set -a lenovofix "%s"' GPART_SET_PMBR_ACTIVE='gpart set -a active "%s"' GRAID_DELETE='graid delete "%s"' KLDLOAD='kldload %s' LN_SF='ln -sf "%s" "%s"' MKDIR_P='mkdir -p "%s"' MOUNT_TYPE='mount -t %s "%s" "%s"' -NEWFS_ESP='newfs_msdos -F %s -L "%s" "%s"' PRINTF_CONF="printf '%s=\"%%s\"\\\n' %s >> \"%s\"" PRINTF_FSTAB='printf "$FSTAB_FMT" "%s" "%s" "%s" "%s" "%s" "%s" >> "%s"' SHELL_TRUNCATE=':> "%s"' SWAP_GMIRROR_LABEL='gmirror label swap %s' SYSCTL_ZFS_MIN_ASHIFT_12='sysctl vfs.zfs.min_auto_ashift=12' UMOUNT='umount "%s"' ZFS_CREATE_WITH_OPTIONS='zfs create %s "%s"' ZFS_MOUNT='zfs mount "%s"' ZFS_SET='zfs set "%s" "%s"' ZFS_UNMOUNT='zfs unmount "%s"' ZPOOL_CREATE_WITH_OPTIONS='zpool create %s "%s" %s %s' ZPOOL_DESTROY='zpool destroy "%s"' ZPOOL_EXPORT='zpool export "%s"' ZPOOL_EXPORT_F='zpool export -f "%s"' ZPOOL_IMPORT_WITH_OPTIONS='zpool import %s "%s"' ZPOOL_LABELCLEAR_F='zpool labelclear -f "%s"' ZPOOL_SET='zpool set %s "%s"' # # Strings that should be moved to an i18n file and loaded with f_include_lang() # hline_alnum_arrows_punc_tab_enter="Use alnum, arrows, punctuation, TAB or ENTER" hline_arrows_space_tab_enter="Use arrows, SPACE, TAB or ENTER" hline_arrows_tab_enter="Press arrows, TAB or ENTER" msg_an_unknown_error_occurred="An unknown error occurred" msg_back="Back" msg_cancel="Cancel" msg_change_selection="Change Selection" msg_configure_options="Configure Options:" msg_detailed_disk_info="gpart(8) show %s:\n%s\n\ncamcontrol(8) inquiry %s:\n%s\n\n\ncamcontrol(8) identify %s:\n%s\n" msg_disk_info="Disk Info" msg_disk_info_help="Get detailed information on disk device(s)" msg_disk_plural="disks" msg_disk_singular="disk" msg_encrypt_disks="Encrypt Disks?" msg_encrypt_disks_help="Use geli(8) to encrypt all data partitions" msg_error="Error" msg_force_4k_sectors="Force 4K Sectors?" msg_force_4k_sectors_help="Align partitions to 4K sector boundries and set vfs.zfs.min_auto_ashift=12" msg_freebsd_installer="FreeBSD Installer" msg_geli_password="Enter a strong passphrase, used to protect your encryption keys. You will be required to enter this passphrase each time the system is booted" msg_geli_setup="Initializing encryption on selected disks,\n this will take several seconds per disk" msg_install="Install" msg_install_desc="Proceed with Installation" msg_install_help="Create ZFS boot pool with displayed options" msg_invalid_boot_pool_size="Invalid boot pool size \`%s'" msg_invalid_disk_argument="Invalid disk argument \`%s'" msg_invalid_index_argument="Invalid index argument \`%s'" msg_invalid_swap_size="Invalid swap size \`%s'" msg_invalid_virtual_device_type="Invalid Virtual Device type \`%s'" msg_last_chance_are_you_sure="Last Chance! Are you sure you want to destroy\nthe current contents of the following disks:\n\n %s" msg_last_chance_are_you_sure_color='\\ZrLast Chance!\\ZR Are you \\Z1sure\\Zn you want to \\Zr\\Z1destroy\\Zn\nthe current contents of the following disks:\n\n %s' msg_mirror_desc="Mirror - n-Way Mirroring" msg_mirror_help="[2+ Disks] Mirroring provides the best performance, but the least storage" msg_missing_disk_arguments="missing disk arguments" msg_missing_one_or_more_scripted_disks="Missing one or more scripted disks!" msg_no="NO" msg_no_disks_present_to_configure="No disk(s) present to configure" msg_no_disks_selected="No disks selected." msg_not_enough_disks_selected="Not enough disks selected. (%u < %u minimum)" msg_null_disk_argument="NULL disk argument" msg_null_index_argument="NULL index argument" msg_null_poolname="NULL poolname" msg_odd_disk_selected="An even number of disks must be selected to create a RAID 1+0. (%u selected)" msg_ok="OK" msg_partition_scheme="Partition Scheme" msg_partition_scheme_help="Select partitioning scheme. GPT is recommended." msg_please_enter_a_name_for_your_zpool="Please enter a name for your zpool:" msg_please_enter_amount_of_swap_space="Please enter amount of swap space (SI-Unit suffixes\nrecommended; e.g., \`2g' for 2 Gigabytes):" msg_please_select_one_or_more_disks="Please select one or more disks to create a zpool:" msg_pool_name="Pool Name" msg_pool_name_cannot_be_empty="Pool name cannot be empty." msg_pool_name_help="Customize the name of the zpool to be created (Required)" msg_pool_type_disks="Pool Type/Disks:" msg_pool_type_disks_help="Choose type of ZFS Virtual Device and disks to use (Required)" msg_processing_selection="Processing selection..." msg_raid10_desc="RAID 1+0 - n x 2-Way Mirrors" msg_raid10_help="[4+ Disks] Striped Mirrors provides the best performance, but the least storage" msg_raidz1_desc="RAID-Z1 - Single Redundant RAID" msg_raidz1_help="[3+ Disks] Withstand failure of 1 disk. Recommended for: 3, 5 or 9 disks" msg_raidz2_desc="RAID-Z2 - Double Redundant RAID" msg_raidz2_help="[4+ Disks] Withstand failure of 2 disks. Recommended for: 4, 6 or 10 disks" msg_raidz3_desc="RAID-Z3 - Triple Redundant RAID" msg_raidz3_help="[5+ Disks] Withstand failure of 3 disks. Recommended for: 5, 7 or 11 disks" msg_rescan_devices="Rescan Devices" msg_rescan_devices_help="Scan for device changes" msg_select="Select" msg_select_a_disk_device="Select a disk device" msg_select_virtual_device_type="Select Virtual Device type:" msg_stripe_desc="Stripe - No Redundancy" msg_stripe_help="[1+ Disks] Striping provides maximum storage but no redundancy" msg_swap_encrypt="Encrypt Swap?" msg_swap_encrypt_help="Encrypt swap partitions with temporary keys, discarded on reboot" msg_swap_invalid="The selected swap size (%s) is invalid. Enter a number optionally followed by units. Example: 2G" msg_swap_mirror="Mirror Swap?" msg_swap_mirror_help="Mirror swap partitions for redundancy, breaks crash dumps" msg_swap_size="Swap Size" msg_swap_size_help="Customize how much swap space is allocated to each selected disk" msg_swap_toosmall="The selected swap size (%s) is to small. Please enter a value greater than 100MB or enter 0 for no swap" msg_these_disks_are_too_small="These disks are smaller than the amount of requested\nswap (%s) and/or geli(8) (%s) partitions, which would\ntake 100%% or more of each of the following selected disks:\n\n %s\n\nRecommend changing partition size(s) and/or selecting a\ndifferent set of disks." msg_unable_to_get_disk_capacity="Unable to get disk capacity of \`%s'" msg_unsupported_partition_scheme="%s is an unsupported partition scheme" msg_user_cancelled="User Cancelled." msg_yes="YES" msg_zfs_configuration="ZFS Configuration" ############################################################ FUNCTIONS # dialog_menu_main # # Display the dialog(1)-based application main menu. # dialog_menu_main() { local title="$DIALOG_TITLE" local btitle="$DIALOG_BACKTITLE" local prompt="$msg_configure_options" local force4k="$msg_no" local usegeli="$msg_no" local swapgeli="$msg_no" local swapmirror="$msg_no" [ "$ZFSBOOT_FORCE_4K_SECTORS" ] && force4k="$msg_yes" [ "$ZFSBOOT_GELI_ENCRYPTION" ] && usegeli="$msg_yes" [ "$ZFSBOOT_SWAP_ENCRYPTION" ] && swapgeli="$msg_yes" [ "$ZFSBOOT_SWAP_MIRROR" ] && swapmirror="$msg_yes" local disks n disks_grammar f_count n $ZFSBOOT_DISKS { [ $n -eq 1 ] && disks_grammar=$msg_disk_singular; } || disks_grammar=$msg_disk_plural # grammar local menu_list=" '>>> $msg_install' '$msg_install_desc' '$msg_install_help' 'T $msg_pool_type_disks' '$ZFSBOOT_VDEV_TYPE: $n $disks_grammar' '$msg_pool_type_disks_help' '- $msg_rescan_devices' '*' '$msg_rescan_devices_help' '- $msg_disk_info' '*' '$msg_disk_info_help' 'N $msg_pool_name' '$ZFSBOOT_POOL_NAME' '$msg_pool_name_help' '4 $msg_force_4k_sectors' '$force4k' '$msg_force_4k_sectors_help' 'E $msg_encrypt_disks' '$usegeli' '$msg_encrypt_disks_help' 'P $msg_partition_scheme' '$ZFSBOOT_PARTITION_SCHEME ($ZFSBOOT_BOOT_TYPE)' '$msg_partition_scheme_help' 'S $msg_swap_size' '$ZFSBOOT_SWAP_SIZE' '$msg_swap_size_help' 'M $msg_swap_mirror' '$swapmirror' '$msg_swap_mirror_help' 'W $msg_swap_encrypt' '$swapgeli' '$msg_swap_encrypt_help' " # END-QUOTE local defaultitem= # Calculated below local hline="$hline_alnum_arrows_punc_tab_enter" local height width rows eval f_dialog_menu_with_help_size height width rows \ \"\$title\" \"\$btitle\" \"\$prompt\" \"\$hline\" $menu_list # Obtain default-item from previously stored selection f_dialog_default_fetch defaultitem local menu_choice menu_choice=$( eval $DIALOG \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --hline \"\$hline\" \ --item-help \ --ok-label \"\$msg_select\" \ --cancel-label \"\$msg_cancel\" \ --default-item \"\$defaultitem\" \ --menu \"\$prompt\" \ $height $width $rows \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) local retval=$? f_dialog_data_sanitize menu_choice f_dialog_menutag_store "$menu_choice" # Only update default-item on success [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } # dialog_last_chance $disks ... # # Display a list of the disks that the user is about to destroy. The default # action is to return error status unless the user explicitly (non-default) # selects "Yes" from the noyes dialog. # dialog_last_chance() { local title="$DIALOG_TITLE" local btitle="$DIALOG_BACKTITLE" local prompt # Calculated below local hline="$hline_arrows_tab_enter" local height=8 width=50 prefix=" " local plen=${#prefix} list= line= local max_width=$(( $width - 3 - $plen )) local yes no defaultno extra_args format if [ "$USE_XDIALOG" ]; then yes=ok no=cancel defaultno=default-no extra_args="--wrap --left" format="$msg_last_chance_are_you_sure" else yes=yes no=no defaultno=defaultno extra_args="--colors --cr-wrap" format="$msg_last_chance_are_you_sure_color" fi local disk line_width for disk in $*; do if [ "$line" ]; then line_width=${#line} else line_width=$plen fi line_width=$(( $line_width + 1 + ${#disk} )) # Add newline before disk if it would exceed max_width if [ $line_width -gt $max_width ]; then list="$list$line\n" line="$prefix" height=$(( $height + 1 )) fi # Add the disk to the list line="$line $disk" done # Append the left-overs if [ "${line#$prefix}" ]; then list="$list$line" height=$(( $height + 1 )) fi # Add height for Xdialog(1) [ "$USE_XDIALOG" ] && height=$(( $height + $height / 5 + 3 )) prompt=$( printf "$format" "$list" ) f_dprintf "%s: Last Chance!" "$0" $DIALOG \ --title "$title" \ --backtitle "$btitle" \ --hline "$hline" \ --$defaultno \ --$yes-label "$msg_yes" \ --$no-label "$msg_no" \ $extra_args \ --yesno "$prompt" $height $width } # dialog_menu_layout # # Configure Virtual Device type and disks to use for the ZFS boot pool. User # must select enough disks to satisfy the chosen vdev type. # dialog_menu_layout() { local funcname=dialog_menu_layout local title="$DIALOG_TITLE" local btitle="$DIALOG_BACKTITLE" local vdev_prompt="$msg_select_virtual_device_type" local disk_prompt="$msg_please_select_one_or_more_disks" local vdev_menu_list=" 'stripe' '$msg_stripe_desc' '$msg_stripe_help' 'mirror' '$msg_mirror_desc' '$msg_mirror_help' 'raid10' '$msg_raid10_desc' '$msg_raid10_help' 'raidz1' '$msg_raidz1_desc' '$msg_raidz1_help' 'raidz2' '$msg_raidz2_desc' '$msg_raidz2_help' 'raidz3' '$msg_raidz3_desc' '$msg_raidz3_help' " # END-QUOTE local disk_check_list= # Calculated below local vdev_hline="$hline_arrows_tab_enter" local disk_hline="$hline_arrows_space_tab_enter" # Warn the user if vdev type is not valid case "$ZFSBOOT_VDEV_TYPE" in stripe|mirror|raid10|raidz1|raidz2|raidz3) : known good ;; *) f_dprintf "%s: Invalid virtual device type \`%s'" \ $funcname "$ZFSBOOT_VDEV_TYPE" f_show_err "$msg_invalid_virtual_device_type" \ "$ZFSBOOT_VDEV_TYPE" f_interactive || return $FAILURE esac # Calculate size of vdev menu once only local vheight vwidth vrows eval f_dialog_menu_with_help_size vheight vwidth vrows \ \"\$title\" \"\$btitle\" \"\$vdev_prompt\" \"\$vdev_hline\" \ $vdev_menu_list # Get a list of probed disk devices local disks= debug= f_device_find "" $DEVICE_TYPE_DISK disks # Prune out mounted md(4) devices that may be part of the boot process local disk name new_list= for disk in $disks; do debug= $disk get name name case "$name" in md[0-9]*) f_mounted -b "/dev/$name" && continue ;; esac new_list="$new_list $disk" done disks="${new_list# }" # Debugging if [ "$debug" ]; then local disk_names= for disk in $disks; do debug= $disk get name name disk_names="$disk_names $name" done f_dprintf "$funcname: disks=[%s]" "${disk_names# }" fi if [ ! "$disks" ]; then f_dprintf "No disk(s) present to configure" f_show_err "$msg_no_disks_present_to_configure" return $FAILURE fi # Lets sort the disks array to be more user friendly f_device_sort_by name disks disks # # Operate in a loop so we can (if interactive) repeat if not enough # disks are selected to satisfy the chosen vdev type or user wants to # back-up to the previous menu. # local vardisk ndisks onoff selections vdev_choice breakout device local valid_disks all_valid want_disks desc height width rows while :; do # # Confirm the vdev type that was selected # if f_interactive && [ "$ZFSBOOT_CONFIRM_LAYOUT" ]; then vdev_choice=$( eval $DIALOG \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --hline \"\$vdev_hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --item-help \ --default-item \"\$ZFSBOOT_VDEV_TYPE\" \ --menu \"\$vdev_prompt\" \ $vheight $vwidth $vrows \ $vdev_menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) || return $? # Exit if user pressed ESC or chose Cancel/No f_dialog_data_sanitize vdev_choice ZFSBOOT_VDEV_TYPE="$vdev_choice" f_dprintf "$funcname: ZFSBOOT_VDEV_TYPE=[%s]" \ "$ZFSBOOT_VDEV_TYPE" fi # Determine the number of disks needed for this vdev type want_disks=0 case "$ZFSBOOT_VDEV_TYPE" in stripe) want_disks=1 ;; mirror) want_disks=2 ;; raid10) want_disks=4 ;; raidz1) want_disks=3 ;; raidz2) want_disks=4 ;; raidz3) want_disks=5 ;; esac # # Warn the user if any scripted disks are invalid # valid_disks= all_valid=${ZFSBOOT_DISKS:+1} # optimism for disk in $ZFSBOOT_DISKS; do if debug= f_device_find -1 \ $disk $DEVICE_TYPE_DISK device then valid_disks="$valid_disks $disk" continue fi f_dprintf "$funcname: \`%s' is not a real disk" "$disk" all_valid= done if [ ! "$all_valid" ]; then if [ "$ZFSBOOT_DISKS" ]; then f_show_err \ "$msg_missing_one_or_more_scripted_disks" else f_dprintf "No disks selected." f_interactive || f_show_err "$msg_no_disks_selected" fi f_interactive || return $FAILURE fi ZFSBOOT_DISKS="${valid_disks# }" # # Short-circuit if we're running non-interactively # if ! f_interactive || [ ! "$ZFSBOOT_CONFIRM_LAYOUT" ]; then f_count ndisks $ZFSBOOT_DISKS [ $ndisks -ge $want_disks ] && break # to success # Not enough disks selected f_dprintf "$funcname: %s: %s (%u < %u minimum)" \ "$ZFSBOOT_VDEV_TYPE" \ "Not enough disks selected." \ $ndisks $want_disks f_interactive || return $FAILURE msg_yes="$msg_change_selection" msg_no="$msg_cancel" \ f_yesno "%s: $msg_not_enough_disks_selected" \ "$ZFSBOOT_VDEV_TYPE" $ndisks $want_disks || return $FAILURE fi # # Confirm the disks that were selected # Loop until the user cancels or selects enough disks # breakout= while :; do # Loop over list of available disks, resetting state for disk in $disks; do f_isset _${disk}_status && _${disk}_status= done # Loop over list of selected disks and create temporary # locals to map statuses onto up-to-date list of disks for disk in $ZFSBOOT_DISKS; do debug= f_device_find -1 \ $disk $DEVICE_TYPE_DISK disk f_isset _${disk}_status || local _${disk}_status _${disk}_status=on done # Create the checklist menu of discovered disk devices disk_check_list= for disk in $disks; do desc= $disk get name name $disk get desc desc f_shell_escape "$desc" desc f_getvar _${disk}_status:-off onoff disk_check_list="$disk_check_list $name '$desc' $onoff" done eval f_dialog_checklist_size height width rows \ \"\$title\" \"\$btitle\" \"\$prompt\" \ \"\$hline\" $disk_check_list selections=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --separate-output \ --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_back\" \ --checklist \"\$prompt\" \ $height $width $rows \ $disk_check_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) || break # Loop if user pressed ESC or chose Cancel/No f_dialog_data_sanitize selections ZFSBOOT_DISKS="$selections" f_dprintf "$funcname: ZFSBOOT_DISKS=[%s]" \ "$ZFSBOOT_DISKS" f_count ndisks $ZFSBOOT_DISKS if [ "$ZFSBOOT_VDEV_TYPE" == "raid10" ] && [ $(( $ndisks % 2 )) -ne 0 ]; then f_dprintf "$funcname: %s: %s (%u %% 2 = %u)" \ "$ZFSBOOT_VDEV_TYPE" \ "Number of disks not even:" \ $ndisks $(( $ndisks % 2 )) msg_yes="$msg_change_selection" \ msg_no="$msg_cancel" \ f_yesno "%s: $msg_odd_disk_selected" \ "$ZFSBOOT_VDEV_TYPE" $ndisks || break continue fi [ $ndisks -ge $want_disks ] && breakout=break && break # Not enough disks selected f_dprintf "$funcname: %s: %s (%u < %u minimum)" \ "$ZFSBOOT_VDEV_TYPE" \ "Not enough disks selected." \ $ndisks $want_disks msg_yes="$msg_change_selection" msg_no="$msg_cancel" \ f_yesno "%s: $msg_not_enough_disks_selected" \ "$ZFSBOOT_VDEV_TYPE" $ndisks $want_disks || break done [ "$breakout" = "break" ] && break [ "$ZFSBOOT_CONFIRM_LAYOUT" ] || return $FAILURE done return $DIALOG_OK } # zfs_create_diskpart $disk $index # # For each block device to be used in the zpool, rather than just create the # zpool with the raw block devices (e.g., da0, da1, etc.) we create partitions # so we can have some real swap. This also provides wiggle room incase your # replacement drivers do not have the exact same sector counts. # # NOTE: $swapsize and $bootsize should be defined by the calling function. # NOTE: Sets $bootpart and $targetpart for the calling function. # zfs_create_diskpart() { local funcname=zfs_create_diskpart local disk="$1" index="$2" # Check arguments if [ ! "$disk" ]; then f_dprintf "$funcname: NULL disk argument" msg_error="$msg_error: $funcname" \ f_show_err "$msg_null_disk_argument" return $FAILURE fi if [ "${disk#*[$IFS]}" != "$disk" ]; then f_dprintf "$funcname: Invalid disk argument \`%s'" "$disk" msg_error="$msg_error: $funcname" \ f_show_err "$msg_invalid_disk_argument" "$disk" return $FAILURE fi if [ ! "$index" ]; then f_dprintf "$funcname: NULL index argument" msg_error="$msg_error: $funcname" \ f_show_err "$msg_null_index_argument" return $FAILURE fi if ! f_isinteger "$index"; then f_dprintf "$funcname: Invalid index argument \`%s'" "$index" msg_error="$msg_error: $funcname" \ f_show_err "$msg_invalid_index_argument" "$index" return $FAILURE fi f_dprintf "$funcname: disk=[%s] index=[%s]" "$disk" "$index" # Check for unknown partition scheme before proceeding further case "$ZFSBOOT_PARTITION_SCHEME" in ""|MBR|GPT*) : known good ;; *) f_dprintf "$funcname: %s is an unsupported partition scheme" \ "$ZFSBOOT_PARTITION_SCHEME" msg_error="$msg_error: $funcname" f_show_err \ "$msg_unsupported_partition_scheme" \ "$ZFSBOOT_PARTITION_SCHEME" return $FAILURE esac # # Destroy whatever partition layout is currently on disk. # NOTE: `-F' required to destroy if partitions still exist. # NOTE: Failure is ok here, blank disk will have nothing to destroy. # f_dprintf "$funcname: Exporting ZFS pools..." zpool list -Ho name | while read z_name; do f_eval_catch -d $funcname zpool "$ZPOOL_EXPORT_F" $z_name done f_dprintf "$funcname: Detaching all GELI providers..." geli status | tail -n +2 | while read g_name g_status g_component; do f_eval_catch -d $funcname geli "$GELI_DETACH_F" $g_name done f_dprintf "$funcname: Destroying all data/layouts on \`%s'..." "$disk" f_eval_catch -d $funcname gpart "$GPART_DESTROY_F" $disk f_eval_catch -d $funcname graid "$GRAID_DELETE" $disk f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" /dev/$disk # Make doubly-sure backup GPT is destroyed f_eval_catch -d $funcname gpart "$GPART_CREATE" gpt $disk f_eval_catch -d $funcname gpart "$GPART_DESTROY_F" $disk # # Lay down the desired type of partition scheme # local setsize mbrindex align_small align_big # # If user has requested 4 K alignment, add these params to the # gpart add calls. With GPT, we align large partitions to 1 M for # improved performance on SSDs. MBR does not always play well with gaps # between partitions, so all alignment is only 4k for that case. # With MBR, we align the BSD partition that contains the MBR, otherwise # the system fails to boot. # if [ "$ZFSBOOT_FORCE_4K_SECTORS" ]; then align_small="-a 4k" align_big="-a 1m" fi case "$ZFSBOOT_PARTITION_SCHEME" in ""|GPT*) f_dprintf "$funcname: Creating GPT layout..." # # 1. Create GPT layout using labels # f_eval_catch $funcname gpart "$GPART_CREATE" gpt $disk || return $FAILURE # # Apply workarounds if requested by the user # if [ "$ZFSBOOT_PARTITION_SCHEME" = "GPT + Lenovo Fix" ]; then f_eval_catch $funcname gpart "$GPART_SET_LENOVOFIX" \ $disk || return $FAILURE elif [ "$ZFSBOOT_PARTITION_SCHEME" = "GPT + Active" ]; then f_eval_catch $funcname gpart "$GPART_SET_PMBR_ACTIVE" \ $disk || return $FAILURE fi # # 2. Add small freebsd-boot and/or efi partition # if [ "$ZFSBOOT_BOOT_TYPE" = "UEFI" -o \ "$ZFSBOOT_BOOT_TYPE" = "BIOS+UEFI" ] then f_eval_catch $funcname gpart \ "$GPART_ADD_ALIGN_LABEL_WITH_SIZE" \ "$align_small" efiboot$index efi 200M \ $disk || return $FAILURE - f_eval_catch $funcname mkdir "$MKDIR_P" \ - "$BSDINSTALL_TMPETC/esp" || - return $FAILURE - f_eval_catch $funcname newfs_msdos "$NEWFS_ESP" "16" \ - "EFISYS" "/dev/${disk}p1" || - return $FAILURE - f_eval_catch $funcname mount "$MOUNT_TYPE" "msdosfs" \ - "/dev/${disk}p1" \ - "$BSDINSTALL_TMPETC/esp" || - return $FAILURE - f_eval_catch $funcname mkdir "$MKDIR_P" \ - "$BSDINSTALL_TMPETC/esp/efi/boot" || - return $FAILURE - f_eval_catch $funcname cp "$COPY" "/boot/loader.efi" \ - "$BSDINSTALL_TMPETC/esp/efi/boot/$ZFSBOOT_ESP_NAME" || - return $FAILURE - f_eval_catch $funcname echo "$ECHO_OVERWRITE" \ - "$ZFSBOOT_ESP_NAME" \ - "$BSDINSTALL_TMPETC/esp/efi/boot/startup.nsh" || - return $FAILURE - f_eval_catch $funcname umount "$UMOUNT" \ - "$BSDINSTALL_TMPETC/esp" || - return $FAILURE + # We'll configure the ESP in bootconfig fi if [ "$ZFSBOOT_BOOT_TYPE" = "BIOS" -o \ "$ZFSBOOT_BOOT_TYPE" = "BIOS+UEFI" ] then f_eval_catch $funcname gpart \ "$GPART_ADD_ALIGN_LABEL_WITH_SIZE" \ "$align_small" gptboot$index \ freebsd-boot 512k $disk || return $FAILURE if [ "$ZFSBOOT_BOOT_TYPE" = "BIOS" ]; then f_eval_catch $funcname gpart \ "$GPART_BOOTCODE_PART" \ /boot/pmbr /boot/gptzfsboot 1 \ $disk || return $FAILURE else f_eval_catch $funcname gpart \ "$GPART_BOOTCODE_PART" \ /boot/pmbr /boot/gptzfsboot 2 \ $disk || return $FAILURE fi fi # NB: zpool will use the `zfs#' GPT labels if [ "$ZFSBOOT_BOOT_TYPE" = "BIOS+UEFI" ]; then if [ "$ZFSBOOT_BOOT_POOL" ]; then bootpart=p3 swappart=p4 targetpart=p4 [ ${swapsize:-0} -gt 0 ] && targetpart=p5 else # Bootpart unused bootpart=p3 swappart=p3 targetpart=p3 [ ${swapsize:-0} -gt 0 ] && targetpart=p4 fi else if [ "$ZFSBOOT_BOOT_POOL" ]; then bootpart=p2 swappart=p3 targetpart=p3 [ ${swapsize:-0} -gt 0 ] && targetpart=p4 else # Bootpart unused bootpart=p2 swappart=p2 targetpart=p2 [ ${swapsize:-0} -gt 0 ] && targetpart=p3 fi fi # # Prepare boot pool if enabled (e.g., for geli(8)) # if [ "$ZFSBOOT_BOOT_POOL" ]; then f_eval_catch $funcname gpart \ "$GPART_ADD_ALIGN_LABEL_WITH_SIZE" \ "$align_big" boot$index freebsd-zfs \ ${bootsize}b $disk || return $FAILURE # Pedantically nuke any old labels f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/$disk$bootpart if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then # Pedantically detach targetpart for later f_eval_catch -d $funcname geli \ "$GELI_DETACH_F" \ /dev/$disk$targetpart fi fi # # 3. Add freebsd-swap partition labeled `swap#' # if [ ${swapsize:-0} -gt 0 ]; then f_eval_catch $funcname gpart \ "$GPART_ADD_ALIGN_LABEL_WITH_SIZE" \ "$align_big" swap$index freebsd-swap \ ${swapsize}b $disk || return $FAILURE # Pedantically nuke any old labels on the swap f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/$disk$swappart fi # # 4. Add freebsd-zfs partition labeled `zfs#' for zroot # f_eval_catch $funcname gpart "$GPART_ADD_ALIGN_LABEL" \ "$align_big" zfs$index freebsd-zfs $disk || return $FAILURE f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/$disk$targetpart ;; MBR) f_dprintf "$funcname: Creating MBR layout..." # # Enable boot pool if encryption is desired # [ "$ZFSBOOT_GELI_ENCRYPTION" ] && ZFSBOOT_BOOT_POOL=1 # # 1. Create MBR layout (no labels) # f_eval_catch $funcname gpart "$GPART_CREATE" mbr $disk || return $FAILURE f_eval_catch $funcname gpart "$GPART_BOOTCODE" /boot/mbr \ $disk || return $FAILURE # # 2. Add freebsd slice with all available space # f_eval_catch $funcname gpart "$GPART_ADD_ALIGN" \ "$align_small" freebsd $disk || return $FAILURE f_eval_catch $funcname gpart "$GPART_SET_ACTIVE" 1 $disk || return $FAILURE # Pedantically nuke any old labels f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/${disk}s1 # Pedantically nuke any old scheme f_eval_catch -d $funcname gpart "$GPART_DESTROY_F" ${disk}s1 # # 3. Write BSD scheme to the freebsd slice # f_eval_catch $funcname gpart "$GPART_CREATE" BSD ${disk}s1 || return $FAILURE # NB: zpool will use s1a (no labels) bootpart=s1a swappart=s1b targetpart=s1d mbrindex=4 # # Always prepare a boot pool on MBR # Do not align this partition, there must not be a gap # ZFSBOOT_BOOT_POOL=1 f_eval_catch $funcname gpart \ "$GPART_ADD_ALIGN_INDEX_WITH_SIZE" \ "" 1 freebsd-zfs ${bootsize}b ${disk}s1 || return $FAILURE # Pedantically nuke any old labels f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/$disk$bootpart if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then # Pedantically detach targetpart for later f_eval_catch -d $funcname geli \ "$GELI_DETACH_F" \ /dev/$disk$targetpart fi # # 4. Add freebsd-swap partition # if [ ${swapsize:-0} -gt 0 ]; then f_eval_catch $funcname gpart \ "$GPART_ADD_ALIGN_INDEX_WITH_SIZE" \ "$align_small" 2 freebsd-swap \ ${swapsize}b ${disk}s1 || return $FAILURE # Pedantically nuke any old labels on the swap f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/${disk}s1b fi # # 5. Add freebsd-zfs partition for zroot # f_eval_catch $funcname gpart "$GPART_ADD_ALIGN_INDEX" \ "$align_small" $mbrindex freebsd-zfs ${disk}s1 || return $FAILURE f_eval_catch -d $funcname zpool "$ZPOOL_LABELCLEAR_F" \ /dev/$disk$targetpart # Pedantic f_eval_catch $funcname dd "$DD_WITH_OPTIONS" \ /boot/zfsboot /dev/${disk}s1 count=1 || return $FAILURE ;; esac # $ZFSBOOT_PARTITION_SCHEME # Update fstab(5) local swapsize f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize if [ "$isswapmirror" ]; then # This is not the first disk in the mirror, do nothing elif [ ${swapsize:-0} -eq 0 ]; then # If swap is 0 sized, don't add it to fstab elif [ "$ZFSBOOT_SWAP_ENCRYPTION" -a "$ZFSBOOT_SWAP_MIRROR" ]; then f_eval_catch $funcname printf "$PRINTF_FSTAB" \ /dev/mirror/swap.eli none swap sw 0 0 \ $BSDINSTALL_TMPETC/fstab || return $FAILURE isswapmirror=1 elif [ "$ZFSBOOT_SWAP_MIRROR" ]; then f_eval_catch $funcname printf "$PRINTF_FSTAB" \ /dev/mirror/swap none swap sw 0 0 \ $BSDINSTALL_TMPETC/fstab || return $FAILURE isswapmirror=1 elif [ "$ZFSBOOT_SWAP_ENCRYPTION" ]; then f_eval_catch $funcname printf "$PRINTF_FSTAB" \ /dev/$disk${swappart}.eli none swap sw 0 0 \ $BSDINSTALL_TMPETC/fstab || return $FAILURE else f_eval_catch $funcname printf "$PRINTF_FSTAB" \ /dev/$disk$swappart none swap sw 0 0 \ $BSDINSTALL_TMPETC/fstab || return $FAILURE fi return $SUCCESS } # zfs_create_boot $poolname $vdev_type $disks ... # # Creates boot pool and dataset layout. Returns error if something goes wrong. # Errors are printed to stderr for collection and display. # zfs_create_boot() { local funcname=zfs_create_boot local zroot_name="$1" local zroot_vdevtype="$2" local zroot_vdevs= # Calculated below local swap_devs= # Calculated below local boot_vdevs= # Used for geli(8) and/or MBR layouts shift 2 # poolname vdev_type local disks="$*" disk local isswapmirror local bootpart targetpart swappart # Set by zfs_create_diskpart() below local create_options # # Pedantic checks; should never be seen # if [ ! "$zroot_name" ]; then f_dprintf "$funcname: NULL poolname" msg_error="$msg_error: $funcname" \ f_show_err "$msg_null_poolname" return $FAILURE fi if [ $# -lt 1 ]; then f_dprintf "$funcname: missing disk arguments" msg_error="$msg_error: $funcname" \ f_show_err "$msg_missing_disk_arguments" return $FAILURE fi f_dprintf "$funcname: poolname=[%s] vdev_type=[%s]" \ "$zroot_name" "$zroot_vdevtype" # # Initialize fstab(5) # f_dprintf "$funcname: Initializing temporary fstab(5) file..." f_eval_catch $funcname sh "$SHELL_TRUNCATE" $BSDINSTALL_TMPETC/fstab || return $FAILURE f_eval_catch $funcname printf "$PRINTF_FSTAB" \ "# Device" Mountpoint FStype Options Dump "Pass#" \ $BSDINSTALL_TMPETC/fstab || return $FAILURE # # Expand SI units in desired sizes # f_dprintf "$funcname: Expanding supplied size values..." local swapsize bootsize if ! f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize; then f_dprintf "$funcname: Invalid swap size \`%s'" \ "$ZFSBOOT_SWAP_SIZE" f_show_err "$msg_invalid_swap_size" "$ZFSBOOT_SWAP_SIZE" return $FAILURE fi if ! f_expand_number "$ZFSBOOT_BOOT_POOL_SIZE" bootsize; then f_dprintf "$funcname: Invalid boot pool size \`%s'" \ "$ZFSBOOT_BOOT_POOL_SIZE" f_show_err "$msg_invalid_boot_pool_size" \ "$ZFSBOOT_BOOT_POOL_SIZE" return $FAILURE fi f_dprintf "$funcname: ZFSBOOT_SWAP_SIZE=[%s] swapsize=[%s]" \ "$ZFSBOOT_SWAP_SIZE" "$swapsize" f_dprintf "$funcname: ZFSBOOT_BOOT_POOL_SIZE=[%s] bootsize=[%s]" \ "$ZFSBOOT_BOOT_POOL_SIZE" "$bootsize" # # Destroy the pool in-case this is our second time 'round (case of # failure and installer presented ``Retry'' option to come back). # # NB: If we don't destroy the pool, later gpart(8) destroy commands # that try to clear existing partitions (see zfs_create_diskpart()) # will fail with a `Device Busy' error, leading to `GEOM exists'. # f_eval_catch -d $funcname zpool "$ZPOOL_DESTROY" "$zroot_name" # # Prepare the disks and build pool device list(s) # f_dprintf "$funcname: Preparing disk partitions for ZFS pool..." # Force 4K sectors using vfs.zfs.min_auto_ashift=12 if [ "$ZFSBOOT_FORCE_4K_SECTORS" ]; then f_dprintf "$funcname: With 4K sectors..." f_eval_catch $funcname sysctl "$SYSCTL_ZFS_MIN_ASHIFT_12" \ || return $FAILURE sysctl kern.geom.part.mbr.enforce_chs=0 fi local n=0 for disk in $disks; do zfs_create_diskpart $disk $n || return $FAILURE # Now $bootpart, $targetpart, and $swappart are set (suffix # for $disk) if [ "$ZFSBOOT_BOOT_POOL" ]; then boot_vdevs="$boot_vdevs $disk$bootpart" fi zroot_vdevs="$zroot_vdevs $disk$targetpart" if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then zroot_vdevs="$zroot_vdevs.eli" fi n=$(( $n + 1 )) done # disks # # If we need/want a boot pool, create it # if [ "$ZFSBOOT_BOOT_POOL" ]; then local bootpool_vdevtype= # Calculated below local bootpool_options= # Calculated below local bootpool_name="$ZFSBOOT_BOOT_POOL_NAME" local bootpool="$BSDINSTALL_CHROOT/$bootpool_name" local zroot_key="${ZFSBOOT_GELI_KEY_FILE#/}" f_dprintf "$funcname: Setting up boot pool..." [ "$ZFSBOOT_GELI_ENCRYPTION" ] && f_dprintf "$funcname: For encrypted root disk..." # Create parent directory for boot pool f_eval_catch -d $funcname umount "$UMOUNT" /mnt f_eval_catch $funcname mount "$MOUNT_TYPE" tmpfs none \ $BSDINSTALL_CHROOT || return $FAILURE # Create mirror across the boot partition on all disks local nvdevs f_count nvdevs $boot_vdevs [ $nvdevs -gt 1 ] && bootpool_vdevtype=mirror create_options="$ZFSBOOT_BOOT_POOL_CREATE_OPTIONS" bootpool_options="-o altroot=$BSDINSTALL_CHROOT" bootpool_options="$bootpool_options $create_options" bootpool_options="$bootpool_options -m \"/$bootpool_name\" -f" f_eval_catch $funcname zpool "$ZPOOL_CREATE_WITH_OPTIONS" \ "$bootpool_options" "$bootpool_name" \ "$bootpool_vdevtype" "$boot_vdevs" || return $FAILURE f_eval_catch $funcname mkdir "$MKDIR_P" "$bootpool/boot" || return $FAILURE if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then # Generate an encryption key using random(4) f_eval_catch $funcname dd "$DD_WITH_OPTIONS" \ /dev/random "$bootpool/$zroot_key" \ "bs=4096 count=1" || return $FAILURE f_eval_catch $funcname chmod "$CHMOD_MODE" \ go-wrx "$bootpool/$zroot_key" || return $FAILURE fi fi # # Create the geli(8) GEOMS # if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then # # Load the AES-NI kernel module to accelerate encryption # f_eval_catch -d $funcname kldload "$KLDLOAD" "aesni" # Prompt user for password (twice) if ! msg_enter_new_password="$msg_geli_password" \ f_dialog_input_password then f_dprintf "$funcname: User cancelled" f_show_err "$msg_user_cancelled" return $FAILURE fi # Initialize geli(8) on each of the target partitions for disk in $disks; do f_dialog_info "$msg_geli_setup" \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD if [ "$ZFSBOOT_BOOT_POOL" ]; then if ! echo "$pw_password" | f_eval_catch \ $funcname geli "$GELI_PASSWORD_INIT" \ "$bootpool/boot/$disk$targetpart.eli" \ AES-XTS "$bootpool/$zroot_key" \ $disk$targetpart then f_interactive || f_die unset pw_password # Sensitive info return $FAILURE fi if ! echo "$pw_password" | f_eval_catch \ $funcname geli "$GELI_ATTACH" \ "$bootpool/$zroot_key" $disk$targetpart then f_interactive || f_die unset pw_password # Sensitive info return $FAILURE fi else # With no bootpool, there is no place to store # the key files, use only a password if ! echo "$pw_password" | f_eval_catch \ $funcname geli \ "$GELI_PASSWORD_GELIBOOT_INIT" \ AES-XTS $disk$targetpart then f_interactive || f_die unset pw_password # Sensitive info return $FAILURE fi if ! echo "$pw_password" | f_eval_catch \ $funcname geli "$GELI_ATTACH_NOKEY" \ $disk$targetpart then f_interactive || f_die unset pw_password # Sensitive info return $FAILURE fi fi done unset pw_password # Sensitive info fi if [ "$ZFSBOOT_BOOT_POOL" ]; then # Clean up f_eval_catch $funcname zfs "$ZFS_UNMOUNT" "$bootpool_name" || return $FAILURE f_eval_catch -d $funcname umount "$UMOUNT" /mnt # tmpfs fi # # Create the gmirror(8) GEOMS for swap # if [ ${swapsize:-0} -gt 0 -a "$ZFSBOOT_SWAP_MIRROR" ]; then for disk in $disks; do swap_devs="$swap_devs $disk$swappart" done f_eval_catch $funcname gmirror "$SWAP_GMIRROR_LABEL" \ "$swap_devs" || return $FAILURE fi # # Create the ZFS root pool with desired type and disk devices # f_dprintf "$funcname: Creating root pool..." create_options="$ZFSBOOT_POOL_CREATE_OPTIONS" if [ "$zroot_vdevtype" == "raid10" ]; then raid10_vdevs="" for vdev in $zroot_vdevs; do f_count nvdev $raid10_vdevs if [ $(( $nvdev % 3 )) -eq 0 ]; then raid10_vdevs="$raid10_vdevs mirror" fi raid10_vdevs="$raid10_vdevs $vdev" done f_eval_catch $funcname zpool "$ZPOOL_CREATE_WITH_OPTIONS" \ "-o altroot=$BSDINSTALL_CHROOT $create_options -m none -f" \ "$zroot_name" "" "$raid10_vdevs" || return $FAILURE else f_eval_catch $funcname zpool "$ZPOOL_CREATE_WITH_OPTIONS" \ "-o altroot=$BSDINSTALL_CHROOT $create_options -m none -f" \ "$zroot_name" "$zroot_vdevtype" "$zroot_vdevs" || return $FAILURE fi # # Create ZFS dataset layout within the new root pool # f_dprintf "$funcname: Creating ZFS datasets..." echo "$ZFSBOOT_DATASETS" | while read dataset options; do # Skip blank lines and comments case "$dataset" in "#"*|"") continue; esac # Remove potential inline comments in options options="${options%%#*}" # Replace tabs with spaces f_replaceall "$options" " " " " options # Reduce contiguous runs of space to one single space oldoptions= while [ "$oldoptions" != "$options" ]; do oldoptions="$options" f_replaceall "$options" " " " " options done # Replace both commas and spaces with ` -o ' f_replaceall "$options" "[ ,]" " -o " options # Create the dataset with desired options f_eval_catch $funcname zfs "$ZFS_CREATE_WITH_OPTIONS" \ "${options:+-o $options}" "$zroot_name$dataset" || return $FAILURE done # # Set a mountpoint for the root of the pool so newly created datasets # have a mountpoint to inherit # f_dprintf "$funcname: Setting mountpoint for root of the pool..." f_eval_catch $funcname zfs "$ZFS_SET" \ "mountpoint=/$zroot_name" "$zroot_name" || return $FAILURE # Touch up permissions on the tmp directories f_dprintf "$funcname: Modifying directory permissions..." local dir for dir in /tmp /var/tmp; do f_eval_catch $funcname mkdir "$MKDIR_P" \ $BSDINSTALL_CHROOT$dir || return $FAILURE f_eval_catch $funcname chmod "$CHMOD_MODE" 1777 \ $BSDINSTALL_CHROOT$dir || return $FAILURE done # Set bootfs property local zroot_bootfs="$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME" f_dprintf "$funcname: Setting bootfs property..." f_eval_catch $funcname zpool "$ZPOOL_SET" \ "bootfs=\"$zroot_name/$zroot_bootfs\"" "$zroot_name" || return $FAILURE # MBR boot loader touch-up if [ "$ZFSBOOT_PARTITION_SCHEME" = "MBR" ]; then # Export the pool(s) f_dprintf "$funcname: Temporarily exporting ZFS pool(s)..." f_eval_catch $funcname zpool "$ZPOOL_EXPORT" "$zroot_name" || return $FAILURE if [ "$ZFSBOOT_BOOT_POOL" ]; then f_eval_catch $funcname zpool "$ZPOOL_EXPORT" \ "$bootpool_name" || return $FAILURE fi f_dprintf "$funcname: Updating MBR boot loader on disks..." # Stick the ZFS boot loader in the "convenient hole" after # the ZFS internal metadata for disk in $disks; do f_eval_catch $funcname dd "$DD_WITH_OPTIONS" \ /boot/zfsboot /dev/$disk$bootpart \ "skip=1 seek=1024" || return $FAILURE done # Re-import the ZFS pool(s) f_dprintf "$funcname: Re-importing ZFS pool(s)..." f_eval_catch $funcname zpool "$ZPOOL_IMPORT_WITH_OPTIONS" \ "-o altroot=\"$BSDINSTALL_CHROOT\"" \ "$zroot_name" || return $FAILURE if [ "$ZFSBOOT_BOOT_POOL" ]; then # Import the bootpool, but do not mount it yet f_eval_catch $funcname zpool \ "$ZPOOL_IMPORT_WITH_OPTIONS" \ "-o altroot=\"$BSDINSTALL_CHROOT\" -N" \ "$bootpool_name" || return $FAILURE fi fi # Remount bootpool and create symlink(s) if [ "$ZFSBOOT_BOOT_POOL" ]; then f_eval_catch $funcname zfs "$ZFS_MOUNT" "$bootpool_name" || return $FAILURE f_dprintf "$funcname: Creating /boot symlink for boot pool..." f_eval_catch $funcname ln "$LN_SF" "$bootpool_name/boot" \ $BSDINSTALL_CHROOT/boot || return $FAILURE fi # zpool.cache is required to mount more than one pool at boot time f_dprintf "$funcname: Configuring zpool.cache for zroot..." f_eval_catch $funcname mkdir "$MKDIR_P" $BSDINSTALL_CHROOT/boot/zfs || return $FAILURE f_eval_catch $funcname zpool "$ZPOOL_SET" \ "cachefile=\"$BSDINSTALL_CHROOT/boot/zfs/zpool.cache\"" \ "$zroot_name" || return $FAILURE if [ "$ZFSBOOT_BOOT_POOL" ]; then f_eval_catch $funcname printf "$PRINTF_CONF" \ vfs.root.mountfrom \ "\"zfs:$zroot_name/$zroot_bootfs\"" \ $BSDINSTALL_TMPBOOT/loader.conf.root || return $FAILURE fi # # Set canmount=noauto so that the default Boot Environment (BE) does # not get mounted if a different BE is selected from the beastie menu # f_dprintf "$funcname: Set canmount=noauto for the root of the pool..." f_eval_catch $funcname zfs "$ZFS_SET" "canmount=noauto" \ "$zroot_name/$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME" # Last, but not least... required lines for rc.conf(5)/loader.conf(5) # NOTE: We later concatenate these into their destination f_dprintf "%s: Configuring rc.conf(5)/loader.conf(5) additions..." \ "$funcname" f_eval_catch $funcname echo "$ECHO_APPEND" 'zfs_enable=\"YES\"' \ $BSDINSTALL_TMPETC/rc.conf.zfs || return $FAILURE f_eval_catch $funcname echo "$ECHO_APPEND" \ 'kern.geom.label.disk_ident.enable=\"0\"' \ $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE f_eval_catch $funcname echo "$ECHO_APPEND" \ 'kern.geom.label.gptid.enable=\"0\"' \ $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE if [ "$ZFSBOOT_FORCE_4K_SECTORS" ]; then f_eval_catch $funcname echo "$ECHO_APPEND" \ 'vfs.zfs.min_auto_ashift=12' \ $BSDINSTALL_TMPETC/sysctl.conf.zfs || return $FAILURE fi if [ "$ZFSBOOT_SWAP_MIRROR" ]; then f_eval_catch $funcname echo "$ECHO_APPEND" \ 'geom_mirror_load=\"YES\"' \ $BSDINSTALL_TMPBOOT/loader.conf.gmirror || return $FAILURE fi # We're all done unless we should go on to do encryption [ "$ZFSBOOT_GELI_ENCRYPTION" ] || return $SUCCESS # # Configure geli(8)-based encryption # f_dprintf "$funcname: Configuring disk encryption..." f_eval_catch $funcname echo "$ECHO_APPEND" 'aesni_load=\"YES\"' \ $BSDINSTALL_TMPBOOT/loader.conf.aesni || return $FAILURE f_eval_catch $funcname echo "$ECHO_APPEND" 'geom_eli_load=\"YES\"' \ $BSDINSTALL_TMPBOOT/loader.conf.geli || return $FAILURE # We're all done unless we should go on for boot pool [ "$ZFSBOOT_BOOT_POOL" ] || return $SUCCESS for disk in $disks; do f_eval_catch $funcname printf "$PRINTF_CONF" \ geli_%s_keyfile0_load "$disk$targetpart YES" \ $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || return $FAILURE f_eval_catch $funcname printf "$PRINTF_CONF" \ geli_%s_keyfile0_type \ "$disk$targetpart $disk$targetpart:geli_keyfile0" \ $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || return $FAILURE f_eval_catch $funcname printf "$PRINTF_CONF" \ geli_%s_keyfile0_name \ "$disk$targetpart \"$ZFSBOOT_GELI_KEY_FILE\"" \ $BSDINSTALL_TMPBOOT/loader.conf.$disk$targetpart || return $FAILURE done # Set cachefile for boot pool so it auto-imports at system start f_dprintf "$funcname: Configuring zpool.cache for boot pool..." f_eval_catch $funcname zpool "$ZPOOL_SET" \ "cachefile=\"$BSDINSTALL_CHROOT/boot/zfs/zpool.cache\"" \ "$bootpool_name" || return $FAILURE # Some additional geli(8) requirements for loader.conf(5) for option in \ 'zpool_cache_load=\"YES\"' \ 'zpool_cache_type=\"/boot/zfs/zpool.cache\"' \ 'zpool_cache_name=\"/boot/zfs/zpool.cache\"' \ 'geom_eli_passphrase_prompt=\"YES\"' \ ; do f_eval_catch $funcname echo "$ECHO_APPEND" "$option" \ $BSDINSTALL_TMPBOOT/loader.conf.zfs || return $FAILURE done return $SUCCESS } # dialog_menu_diskinfo # # Prompt the user to select a disk and then provide detailed info on it. # dialog_menu_diskinfo() { local device disk # # Break from loop when user cancels disk selection # while :; do device=$( msg_cancel="$msg_back" f_device_menu \ "$DIALOG_TITLE" "$msg_select_a_disk_device" "" \ $DEVICE_TYPE_DISK 2>&1 ) || break $device get name disk # Show gpart(8) `show' and camcontrol(8) `inquiry' data f_show_msg "$msg_detailed_disk_info" \ "$disk" "$( gpart show $disk 2> /dev/null )" \ "$disk" "$( camcontrol inquiry $disk 2> /dev/null )" \ "$disk" "$( camcontrol identify $disk 2> /dev/null )" done return $SUCCESS } ############################################################ MAIN # # Initialize # f_dialog_title "$msg_zfs_configuration" f_dialog_backtitle "$msg_freebsd_installer" # User may have specifically requested ZFS-related operations be interactive ! f_interactive && f_zfsinteractive && unset $VAR_NONINTERACTIVE # # Debugging # f_dprintf "BSDINSTALL_CHROOT=[%s]" "$BSDINSTALL_CHROOT" f_dprintf "BSDINSTALL_TMPETC=[%s]" "$BSDINSTALL_TMPETC" f_dprintf "FSTAB_FMT=[%s]" "$FSTAB_FMT" # # Determine default boot type # case $(uname -m) in arm64) # We support only UEFI boot for arm64 : ${ZFSBOOT_BOOT_TYPE:=UEFI} : ${ZFSBOOT_PARTITION_SCHEME:=GPT} ;; *) # If the system was booted with UEFI, set the default boot type to UEFI bootmethod=$( sysctl -n machdep.bootmethod ) f_dprintf "machdep.bootmethod=[%s]" "$bootmethod" if [ "$bootmethod" = "UEFI" ]; then : ${ZFSBOOT_BOOT_TYPE:=BIOS+UEFI} : ${ZFSBOOT_PARTITION_SCHEME:=GPT} else : ${ZFSBOOT_BOOT_TYPE:=BIOS} : ${ZFSBOOT_PARTITION_SCHEME:=GPT} fi ;; -esac - -# -# The EFI loader installed in the ESP (EFI System Partition) must -# have the expected name in order to load correctly. -# -[ "$ZFSBOOT_ESP_NAME" ] || case "${UNAME_m:-$( uname -m )}" in - arm64) ZFSBOOT_ESP_NAME=BOOTaa64.efi ;; - arm) ZFSBOOT_ESP_NAME=BOOTarm.efi ;; - i386) ZFSBOOT_ESP_NAME=BOOTia32.efi ;; - amd64) ZFSBOOT_ESP_NAME=BOOTx64.efi ;; - *) - f_dprintf "Unsupported architecture: %s" $UNAME_m - f_die esac # # Loop over the main menu until we've accomplished what we came here to do # while :; do if ! f_interactive; then retval=$DIALOG_OK mtag=">>> $msg_install" else dialog_menu_main retval=$? f_dialog_menutag_fetch mtag fi f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in ">>> $msg_install") # # First, validate the user's selections # # Make sure they gave us a name for the pool if [ ! "$ZFSBOOT_POOL_NAME" ]; then f_dprintf "Pool name cannot be empty." f_show_err "$msg_pool_name_cannot_be_empty" continue fi # Validate vdev type against number of disks selected/scripted # (also validates that ZFSBOOT_DISKS are real [probed] disks) # NB: dialog_menu_layout supports running non-interactively dialog_menu_layout || continue # Make sure each disk will have room for ZFS if f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize && f_expand_number "$ZFSBOOT_BOOT_POOL_SIZE" bootsize && f_expand_number "1g" zpoolmin then minsize=$(( $swapsize + $zpoolmin )) teeny_disks= [ "$ZFSBOOT_BOOT_POOL" ] && minsize=$(( $minsize + $bootsize )) for disk in $ZFSBOOT_DISKS; do debug= f_device_find -1 \ $disk $DEVICE_TYPE_DISK device $device get capacity disksize || continue [ ${disksize:-0} -ge 0 ] || disksize=0 [ $disksize -lt $minsize ] && teeny_disks="$teeny_disks $disk" done if [ "$teeny_disks" ]; then f_dprintf "swapsize=[%s] bootsize[%s] %s" \ "$ZFSBOOT_SWAP_SIZE" \ "$ZFSBOOT_BOOT_POOL_SIZE" \ "minsize=[$minsize]" f_dprintf "These disks are too small: %s" \ "$teeny_disks" f_show_err "$msg_these_disks_are_too_small" \ "$ZFSBOOT_SWAP_SIZE" \ "$ZFSBOOT_BOOT_POOL_SIZE" \ "$teeny_disks" continue fi fi # # Last Chance! # if f_interactive; then dialog_last_chance $ZFSBOOT_DISKS || continue fi # # Let's do this # vdev_type="$ZFSBOOT_VDEV_TYPE" # Blank the vdev type for the default layout [ "$vdev_type" = "stripe" ] && vdev_type= zfs_create_boot "$ZFSBOOT_POOL_NAME" \ "$vdev_type" $ZFSBOOT_DISKS || continue break # to success ;; ?" $msg_pool_type_disks") ZFSBOOT_CONFIRM_LAYOUT=1 dialog_menu_layout # User has poked settings, disable later confirmation ZFSBOOT_CONFIRM_LAYOUT= ;; "- $msg_rescan_devices") f_device_rescan ;; "- $msg_disk_info") dialog_menu_diskinfo ;; ?" $msg_pool_name") # Prompt the user to input/change the name for the new pool f_dialog_input input \ "$msg_please_enter_a_name_for_your_zpool" \ "$ZFSBOOT_POOL_NAME" && ZFSBOOT_POOL_NAME="$input" ;; ?" $msg_force_4k_sectors") # Toggle the variable referenced both by the menu and later if [ "$ZFSBOOT_FORCE_4K_SECTORS" ]; then ZFSBOOT_FORCE_4K_SECTORS= else ZFSBOOT_FORCE_4K_SECTORS=1 fi ;; ?" $msg_encrypt_disks") # Toggle the variable referenced both by the menu and later if [ "$ZFSBOOT_GELI_ENCRYPTION" ]; then ZFSBOOT_GELI_ENCRYPTION= else ZFSBOOT_FORCE_4K_SECTORS=1 ZFSBOOT_GELI_ENCRYPTION=1 fi ;; ?" $msg_partition_scheme") # Toggle between GPT (BIOS), GPT (UEFI) and MBR if [ "$ZFSBOOT_PARTITION_SCHEME" = "GPT" -a \ "$ZFSBOOT_BOOT_TYPE" = "BIOS" ] then ZFSBOOT_PARTITION_SCHEME="GPT" ZFSBOOT_BOOT_TYPE="UEFI" elif [ "$ZFSBOOT_PARTITION_SCHEME" = "GPT" -a \ "$ZFSBOOT_BOOT_TYPE" = "UEFI" ] then ZFSBOOT_PARTITION_SCHEME="GPT" ZFSBOOT_BOOT_TYPE="BIOS+UEFI" elif [ "$ZFSBOOT_PARTITION_SCHEME" = "GPT" ]; then ZFSBOOT_PARTITION_SCHEME="MBR" ZFSBOOT_BOOT_TYPE="BIOS" elif [ "$ZFSBOOT_PARTITION_SCHEME" = "MBR" ]; then ZFSBOOT_PARTITION_SCHEME="GPT + Active" ZFSBOOT_BOOT_TYPE="BIOS" elif [ "$ZFSBOOT_PARTITION_SCHEME" = "GPT + Active" ]; then ZFSBOOT_PARTITION_SCHEME="GPT + Lenovo Fix" ZFSBOOT_BOOT_TYPE="BIOS" else ZFSBOOT_PARTITION_SCHEME="GPT" ZFSBOOT_BOOT_TYPE="BIOS" fi ;; ?" $msg_swap_size") # Prompt the user to input/change the swap size for each disk while :; do f_dialog_input input \ "$msg_please_enter_amount_of_swap_space" \ "$ZFSBOOT_SWAP_SIZE" && ZFSBOOT_SWAP_SIZE="${input:-0}" if f_expand_number "$ZFSBOOT_SWAP_SIZE" swapsize then if [ $swapsize -ne 0 -a $swapsize -lt 104857600 ]; then f_show_err "$msg_swap_toosmall" \ "$ZFSBOOT_SWAP_SIZE" continue; else break; fi else f_show_err "$msg_swap_invalid" \ "$ZFSBOOT_SWAP_SIZE" continue; fi done ;; ?" $msg_swap_mirror") # Toggle the variable referenced both by the menu and later if [ "$ZFSBOOT_SWAP_MIRROR" ]; then ZFSBOOT_SWAP_MIRROR= else ZFSBOOT_SWAP_MIRROR=1 fi ;; ?" $msg_swap_encrypt") # Toggle the variable referenced both by the menu and later if [ "$ZFSBOOT_SWAP_ENCRYPTION" ]; then ZFSBOOT_SWAP_ENCRYPTION= else ZFSBOOT_SWAP_ENCRYPTION=1 fi ;; esac done exit $SUCCESS ################################################################################ # END ################################################################################