Index: head/release/tools/vmimage.subr =================================================================== --- head/release/tools/vmimage.subr (revision 368421) +++ head/release/tools/vmimage.subr (revision 368422) @@ -1,279 +1,279 @@ #!/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) ESP=yes SCHEME=gpt BOOTPARTS="-b ${BOOTFILES}/i386/pmbr/pmbr \ -p freebsd-boot/bootfs:=${BOOTFILES}/i386/gptboot/gptboot" ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" ;; - arm64:aarch64) + arm64:aarch64 | riscv:riscv64*) ESP=yes SCHEME=gpt BOOTPARTS= ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" ;; powerpc:powerpc*) ESP=no SCHEME=apm BOOTPARTS="-p apple-boot/bootfs:=${BOOTFILES}/powerpc/boot1.chrp/boot1.hfs" ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" ;; *) echo "vmimage.subr: unsupported target '${TARGET}:${TARGET_ARCH}'" >&2 exit 1 ;; esac if [ ${ESP} = "yes" ]; then # Create an ESP espfilename=$(mktemp /tmp/efiboot.XXXXXX) make_esp_file ${espfilename} ${fat32min} ${BOOTFILES}/efi/loader_lua/loader_lua.efi BOOTPARTS="${BOOTPARTS} -p efi:=${espfilename}" fi mkimg -s ${SCHEME} -f ${VMFORMAT} \ ${BOOTPARTS} \ ${SWAPOPT} \ ${ROOTFSPART} \ -o ${VMIMAGE} if [ ${ESP} = "yes" ]; then rm ${espfilename} fi 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 if ! [ -z "${QEMUSTATIC}" ]; then export EMULATOR=/qemu cp ${QEMUSTATIC} ${DESTDIR}/${EMULATOR} fi mkdir -p ${DESTDIR}/dev mount -t devfs devfs ${DESTDIR}/dev chroot ${DESTDIR} ${EMULATOR} /usr/bin/newaliases chroot ${DESTDIR} ${EMULATOR} /bin/sh /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 # Expand the filesystem to fill the disk. echo 'growfs_enable="YES"' >> ${DESTDIR}/etc/rc.conf touch ${DESTDIR}/firstboot 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} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ /usr/sbin/pkg bootstrap -y chroot ${DESTDIR} ${EMULATOR} 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. if ! [ -z "${QEMUSTATIC}" ]; then rm -f ${DESTDIR}/${EMULATOR} fi rm -f ${DESTDIR}/etc/resolv.conf return 0 } vm_extra_pkg_rmcache() { if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then chroot ${DESTDIR} ${EMULATOR} 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/tools/boot/install-boot.sh =================================================================== --- head/tools/boot/install-boot.sh (revision 368421) +++ head/tools/boot/install-boot.sh (revision 368422) @@ -1,442 +1,443 @@ #!/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; }' } get_uefi_bootname() { case ${TARGET:-$(uname -m)} in amd64) echo bootx64 ;; arm64) echo bootaa64 ;; i386) echo bootia32 ;; arm) echo bootarm ;; + riscv) echo bootriscv64 ;; *) die "machine type $(uname -m) doesn't support UEFI" ;; esac } make_esp_file() { local file sizekb loader device stagedir 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 stagedir=$(mktemp -d /tmp/stand-test.XXXXXX) mkdir -p "${stagedir}/EFI/BOOT" efibootname=$(get_uefi_bootname) cp "${loader}" "${stagedir}/EFI/BOOT/${efibootname}.efi" makefs -t msdos \ -o fat_type=${fatbits} \ -o sectors_per_cluster=1 \ -o volume_label=EFISYS \ -s ${sizekb}k \ "${file}" "${stagedir}" rm -rf "${stagedir}" } make_esp_device() { local dev file mntpt fstype efibootname kbfree loadersize efibootfile local isboot1 existingbootentryloaderfile bootorder bootentry # ESP device node dev=$1 file=$2 mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) # 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" if [ -n "${updatesystem}" ]; then 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 else # Configure for booting from removable media if [ ! -d "${mntpt}/EFI/BOOT" ]; then mkdir -p "${mntpt}/EFI/BOOT" fi cp "${file}" "${mntpt}/EFI/BOOT/${efibootname}.efi" 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}/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}/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} } 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 ' -u Run commands such as efibootmgr to update the\n' printf ' currently running system\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:ho:s:u" opt; do case "$opt" in b) bios=${OPTARG} ;; d) srcroot=${OPTARG} ;; f) fs=${OPTARG} ;; g) case ${OPTARG} in [Yy][Ee][Ss]|geli) geli=geli ;; *) geli=nogeli ;; esac ;; u) updatesystem=1 ;; o) opts=${OPTARG} ;; s) scheme=${OPTARG} ;; ?|h) usage ;; esac done 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=${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=${srcroot}/boot/mbr mbr2=${srcroot}/boot/boot # VTOC8 vtoc8=${srcroot}/boot/boot1 # sanity check here # 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