diff --git a/release/scripts/mk-vmimage.sh b/release/scripts/mk-vmimage.sh index cf795e04655b..d5985ceb0d25 100755 --- a/release/scripts/mk-vmimage.sh +++ b/release/scripts/mk-vmimage.sh @@ -1,114 +1,112 @@ #!/bin/sh #- # Copyright (c) 2014, 2015 The FreeBSD Foundation # All rights reserved. # # This software was developed by Glen Barber under sponsorship # from the FreeBSD Foundation. # # 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. # # mk-vmimage.sh: Create virtual machine disk images in various formats. # # $FreeBSD$ # usage() { echo "${0} usage:" echo "${@}" return 1 } main() { local arg VMCONFIG="/dev/null" while getopts "C:c:d:f:i:o:s:S:" arg; do case "${arg}" in C) VMBUILDCONF="${OPTARG}" ;; c) VMCONFIG="${OPTARG}" ;; d) DESTDIR="${OPTARG}" ;; f) VMFORMAT="${OPTARG}" ;; i) VMBASE="${OPTARG}" ;; o) VMIMAGE="${OPTARG}" ;; s) VMSIZE="${OPTARG}" ;; S) WORLDDIR="${OPTARG}" ;; *) ;; esac done shift $(( ${OPTIND} - 1)) if [ -z "${VMBASE}" -o \ -z "${WORLDDIR}" -o \ -z "${DESTDIR}" -o \ -z "${VMSIZE}" -o \ -z "${VMIMAGE}" ]; then usage || exit 0 fi if [ -z "${VMBUILDCONF}" ] || [ ! -e "${VMBUILDCONF}" ]; then echo "Must provide the path to vmimage.subr." return 1 fi . "${VMBUILDCONF}" if [ ! -z "${VMCONFIG}" ] && [ ! -c "${VMCONFIG}" ]; then . "${VMCONFIG}" fi - ROOTLABEL="gpt" - vm_create_base vm_install_base vm_extra_install_base vm_extra_install_packages vm_extra_install_ports vm_extra_enable_services vm_extra_pre_umount vm_extra_pkg_rmcache cleanup vm_copy_base vm_create_disk || return 0 vm_extra_create_disk return 0 } main "$@" diff --git a/release/tools/vmimage.subr b/release/tools/vmimage.subr index 7bd971013656..6e0c1ea633e2 100644 --- a/release/tools/vmimage.subr +++ b/release/tools/vmimage.subr @@ -1,288 +1,265 @@ #!/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 | 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/efiesp:=${espfilename}" - - # Add this to fstab, requires temporarily remounting the fs - mddev=$(mdconfig -f ${VMBASE}) - mount /dev/${mddev} ${DESTDIR} - mkdir -p ${DESTDIR}/boot/efi - echo "/dev/${ROOTLABEL}/efiesp /boot/efi msdosfs rw 2 2" \ - >> ${DESTDIR}/etc/fstab - umount ${DESTDIR} - mdconfig -d -u ${mddev} - fi - - mkimg -s ${SCHEME} -f ${VMFORMAT} \ - ${BOOTPARTS} \ - ${SWAPOPT} \ - ${ROOTFSPART} \ - -o ${VMIMAGE} - - if [ ${ESP} = "yes" ]; then - rm ${espfilename} - fi - - return 0 -} +# Platform-specific large-scale setup +# Most platforms use GPT, so put that as default, then special cases +PARTSCHEME=gpt +ROOTLABEL="gpt" +case "${TARGET}:${TARGET_ARCH}" in + powerpc:powerpc*) + PARTSCHEME=mbr + ROOTLABEL="ufs" + NOSWAP=yes # Can't label swap partition with MBR, so no swap + ;; +esac 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} + # Defunct } 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 + 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 + BOOTPARTS="-b ${BOOTFILES}/i386/pmbr/pmbr \ + -p freebsd-boot/bootfs:=${BOOTFILES}/i386/gptboot/gptboot" + ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" + MAKEFSARGS="-B little" + ;; + arm64:aarch64 | riscv:riscv64*) + ESP=yes + BOOTPARTS= + ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" + MAKEFSARGS="-B little" + ;; + powerpc:powerpc*) + ESP=no + BOOTPARTS="-p prepboot:=${BOOTFILES}/powerpc/boot1.chrp/boot1.elf -a 1" + ROOTFSPART="-p freebsd:=${VMBASE}" + if [ ${TARGET_ARCH} = powerpc64le ]; then + MAKEFSARGS="-B little" + else + MAKEFSARGS="-B big" + fi + ;; + *) + 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/efiesp:=${espfilename}" + + # Add this to fstab + mkdir -p ${DESTDIR}/boot/efi + echo "/dev/${ROOTLABEL}/efiesp /boot/efi msdosfs rw 2 2" \ + >> ${DESTDIR}/etc/fstab + fi + + echo "Building filesystem... Please wait." + makefs ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \ + -s ${VMSIZE} ${VMBASE} ${DESTDIR} + + echo "Building final disk image... Please wait." + mkimg -s ${PARTSCHEME} -f ${VMFORMAT} \ + ${BOOTPARTS} \ + ${SWAPOPT} \ + ${ROOTFSPART} \ + -o ${VMIMAGE} + + if [ ${ESP} = "yes" ]; then + rm ${espfilename} + fi return 0 } vm_extra_create_disk() { return 0 }