1*a7ebe2d4SWarner Losh#!/bin/sh 2*a7ebe2d4SWarner Losh# 3*a7ebe2d4SWarner Losh# boot-test.sh - Automated boot loader regression tests 4*a7ebe2d4SWarner Losh# 5*a7ebe2d4SWarner Losh# Builds a minimal bootable tree, assembles disk images for all supported boot 6*a7ebe2d4SWarner Losh# configurations, then runs each in QEMU with a timeout looking for "SUCCESS". 7*a7ebe2d4SWarner Losh# 8*a7ebe2d4SWarner Losh# All tests run as an unprivileged user. No root required, with one exception: 9*a7ebe2d4SWarner Losh# netboot-bios/netboot-efi need a real tap(4) device + dnsmasq (so DHCP can 10*a7ebe2d4SWarner Losh# carry a root-path) instead of QEMU's slirp networking, and both creating a tap 11*a7ebe2d4SWarner Losh# and giving it an address require root. That setup runs once (via sudo, 12*a7ebe2d4SWarner Losh# prompting interactively) and is left running -- later runs detect the existing 13*a7ebe2d4SWarner Losh# setup and skip sudo entirely. Assumes buildworld and buildkernel have already 14*a7ebe2d4SWarner Losh# been done for the target architecture. 15*a7ebe2d4SWarner Losh# 16*a7ebe2d4SWarner Losh# Usage: 17*a7ebe2d4SWarner Losh# cd /usr/src/stand && sh ../tools/boot/boot-test.sh [options] 18*a7ebe2d4SWarner Losh# 19*a7ebe2d4SWarner Losh# Options: 20*a7ebe2d4SWarner Losh# -a ARCH Architecture to test; repeat -a to test several 21*a7ebe2d4SWarner Losh# (default: host arch) 22*a7ebe2d4SWarner Losh# Supported: amd64, aarch64, armv7, riscv64, powerpc, powerpc64, 23*a7ebe2d4SWarner Losh# powerpc64le 24*a7ebe2d4SWarner Losh# -A Test every supported architecture 25*a7ebe2d4SWarner Losh# -b Skip build/install phase (reuse existing tree) 26*a7ebe2d4SWarner Losh# -B Skip build/install and image creation (reuse existing images) 27*a7ebe2d4SWarner Losh# -l Tell us the log directory and exit 28*a7ebe2d4SWarner Losh# -j JOBS Max parallel QEMU instances (default: unlimited) 29*a7ebe2d4SWarner Losh# -o DIR Output directory for images and logs 30*a7ebe2d4SWarner Losh# -t REGEX Only run tests matching REGEX 31*a7ebe2d4SWarner Losh# -T SECONDS QEMU timeout (default: per-arch, 60; 180 for powerpc64) 32*a7ebe2d4SWarner Losh# --netboot-teardown 33*a7ebe2d4SWarner Losh# Destroy the tap(4)/dnsmasq netboot setup (run with sudo) 34*a7ebe2d4SWarner Losh 35*a7ebe2d4SWarner Loshset -e 36*a7ebe2d4SWarner Losh 37*a7ebe2d4SWarner Loshdie() { 38*a7ebe2d4SWarner Losh echo "FATAL: $*" >&2 39*a7ebe2d4SWarner Losh exit 1 40*a7ebe2d4SWarner Losh} 41*a7ebe2d4SWarner Losh 42*a7ebe2d4SWarner Losh# FreeBSD port package (origin) that provides a command or file; "" = base 43*a7ebe2d4SWarner Losh# system. 44*a7ebe2d4SWarner Loshpkg_for() { 45*a7ebe2d4SWarner Losh case "$1" in 46*a7ebe2d4SWarner Losh jq) echo textproc/jq ;; 47*a7ebe2d4SWarner Losh expect) echo lang/expect ;; 48*a7ebe2d4SWarner Losh qemu-system-*) echo emulators/qemu ;; 49*a7ebe2d4SWarner Losh *ipxe*) echo sysutils/ipxe ;; 50*a7ebe2d4SWarner Losh *syslinux*|*memdisk*) echo sysutils/syslinux ;; 51*a7ebe2d4SWarner Losh *edk2*) echo emulators/qemu ;; # edk2-*.fd ship with qemu 52*a7ebe2d4SWarner Losh *) echo "" ;; # makefs/mkimg etc. = base 53*a7ebe2d4SWarner Losh esac 54*a7ebe2d4SWarner Losh} 55*a7ebe2d4SWarner Losh 56*a7ebe2d4SWarner Losh# Hard requirement: a command that must be in PATH, else abort naming the pkg. 57*a7ebe2d4SWarner Loshneed_cmd() { 58*a7ebe2d4SWarner Losh which "$1" >/dev/null 2>&1 && return 0 59*a7ebe2d4SWarner Losh p=$(pkg_for "$1") 60*a7ebe2d4SWarner Losh [ -n "${p}" ] && die "$1 not found; install with: pkg install ${p##*/}" 61*a7ebe2d4SWarner Losh die "$1 not found (expected in the base system)" 62*a7ebe2d4SWarner Losh} 63*a7ebe2d4SWarner Losh 64*a7ebe2d4SWarner Losh# Optional requirement: a file for a feature; warn + skip (return 1) if absent. 65*a7ebe2d4SWarner Loshhave_file() { 66*a7ebe2d4SWarner Losh [ -e "$1" ] && return 0 67*a7ebe2d4SWarner Losh p=$(pkg_for "$1") 68*a7ebe2d4SWarner Losh echo " WARNING: $1 missing${p:+; pkg install ${p##*/}} -- skipping" >&2 69*a7ebe2d4SWarner Losh return 1 70*a7ebe2d4SWarner Losh} 71*a7ebe2d4SWarner Losh 72*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 73*a7ebe2d4SWarner Losh# Architecture configuration 74*a7ebe2d4SWarner Losh# 75*a7ebe2d4SWarner Losh# Per-arch parameters live in boot-test.json, as an arch object merged over a 76*a7ebe2d4SWarner Losh# "defaults" object. Arrays are expanded to a space separated list. Missing 77*a7ebe2d4SWarner Losh# values default to an empty string. ARCH_CAPS is a cached copy of the 78*a7ebe2d4SWarner Losh# capabilities array for the architecture. TARGET and TARGET_ARCH are cached due 79*a7ebe2d4SWarner Losh# to heavy use. Other parameters are fetched as needed as their use is 80*a7ebe2d4SWarner Losh# infrequent. 81*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 82*a7ebe2d4SWarner Losh 83*a7ebe2d4SWarner Losh# Test whether the current arch declares a capability, e.g. "has efi". 84*a7ebe2d4SWarner Loshhas() { 85*a7ebe2d4SWarner Losh case " ${ARCH_CAPS} " in 86*a7ebe2d4SWarner Losh *" $1 "*) return 0 ;; 87*a7ebe2d4SWarner Losh esac 88*a7ebe2d4SWarner Losh return 1 89*a7ebe2d4SWarner Losh} 90*a7ebe2d4SWarner Losh 91*a7ebe2d4SWarner Losh# Fetch the named parameter for this $ARCH. 92*a7ebe2d4SWarner Loshparam() { 93*a7ebe2d4SWarner Losh jq -r --arg a "${ARCH}" --arg k "$1" ' 94*a7ebe2d4SWarner Losh (.defaults + .arch[$a])[$k] as $v 95*a7ebe2d4SWarner Losh | if ($v | type) == "array" then $v | join(" ") 96*a7ebe2d4SWarner Losh elif $v == null then "" 97*a7ebe2d4SWarner Losh else $v end 98*a7ebe2d4SWarner Losh ' "${CONF}" 99*a7ebe2d4SWarner Losh} 100*a7ebe2d4SWarner Losh 101*a7ebe2d4SWarner Losh# Validate ${ARCH} and cache the parameters that are heavily used in global 102*a7ebe2d4SWarner Losh# variables. 103*a7ebe2d4SWarner Loshload_arch_config() { 104*a7ebe2d4SWarner Losh jq -e --arg a "${ARCH}" '.arch | has($a)' "${CONF}" >/dev/null 2>&1 \ 105*a7ebe2d4SWarner Losh || die "Unknown architecture: ${ARCH}" 106*a7ebe2d4SWarner Losh ARCH_CAPS=$(param caps) 107*a7ebe2d4SWarner Losh TARGET=$(param target) 108*a7ebe2d4SWarner Losh TARGET_ARCH=$(param target_arch) 109*a7ebe2d4SWarner Losh} 110*a7ebe2d4SWarner Losh 111*a7ebe2d4SWarner Losh# Derive every per-arch value for ${1} into the ARCH_*/TARGET/OUTDIR/... globals. 112*a7ebe2d4SWarner Losh# Callers isolate architectures via subshells (build) or job backgrounding (run) 113*a7ebe2d4SWarner Losh# -- both snapshot these globals -- so they never collide across arches, and we 114*a7ebe2d4SWarner Losh# never have to pass the whole bundle around. 115*a7ebe2d4SWarner Loshsetup_arch_env() { 116*a7ebe2d4SWarner Losh ARCH=$1 117*a7ebe2d4SWarner Losh load_arch_config 118*a7ebe2d4SWarner Losh TIMEOUT=${TIMEOUT_OVERRIDE:-$(param timeout)} 119*a7ebe2d4SWarner Losh MK="make TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" 120*a7ebe2d4SWarner Losh ARCH_OBJDIR=$(${MK} -v .OBJDIR) 121*a7ebe2d4SWarner Losh [ -n "${ARCH_OBJDIR}" ] || die "Cannot determine OBJDIR for ${ARCH}" 122*a7ebe2d4SWarner Losh OUTDIR=${OUTDIR_OVERRIDE:-${ARCH_OBJDIR}/boot-test} 123*a7ebe2d4SWarner Losh IMGDIR=${OUTDIR}/images 124*a7ebe2d4SWarner Losh LOGDIR=${OUTDIR}/logs 125*a7ebe2d4SWarner Losh DESTDIR=${OUTDIR}/tree 126*a7ebe2d4SWarner Losh TESTLIST=${OUTDIR}/test-list.txt 127*a7ebe2d4SWarner Losh mkdir -p ${IMGDIR} ${LOGDIR} 128*a7ebe2d4SWarner Losh} 129*a7ebe2d4SWarner Losh 130*a7ebe2d4SWarner Losh# --- Configuration --- 131*a7ebe2d4SWarner Losh 132*a7ebe2d4SWarner LoshSKIP_BUILD=false 133*a7ebe2d4SWarner LoshSKIP_IMAGES=false 134*a7ebe2d4SWarner LoshTEST_FILTER="" 135*a7ebe2d4SWarner LoshMAX_JOBS=0 136*a7ebe2d4SWarner LoshOUTDIR_OVERRIDE="" # from -o; only valid with a single arch 137*a7ebe2d4SWarner LoshTIMEOUT_OVERRIDE="" # from -T; else each arch's param timeout 138*a7ebe2d4SWarner LoshARCHES="" # from -a (repeatable) / -A; defaults to $(uname -p) 139*a7ebe2d4SWarner LoshALL=false 140*a7ebe2d4SWarner Losh 141*a7ebe2d4SWarner Losh# State shared by tap(4)/dnsmasq netboot networking (see netboot_network_setup / 142*a7ebe2d4SWarner Losh# netboot_helper below). Key off user's ID to allow multiple people to 143*a7ebe2d4SWarner Losh# run the script at the same time. 144*a7ebe2d4SWarner LoshNETBOOT_STATE_DIR=${TMPDIR:-/tmp}/boot-test-net.${SUDO_UID:-$(id -u)} 145*a7ebe2d4SWarner Losh 146*a7ebe2d4SWarner Losh# --netboot-helper/--netboot-teardown are internal entry points used to run 147*a7ebe2d4SWarner Losh# privileged setup/teardown via sudo (see netboot_network_setup); they bypass 148*a7ebe2d4SWarner Losh# normal option parsing and are dispatched once every function is defined, in 149*a7ebe2d4SWarner Losh# the Main section at the bottom of this script. 150*a7ebe2d4SWarner LoshNETBOOT_MODE="" 151*a7ebe2d4SWarner Loshcase "$1" in 152*a7ebe2d4SWarner Losh --netboot-helper) NETBOOT_MODE=helper; NETBOOT_HELPER_PLAN=$2 ;; 153*a7ebe2d4SWarner Losh --netboot-teardown) NETBOOT_MODE=teardown ;; 154*a7ebe2d4SWarner Loshesac 155*a7ebe2d4SWarner Losh 156*a7ebe2d4SWarner Loshdo_report_dirs=false 157*a7ebe2d4SWarner Loshif [ -z "${NETBOOT_MODE}" ]; then 158*a7ebe2d4SWarner Losh while getopts "a:AbBlj:o:t:T:" opt; do 159*a7ebe2d4SWarner Losh case "$opt" in 160*a7ebe2d4SWarner Losh a) ARCHES="${ARCHES} $OPTARG" ;; 161*a7ebe2d4SWarner Losh A) ALL=true ;; 162*a7ebe2d4SWarner Losh b) SKIP_BUILD=true ;; 163*a7ebe2d4SWarner Losh B) SKIP_BUILD=true; SKIP_IMAGES=true ;; 164*a7ebe2d4SWarner Losh j) MAX_JOBS="$OPTARG" ;; 165*a7ebe2d4SWarner Losh l) do_report_dirs=true ;; 166*a7ebe2d4SWarner Losh o) OUTDIR_OVERRIDE="$OPTARG" ;; 167*a7ebe2d4SWarner Losh t) TEST_FILTER="$OPTARG" ;; 168*a7ebe2d4SWarner Losh T) TIMEOUT_OVERRIDE="$OPTARG" ;; 169*a7ebe2d4SWarner Losh ?) echo "Usage: $0 [-a arch]... | -A] [-b] [-B] [-t regex] [-T secs] [-j jobs] [-o dir]" >&2 170*a7ebe2d4SWarner Losh exit 1 ;; 171*a7ebe2d4SWarner Losh esac 172*a7ebe2d4SWarner Losh done 173*a7ebe2d4SWarner Losh 174*a7ebe2d4SWarner Losh # Resolve the config file next to this script before we cd elsewhere. 175*a7ebe2d4SWarner Losh CONF="$(cd "$(dirname "$0")" && pwd)/boot-test.json" 176*a7ebe2d4SWarner Losh [ -f "${CONF}" ] || die "Config file not found: ${CONF}" 177*a7ebe2d4SWarner Losh 178*a7ebe2d4SWarner Losh SRCTOP=$(make -v SRCTOP) || die "Run from stand/ directory in a FreeBSD source tree" 179*a7ebe2d4SWarner Losh cd ${SRCTOP}/stand 180*a7ebe2d4SWarner Losh 181*a7ebe2d4SWarner Losh # Build the architecture list from json. Partially supported architectures 182*a7ebe2d4SWarner Losh # are omitted from -A, but accessible with a direct -a. 183*a7ebe2d4SWarner Losh if ${ALL}; then 184*a7ebe2d4SWarner Losh for a in $(jq -r '.arch | keys_unsorted[]' "${CONF}"); do 185*a7ebe2d4SWarner Losh if [ $(jq ".arch.${a}.disabled" "${CONF}") != "true" ]; then 186*a7ebe2d4SWarner Losh ARCHES="$ARCHES $a" 187*a7ebe2d4SWarner Losh fi 188*a7ebe2d4SWarner Losh done 189*a7ebe2d4SWarner Losh fi 190*a7ebe2d4SWarner Losh [ -n "${ARCHES}" ] || ARCHES=$(uname -p) 191*a7ebe2d4SWarner Losh 192*a7ebe2d4SWarner Losh # -o names one output directory, so it only makes sense for a single arch. 193*a7ebe2d4SWarner Losh if [ -n "${OUTDIR_OVERRIDE}" ] && [ $(echo ${ARCHES} | wc -w) -gt 1 ]; then 194*a7ebe2d4SWarner Losh die "-o cannot be combined with multiple architectures" 195*a7ebe2d4SWarner Losh fi 196*a7ebe2d4SWarner Loshfi 197*a7ebe2d4SWarner Losh 198*a7ebe2d4SWarner Losh# The smallest FAT32 filesystem is 33292 KB 199*a7ebe2d4SWarner Loshespsize=33292 200*a7ebe2d4SWarner Losh 201*a7ebe2d4SWarner Losh# Linux kernel version for linuxboot tests 202*a7ebe2d4SWarner LoshLINUX_VERSION=6.18.2 203*a7ebe2d4SWarner Losh 204*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 205*a7ebe2d4SWarner Losh# QEMU command builders 206*a7ebe2d4SWarner Losh# 207*a7ebe2d4SWarner Losh# qemu_base wraps the constant bits -- binary, memory, machine, per-arch extra 208*a7ebe2d4SWarner Losh# flags, and the -nographic/serial tail -- around the device arguments each 209*a7ebe2d4SWarner Losh# specific builder passes in (disks, CDs, firmware, bios). 210*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 211*a7ebe2d4SWarner Losh 212*a7ebe2d4SWarner Loshqemu_base() { 213*a7ebe2d4SWarner Losh echo "$(param qemu_bin) -m 1g $(param qemu_machine) $(param qemu_extra) $* -nographic -monitor none -serial stdio" 214*a7ebe2d4SWarner Losh} 215*a7ebe2d4SWarner Losh 216*a7ebe2d4SWarner Losh# -drive for the EFI firmware pflash; empty when the arch has no separate 217*a7ebe2d4SWarner Losh# firmware (e.g. riscv64's u-boot payload). 218*a7ebe2d4SWarner Loshqemu_efi_firmware() { 219*a7ebe2d4SWarner Losh [ -z "$(param efi_firmware)" ] && return 0 220*a7ebe2d4SWarner Losh echo "-drive file=$(param efi_firmware),format=raw,if=pflash,readonly=on" 221*a7ebe2d4SWarner Losh} 222*a7ebe2d4SWarner Losh 223*a7ebe2d4SWarner Losh# Custom OpenBIOS cached beside the ISOs, if present (QEMU's bundled one is 224*a7ebe2d4SWarner Losh# missing fixes we need for now); empty otherwise. 225*a7ebe2d4SWarner Loshqemu_ofw_bios() { 226*a7ebe2d4SWarner Losh [ -f "${ISODIR}/openbios-ppc" ] || return 0 227*a7ebe2d4SWarner Losh echo "-bios ${ISODIR}/openbios-ppc" 228*a7ebe2d4SWarner Losh} 229*a7ebe2d4SWarner Losh 230*a7ebe2d4SWarner Loshqemu_bios() { qemu_base "-drive file=$1,format=raw"; } 231*a7ebe2d4SWarner Loshqemu_efi() { qemu_base "$(qemu_efi_firmware) -drive file=$1,format=raw"; } 232*a7ebe2d4SWarner Loshqemu_bios_cdrom() { qemu_base "-cdrom $1"; } 233*a7ebe2d4SWarner Loshqemu_efi_cdrom() { qemu_base "$(qemu_efi_firmware) -cdrom $1"; } 234*a7ebe2d4SWarner Losh 235*a7ebe2d4SWarner Losh# OFW disk on the default (macio IDE) bus: OpenBIOS aliases it "hd" and 236*a7ebe2d4SWarner Losh# auto-probes hd:,\\:tbxi for the Apple_Bootstrap (boot1.hfs) partition. 237*a7ebe2d4SWarner Losh# virtio disks are not reachable as "hd", so OF finds nothing and drops to "0 >". 238*a7ebe2d4SWarner Loshqemu_ofw() { qemu_base "$(qemu_ofw_bios) -drive file=$1,format=raw"; } 239*a7ebe2d4SWarner Losh# -boot d boots the (macio IDE) CD-ROM, which OpenBIOS probes cd:,\\:tbxi. 240*a7ebe2d4SWarner Loshqemu_ofw_cdrom() { qemu_base "$(qemu_ofw_bios) -boot d -cdrom $1"; } 241*a7ebe2d4SWarner Losh 242*a7ebe2d4SWarner Losh# pseries PReP disk boot: virtio-blk (vtbd0), matching freebsd-ci; SLOF finds 243*a7ebe2d4SWarner Losh# the PReP boot partition on it and runs boot1.elf. 244*a7ebe2d4SWarner Loshqemu_prep() { qemu_base "-drive if=none,file=$1,format=raw,id=hd0 -device virtio-blk,drive=hd0"; } 245*a7ebe2d4SWarner Losh# pseries CD boot: SLOF boots the El Torito CHRP image; -boot d selects the CD. 246*a7ebe2d4SWarner Loshqemu_prep_cdrom() { qemu_base "-cdrom $1 -boot d"; } 247*a7ebe2d4SWarner Losh 248*a7ebe2d4SWarner Losh# Direct linuxboot: hand the Linux kernel and initrd to QEMU on the command 249*a7ebe2d4SWarner Losh# line (-kernel/-initrd) rather than off an ESP. Used by platforms with no 250*a7ebe2d4SWarner Losh# EFI/ESP (e.g. powerpc64le/pseries); the FreeBSD root disk is attached the 251*a7ebe2d4SWarner Losh# same way as every other test. 252*a7ebe2d4SWarner Loshqemu_linuxboot() { 253*a7ebe2d4SWarner Losh extra="-kernel $2 -initrd $3" 254*a7ebe2d4SWarner Losh [ -n "$(param linux_console)" ] && extra="${extra} -append $(param linux_console)" 255*a7ebe2d4SWarner Losh qemu_base "${extra} -drive file=$1,format=raw" 256*a7ebe2d4SWarner Losh} 257*a7ebe2d4SWarner Losh 258*a7ebe2d4SWarner Losh# Netboot over $1 -- vmnet(4): dnsmasq owns DHCP/TFTP/root-path, which we use so 259*a7ebe2d4SWarner Losh# we use tftp, not NFS, for all the files. QEMU's netdev type is still "tap" (it 260*a7ebe2d4SWarner Losh# treats vmnet(4) and tap(4) identically). $2 = bios|efi (efi adds the pflash 261*a7ebe2d4SWarner Losh# firmware; bios relies on the NIC's PXE option ROM). 262*a7ebe2d4SWarner Loshqemu_netboot() { 263*a7ebe2d4SWarner Losh netif=$1 264*a7ebe2d4SWarner Losh fw="" 265*a7ebe2d4SWarner Losh [ "$2" = efi ] && fw="$(qemu_efi_firmware)" 266*a7ebe2d4SWarner Losh qemu_base "${fw} -netdev tap,id=net0,ifname=${netif},script=no,downscript=no -device $(param netboot_nic),netdev=net0 -boot n" 267*a7ebe2d4SWarner Losh} 268*a7ebe2d4SWarner Losh 269*a7ebe2d4SWarner Losh# RAM-disk netboot (x86 EFI). Boots iPXE from -hda (edk2's own PXE is disabled 270*a7ebe2d4SWarner Losh# via fw_cfg); iPXE DHCPs, fetches the bootfile (an iPXE script) over TFTP, and 271*a7ebe2d4SWarner Losh# chains loader.efi with memdisk=<url>. The loader downloads that image and 272*a7ebe2d4SWarner Losh# boots it entirely from RAM -- no NFS and no DHCP root-path needed. Mirrors 273*a7ebe2d4SWarner Losh# ~/memdisk/do-memddisk-efi. ${OUTDIR}/netboot-vars.fd is a writable edk2 vars 274*a7ebe2d4SWarner Losh# copy made by assemble_netboot. 275*a7ebe2d4SWarner Losh# 276*a7ebe2d4SWarner Losh# We'll need to to http boots in the future, and that will likely require 277*a7ebe2d4SWarner Losh# we don't use the ipxe USB path we use here. We use that because Tianocore 278*a7ebe2d4SWarner Losh# expects http/https booting when the obvious '-boot n' sort of things 279*a7ebe2d4SWarner Losh# are used. 280*a7ebe2d4SWarner Loshqemu_netboot_ramdisk() { 281*a7ebe2d4SWarner Losh netif=$1 282*a7ebe2d4SWarner Losh echo "$(param qemu_bin) -M q35 -cpu max -m 2g \ 283*a7ebe2d4SWarner Losh -drive if=pflash,format=raw,readonly=on,file=$(param efi_firmware) \ 284*a7ebe2d4SWarner Losh -drive if=pflash,format=raw,file=${OUTDIR}/netboot-vars.fd \ 285*a7ebe2d4SWarner Losh -hda ${OUTDIR}/netboot-ipxe.img \ 286*a7ebe2d4SWarner Losh -device virtio-net,netdev=net0 \ 287*a7ebe2d4SWarner Losh -netdev tap,id=net0,ifname=${netif},script=no,downscript=no \ 288*a7ebe2d4SWarner Losh -fw_cfg name=opt/org.tianocore/IPv4PXESupport,string=no \ 289*a7ebe2d4SWarner Losh -fw_cfg name=opt/org.tianocore/IPv6PXESupport,string=no \ 290*a7ebe2d4SWarner Losh -nographic -monitor none -serial stdio" 291*a7ebe2d4SWarner Losh} 292*a7ebe2d4SWarner Losh 293*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 294*a7ebe2d4SWarner Losh# Phase 0: Extract minimal userland from release ISO 295*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 296*a7ebe2d4SWarner Losh 297*a7ebe2d4SWarner Losh# Binaries needed for a minimal bootable userland. 298*a7ebe2d4SWarner Losh# Libraries are inferred from these via ldd on the host equivalents. 299*a7ebe2d4SWarner LoshUSERLAND_BINS="sbin/fastboot sbin/halt sbin/init bin/sh sbin/sysctl" 300*a7ebe2d4SWarner Losh 301*a7ebe2d4SWarner LoshISODIR=${HOME}/iso 302*a7ebe2d4SWarner Losh 303*a7ebe2d4SWarner Loshfind_iso() { 304*a7ebe2d4SWarner Losh local isotgt=${TARGET} 305*a7ebe2d4SWarner Losh [ ${TARGET} != ${TARGET_ARCH} ] && isotgt="${isotgt}-${TARGET_ARCH}" 306*a7ebe2d4SWarner Losh local isoname="${ISODIR}/FreeBSD-$(param freebsd_version)-RELEASE-${isotgt}-disc1.iso.xz" 307*a7ebe2d4SWarner Losh [ -f "${isoname}" ] && echo "${isoname}" && return 308*a7ebe2d4SWarner Losh die "No ISO found for ${ARCH}: ${isoname} not found" 309*a7ebe2d4SWarner Losh} 310*a7ebe2d4SWarner Losh 311*a7ebe2d4SWarner Loshinstall_minimal_userland() { 312*a7ebe2d4SWarner Losh # Split the declaration from the assignment: `local iso=$(find_iso)` would 313*a7ebe2d4SWarner Losh # swallow find_iso's exit status (local returns 0), so its die() -- which 314*a7ebe2d4SWarner Losh # runs in the $() subshell -- would not stop this script. 315*a7ebe2d4SWarner Losh local iso 316*a7ebe2d4SWarner Losh iso=$(find_iso) || exit 1 317*a7ebe2d4SWarner Losh echo " Extracting minimal userland from release ISO ${iso}..." 318*a7ebe2d4SWarner Losh 319*a7ebe2d4SWarner Losh # Determine library paths from host equivalents of our binaries 320*a7ebe2d4SWarner Losh local host_bins="" 321*a7ebe2d4SWarner Losh for b in ${USERLAND_BINS}; do 322*a7ebe2d4SWarner Losh host_bins="${host_bins} /${b}" 323*a7ebe2d4SWarner Losh done 324*a7ebe2d4SWarner Losh lib_paths=$(ldd ${host_bins} 2>/dev/null \ 325*a7ebe2d4SWarner Losh | awk 'NF == 4 { print $3 }' | sort -u) 326*a7ebe2d4SWarner Losh 327*a7ebe2d4SWarner Losh # Build the list of paths to extract: binaries + libraries + rtld 328*a7ebe2d4SWarner Losh local extract_list="" 329*a7ebe2d4SWarner Losh for b in ${USERLAND_BINS}; do 330*a7ebe2d4SWarner Losh extract_list="${extract_list} ./${b}" 331*a7ebe2d4SWarner Losh done 332*a7ebe2d4SWarner Losh for l in ${lib_paths}; do 333*a7ebe2d4SWarner Losh extract_list="${extract_list} .${l}" 334*a7ebe2d4SWarner Losh done 335*a7ebe2d4SWarner Losh extract_list="${extract_list} ./libexec/ld-elf.so.1" 336*a7ebe2d4SWarner Losh 337*a7ebe2d4SWarner Losh # Some architectures (e.g., armv7) need libgcc_s.so.1 even though the host 338*a7ebe2d4SWarner Losh # binaries don't. Always try to extract it. We'll ignore it if not there. 339*a7ebe2d4SWarner Losh extract_list="${extract_list} ./lib/libgcc_s.so.1" 340*a7ebe2d4SWarner Losh 341*a7ebe2d4SWarner Losh # Extract the files from the tarball. 342*a7ebe2d4SWarner Losh tar -C ${DESTDIR} -xf ${iso} ${extract_list} \ 343*a7ebe2d4SWarner Losh >> ${LOGDIR}/installuserland.log 2>&1 || true 344*a7ebe2d4SWarner Losh} 345*a7ebe2d4SWarner Losh 346*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 347*a7ebe2d4SWarner Losh# Phase 1: Build the boot tree 348*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 349*a7ebe2d4SWarner Losh 350*a7ebe2d4SWarner Loshbuild_tree() { 351*a7ebe2d4SWarner Losh echo "=== Phase 1: Building boot tree ===" 352*a7ebe2d4SWarner Losh 353*a7ebe2d4SWarner Losh rm -rf ${DESTDIR} 354*a7ebe2d4SWarner Losh mkdir -p ${DESTDIR}/boot/defaults 355*a7ebe2d4SWarner Losh mkdir -p ${DESTDIR}/boot/kernel 356*a7ebe2d4SWarner Losh mkdir -p ${DESTDIR}/boot/uboot 357*a7ebe2d4SWarner Losh mkdir -p ${DESTDIR}/sbin ${DESTDIR}/bin \ 358*a7ebe2d4SWarner Losh ${DESTDIR}/lib ${DESTDIR}/libexec \ 359*a7ebe2d4SWarner Losh ${DESTDIR}/etc ${DESTDIR}/dev 360*a7ebe2d4SWarner Losh 361*a7ebe2d4SWarner Losh # Install kernel 362*a7ebe2d4SWarner Losh # I'd prefer this to be MINIMAL, but GENERIC is needed until I work out what 363*a7ebe2d4SWarner Losh # different devices we boot from... 364*a7ebe2d4SWarner Losh (cd ${SRCTOP} && ${MK} installkernel \ 365*a7ebe2d4SWarner Losh KERNCONF=$(param kernconf) \ 366*a7ebe2d4SWarner Losh MODULES_OVERRIDE="ufs zfs acl_nfs4 crypto zlib cd9660" \ 367*a7ebe2d4SWarner Losh DESTDIR=${DESTDIR} \ 368*a7ebe2d4SWarner Losh MK_KERNEL_SYMBOLS=no \ 369*a7ebe2d4SWarner Losh MK_INSTALL_AS_USER=yes) > ${LOGDIR}/installkernel.log 2>&1 \ 370*a7ebe2d4SWarner Losh || die "Kernel install failed (see ${LOGDIR}/installkernel.log)" 371*a7ebe2d4SWarner Losh 372*a7ebe2d4SWarner Losh # Install boot loaders 373*a7ebe2d4SWarner Losh ${MK} buildenv \ 374*a7ebe2d4SWarner Losh DESTDIR=${DESTDIR} \ 375*a7ebe2d4SWarner Losh MK_MAN=no \ 376*a7ebe2d4SWarner Losh MK_INSTALL_AS_USER=yes \ 377*a7ebe2d4SWarner Losh MK_DEBUG_FILES=no \ 378*a7ebe2d4SWarner Losh BUILDENV_SHELL="make all install" \ 379*a7ebe2d4SWarner Losh >> ${LOGDIR}/installloader.log 2>&1 \ 380*a7ebe2d4SWarner Losh || die "Boot loader install failed (see ${LOGDIR}/installloader.log)" 381*a7ebe2d4SWarner Losh 382*a7ebe2d4SWarner Losh # Install minimal userland (works for both native and cross builds) 383*a7ebe2d4SWarner Losh install_minimal_userland 384*a7ebe2d4SWarner Losh 385*a7ebe2d4SWarner Losh # Remove default loader symlinks -- we add them back per-image via 386*a7ebe2d4SWarner Losh # mtree overlays to test each loader variant individually. 387*a7ebe2d4SWarner Losh # /boot/loader is the BIOS stage-3 (amd64 only). 388*a7ebe2d4SWarner Losh # /boot/loader.efi is what boot1.efi chainloads (all EFI platforms). 389*a7ebe2d4SWarner Losh # OFW/PReP are the exception: their /boot/loader is the one real loader 390*a7ebe2d4SWarner Losh # (no lua/4th/simp variants are built), chainloaded by boot1.hfs (mac99) 391*a7ebe2d4SWarner Losh # or boot1.elf (pseries PReP), so keep it in the tree. 392*a7ebe2d4SWarner Losh has ofw || has prep || rm -f ${DESTDIR}/boot/loader 393*a7ebe2d4SWarner Losh rm -f ${DESTDIR}/boot/loader.efi 394*a7ebe2d4SWarner Losh 395*a7ebe2d4SWarner Losh # Serial console configuration. boot.config is consumed only by the 396*a7ebe2d4SWarner Losh # BIOS boot blocks; its -h/-D/-S flags are meaningless on EFI/OFW, so 397*a7ebe2d4SWarner Losh # only write it where BIOS booting is supported. 398*a7ebe2d4SWarner Losh if has bios; then 399*a7ebe2d4SWarner Losh echo -h -D -S115200 > ${DESTDIR}/boot.config 400*a7ebe2d4SWarner Losh fi 401*a7ebe2d4SWarner Losh # Unified loader.conf: always load ufs, zfs, and cd9660 402*a7ebe2d4SWarner Losh cat > ${DESTDIR}/boot/loader.conf <<EOF 403*a7ebe2d4SWarner Loshboot_serial=YES 404*a7ebe2d4SWarner Loshcomconsole_speed=115200 405*a7ebe2d4SWarner Loshautoboot_delay=1 406*a7ebe2d4SWarner Loshufs_load="YES" 407*a7ebe2d4SWarner Loshzfs_load="YES" 408*a7ebe2d4SWarner Loshcd9660_load="YES" 409*a7ebe2d4SWarner LoshEOF 410*a7ebe2d4SWarner Losh local hints=$(param hints) 411*a7ebe2d4SWarner Losh if [ -n "${hints}" ] && [ -f "${SRCTOP}/${hints}" ]; then 412*a7ebe2d4SWarner Losh cp "${SRCTOP}/${hints}" ${DESTDIR}/boot/device.hints 413*a7ebe2d4SWarner Losh fi 414*a7ebe2d4SWarner Losh 415*a7ebe2d4SWarner Losh # Test /etc/rc - prints success and halts 416*a7ebe2d4SWarner Losh cat > ${DESTDIR}/etc/rc <<'RCEOF' 417*a7ebe2d4SWarner Losh#!/bin/sh 418*a7ebe2d4SWarner Losh 419*a7ebe2d4SWarner Loshsysctl machdep.bootmethod 420*a7ebe2d4SWarner Loshecho "RC COMMAND RUNNING -- SUCCESS!!!!!" 421*a7ebe2d4SWarner Loshhalt -p 422*a7ebe2d4SWarner LoshRCEOF 423*a7ebe2d4SWarner Losh chmod +x ${DESTDIR}/etc/rc 424*a7ebe2d4SWarner Losh 425*a7ebe2d4SWarner Losh # Create fstab used by UFS mtree overlays 426*a7ebe2d4SWarner Losh cat > ${OUTDIR}/fstab.ufs <<EOF 427*a7ebe2d4SWarner Losh/dev/ufs/root / ufs rw 1 1 428*a7ebe2d4SWarner LoshEOF 429*a7ebe2d4SWarner Losh # Create fstab used by CD mtree overlays 430*a7ebe2d4SWarner Losh cat > ${OUTDIR}/fstab.cd <<EOF 431*a7ebe2d4SWarner Losh/dev/iso9660/FBSDTEST / cd9660 ro 0 0 432*a7ebe2d4SWarner LoshEOF 433*a7ebe2d4SWarner Losh 434*a7ebe2d4SWarner Losh echo "Boot tree built in ${DESTDIR}" 435*a7ebe2d4SWarner Losh} 436*a7ebe2d4SWarner Losh 437*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 438*a7ebe2d4SWarner Losh# Phase 2: Create base filesystem images 439*a7ebe2d4SWarner Losh# 440*a7ebe2d4SWarner Losh# Uses mtree overlays to vary the /boot/loader and /etc/fstab 441*a7ebe2d4SWarner Losh# without copying the tree. The base tree has no /boot/loader or 442*a7ebe2d4SWarner Losh# /etc/fstab; each image adds what it needs via an mtree spec file 443*a7ebe2d4SWarner Losh# passed as a second source to makefs. 444*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 445*a7ebe2d4SWarner Losh 446*a7ebe2d4SWarner Losh# Create a UFS image with a specific loader variant via mtree overlay 447*a7ebe2d4SWarner Loshmake_one_ufs() { 448*a7ebe2d4SWarner Losh variant=$1 449*a7ebe2d4SWarner Losh img=${IMGDIR}/bootable-ufs-${variant}.img 450*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/ufs-mtree.XXXXXX) 451*a7ebe2d4SWarner Losh 452*a7ebe2d4SWarner Losh echo " Creating UFS image with loader_${variant}..." 453*a7ebe2d4SWarner Losh echo "./etc/fstab type=file mode=0644 contents=${OUTDIR}/fstab.ufs" > ${mt} 454*a7ebe2d4SWarner Losh # BIOS: /boot/loader -> loader_<variant> 455*a7ebe2d4SWarner Losh if [ -n "$(param bios_loaders)" ]; then 456*a7ebe2d4SWarner Losh echo "./boot/loader type=file mode=0644 contents=${DESTDIR}/boot/loader_${variant}" >> ${mt} 457*a7ebe2d4SWarner Losh fi 458*a7ebe2d4SWarner Losh # OFW: boot1.hfs chainloads the single /boot/loader already in the 459*a7ebe2d4SWarner Losh # tree (no per-variant overlay needed). 460*a7ebe2d4SWarner Losh # EFI: /boot/loader.efi -> loader_<variant>.efi (needed for boot1.efi chainload) 461*a7ebe2d4SWarner Losh if has efi; then 462*a7ebe2d4SWarner Losh echo "./boot/loader.efi type=file mode=0755 contents=${DESTDIR}/boot/loader_${variant}.efi" >> ${mt} 463*a7ebe2d4SWarner Losh fi 464*a7ebe2d4SWarner Losh makefs -t ffs -B $(param byte_order) -M 10m -o label=root -o version=2 \ 465*a7ebe2d4SWarner Losh ${img} ${mt} ${DESTDIR} >> ${LOGDIR}/imagebuild.log 2>&1 466*a7ebe2d4SWarner Losh rm -f ${mt} 467*a7ebe2d4SWarner Losh} 468*a7ebe2d4SWarner Losh 469*a7ebe2d4SWarner Losh# Create a ZFS image with a specific loader variant via mtree overlay 470*a7ebe2d4SWarner Loshmake_one_zfs() { 471*a7ebe2d4SWarner Losh variant=$1 472*a7ebe2d4SWarner Losh img=${IMGDIR}/bootable-zfs-${variant}.img 473*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/zfs-mtree.XXXXXX) 474*a7ebe2d4SWarner Losh 475*a7ebe2d4SWarner Losh echo " Creating ZFS image with loader_${variant}..." 476*a7ebe2d4SWarner Losh > ${mt} 477*a7ebe2d4SWarner Losh # BIOS: /boot/loader -> loader_<variant> 478*a7ebe2d4SWarner Losh if [ -n "$(param bios_loaders)" ]; then 479*a7ebe2d4SWarner Losh echo "./boot/loader type=link link=loader_${variant}" >> ${mt} 480*a7ebe2d4SWarner Losh fi 481*a7ebe2d4SWarner Losh # OFW: boot1.hfs chainloads the single /boot/loader already in the 482*a7ebe2d4SWarner Losh # tree (no per-variant overlay needed). 483*a7ebe2d4SWarner Losh # EFI: /boot/loader.efi -> loader_<variant>.efi (needed for boot1.efi chainload) 484*a7ebe2d4SWarner Losh if has efi; then 485*a7ebe2d4SWarner Losh echo "./boot/loader.efi type=file mode=0755 contents=${DESTDIR}/boot/loader_${variant}.efi" >> ${mt} 486*a7ebe2d4SWarner Losh fi 487*a7ebe2d4SWarner Losh makefs -t zfs -s 100m \ 488*a7ebe2d4SWarner Losh -o poolname=ztestroot -o bootfs=ztestroot -o rootpath=/ \ 489*a7ebe2d4SWarner Losh ${img} ${mt} ${DESTDIR} >> ${LOGDIR}/imagebuild.log 2>&1 490*a7ebe2d4SWarner Losh rm -f ${mt} 491*a7ebe2d4SWarner Losh} 492*a7ebe2d4SWarner Losh 493*a7ebe2d4SWarner Losh# Create an ESP with the given EFI loader using an mtree spec 494*a7ebe2d4SWarner Loshmake_one_esp() { 495*a7ebe2d4SWarner Losh loader_name=$1 496*a7ebe2d4SWarner Losh esp=${IMGDIR}/${loader_name}.esp 497*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/esp-mtree.XXXXXX) 498*a7ebe2d4SWarner Losh 499*a7ebe2d4SWarner Losh echo " Creating ESP with ${loader_name}.efi..." 500*a7ebe2d4SWarner Losh cat > ${mt} <<EOF 501*a7ebe2d4SWarner Losh./efi type=dir uname=root gname=wheel mode=0755 502*a7ebe2d4SWarner Losh./efi/boot type=dir uname=root gname=wheel mode=0755 503*a7ebe2d4SWarner Losh./efi/boot/$(param efi_bootname).efi type=file uname=root gname=wheel mode=0755 contents=${DESTDIR}/boot/${loader_name}.efi 504*a7ebe2d4SWarner LoshEOF 505*a7ebe2d4SWarner Losh makefs -t msdos \ 506*a7ebe2d4SWarner Losh -o fat_type=32 \ 507*a7ebe2d4SWarner Losh -o sectors_per_cluster=1 \ 508*a7ebe2d4SWarner Losh -o volume_label=EFISYS \ 509*a7ebe2d4SWarner Losh -s ${espsize}k \ 510*a7ebe2d4SWarner Losh ${esp} ${mt} >> ${LOGDIR}/imagebuild.log 2>&1 511*a7ebe2d4SWarner Losh rm -f ${mt} 512*a7ebe2d4SWarner Losh} 513*a7ebe2d4SWarner Losh 514*a7ebe2d4SWarner Losh# Create a small ESP for CD hybrid boot using an mtree spec 515*a7ebe2d4SWarner Loshmake_cd_esp() { 516*a7ebe2d4SWarner Losh file=$1 517*a7ebe2d4SWarner Losh loader=$2 518*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/cd-esp-mtree.XXXXXX) 519*a7ebe2d4SWarner Losh 520*a7ebe2d4SWarner Losh cat > ${mt} <<EOF 521*a7ebe2d4SWarner Losh./efi type=dir uname=root gname=wheel mode=0755 522*a7ebe2d4SWarner Losh./efi/boot type=dir uname=root gname=wheel mode=0755 523*a7ebe2d4SWarner Losh./efi/boot/$(param efi_bootname).efi type=file uname=root gname=wheel mode=0755 contents=${loader} 524*a7ebe2d4SWarner LoshEOF 525*a7ebe2d4SWarner Losh makefs -t msdos \ 526*a7ebe2d4SWarner Losh -o fat_type=12 \ 527*a7ebe2d4SWarner Losh -o sectors_per_cluster=1 \ 528*a7ebe2d4SWarner Losh -o volume_label=EFISYS \ 529*a7ebe2d4SWarner Losh -s 2048k \ 530*a7ebe2d4SWarner Losh ${file} ${mt} >> ${LOGDIR}/imagebuild.log 2>&1 531*a7ebe2d4SWarner Losh rm -f ${mt} 532*a7ebe2d4SWarner Losh} 533*a7ebe2d4SWarner Losh 534*a7ebe2d4SWarner Losh# Find the pre-built Linux kernel EFI binary for linuxboot 535*a7ebe2d4SWarner Loshfind_linux_kernel() { 536*a7ebe2d4SWarner Losh target_arch=$(${MK} -v TARGET_ARCH) 537*a7ebe2d4SWarner Losh linuxboot_dir=${ARCH_OBJDIR}/../../linuxboot/data/output 538*a7ebe2d4SWarner Losh 539*a7ebe2d4SWarner Losh # amd64 has .efi suffix, others don't 540*a7ebe2d4SWarner Losh for f in \ 541*a7ebe2d4SWarner Losh ${linuxboot_dir}/${target_arch}.linux.v${LINUX_VERSION}.efi \ 542*a7ebe2d4SWarner Losh ${linuxboot_dir}/${target_arch}.linux.v${LINUX_VERSION} \ 543*a7ebe2d4SWarner Losh ${linuxboot_dir}/${target_arch}.v${LINUX_VERSION}.efi \ 544*a7ebe2d4SWarner Losh ${linuxboot_dir}/${target_arch}.v${LINUX_VERSION}; do 545*a7ebe2d4SWarner Losh if [ -f "$f" ]; then 546*a7ebe2d4SWarner Losh echo "$f" 547*a7ebe2d4SWarner Losh return 548*a7ebe2d4SWarner Losh fi 549*a7ebe2d4SWarner Losh done 550*a7ebe2d4SWarner Losh return 1 551*a7ebe2d4SWarner Losh} 552*a7ebe2d4SWarner Losh 553*a7ebe2d4SWarner Losh# Build a linuxboot initrd using tar --format newc with an mtree spec. 554*a7ebe2d4SWarner Losh# No root/sudo required -- device nodes are written directly into the 555*a7ebe2d4SWarner Losh# cpio archive via mtree type=char entries. 556*a7ebe2d4SWarner Loshmake_linuxboot_initrd() { 557*a7ebe2d4SWarner Losh initrd=${IMGDIR}/linuxboot-initrd.cpio.gz 558*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/initrd-mtree.XXXXXX) 559*a7ebe2d4SWarner Losh 560*a7ebe2d4SWarner Losh echo " Creating linuxboot initrd..." 561*a7ebe2d4SWarner Losh 562*a7ebe2d4SWarner Losh # Build mtree spec for the initrd contents 563*a7ebe2d4SWarner Losh cat > ${mt} <<EOF 564*a7ebe2d4SWarner Losh./init type=file mode=0755 contents=${DESTDIR}/boot/loader.kboot 565*a7ebe2d4SWarner Losh./dev type=dir mode=0755 566*a7ebe2d4SWarner Losh./dev/console type=char mode=0600 device=freebsd,5,0 567*a7ebe2d4SWarner Losh./dev/tty type=char mode=0600 device=freebsd,5,1 568*a7ebe2d4SWarner Losh./dev/ttyS0 type=char mode=0600 device=freebsd,4,64 569*a7ebe2d4SWarner Losh./boot type=dir mode=0755 570*a7ebe2d4SWarner Losh./boot/defaults type=dir mode=0755 571*a7ebe2d4SWarner Losh./boot/defaults/loader.conf type=file mode=0644 contents=${DESTDIR}/boot/defaults/loader.conf 572*a7ebe2d4SWarner Losh./boot/lua type=dir mode=0755 573*a7ebe2d4SWarner LoshEOF 574*a7ebe2d4SWarner Losh 575*a7ebe2d4SWarner Losh # Add all lua files 576*a7ebe2d4SWarner Losh for f in ${DESTDIR}/boot/lua/*.lua; do 577*a7ebe2d4SWarner Losh [ -f "$f" ] || continue 578*a7ebe2d4SWarner Losh bn=$(basename $f) 579*a7ebe2d4SWarner Losh echo "./boot/lua/${bn} type=file mode=0644 contents=$f" >> ${mt} 580*a7ebe2d4SWarner Losh done 581*a7ebe2d4SWarner Losh 582*a7ebe2d4SWarner Losh # Add loader.help.kboot if present 583*a7ebe2d4SWarner Losh if [ -f "${DESTDIR}/boot/loader.help.kboot" ]; then 584*a7ebe2d4SWarner Losh echo "./boot/loader.help.kboot type=file mode=0644 contents=${DESTDIR}/boot/loader.help.kboot" >> ${mt} 585*a7ebe2d4SWarner Losh fi 586*a7ebe2d4SWarner Losh 587*a7ebe2d4SWarner Losh # Create the kboot-specific loader.conf 588*a7ebe2d4SWarner Losh kboot_conf=$(mktemp ${OUTDIR}/kboot-loader-conf.XXXXXX) 589*a7ebe2d4SWarner Losh cat > ${kboot_conf} <<EOF 590*a7ebe2d4SWarner Losh# Kboot configuration -- FreeBSD ${ARCH} 591*a7ebe2d4SWarner Loshboot_serial="YES" 592*a7ebe2d4SWarner LoshEOF 593*a7ebe2d4SWarner Losh if [ "${ARCH}" = "amd64" ]; then 594*a7ebe2d4SWarner Losh cat >> ${kboot_conf} <<EOF 595*a7ebe2d4SWarner Loshhw.uart.console="io:1016,br:115200" 596*a7ebe2d4SWarner LoshEOF 597*a7ebe2d4SWarner Losh fi 598*a7ebe2d4SWarner Losh echo "./boot/loader.conf type=file mode=0644 contents=${kboot_conf}" >> ${mt} 599*a7ebe2d4SWarner Losh 600*a7ebe2d4SWarner Losh # Create the initrd as a gzip-compressed newc cpio archive 601*a7ebe2d4SWarner Losh tar --format newc -cf - @${mt} 2>> ${LOGDIR}/imagebuild.log | \ 602*a7ebe2d4SWarner Losh gzip > ${initrd} 603*a7ebe2d4SWarner Losh 604*a7ebe2d4SWarner Losh rm -f ${mt} ${kboot_conf} 605*a7ebe2d4SWarner Losh} 606*a7ebe2d4SWarner Losh 607*a7ebe2d4SWarner Losh# Build a linuxboot ESP containing the Linux kernel, initrd, and startup.nsh 608*a7ebe2d4SWarner Loshmake_linuxboot_esp() { 609*a7ebe2d4SWarner Losh linux_kernel=$1 610*a7ebe2d4SWarner Losh esp=${IMGDIR}/linuxboot.esp 611*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/linuxboot-esp-mtree.XXXXXX) 612*a7ebe2d4SWarner Losh 613*a7ebe2d4SWarner Losh echo " Creating linuxboot ESP..." 614*a7ebe2d4SWarner Losh 615*a7ebe2d4SWarner Losh # Generate startup.nsh 616*a7ebe2d4SWarner Losh startup=${OUTDIR}/startup.nsh 617*a7ebe2d4SWarner Losh cat > ${startup} <<EOF 618*a7ebe2d4SWarner Losh\\linux.efi $(param linux_console) initrd=\\initrd 619*a7ebe2d4SWarner LoshEOF 620*a7ebe2d4SWarner Losh 621*a7ebe2d4SWarner Losh cat > ${mt} <<EOF 622*a7ebe2d4SWarner Losh./startup.nsh type=file mode=0644 contents=${startup} 623*a7ebe2d4SWarner Losh./linux.efi type=file mode=0755 contents=${linux_kernel} 624*a7ebe2d4SWarner Losh./initrd type=file mode=0644 contents=${IMGDIR}/linuxboot-initrd.cpio.gz 625*a7ebe2d4SWarner LoshEOF 626*a7ebe2d4SWarner Losh makefs -t msdos \ 627*a7ebe2d4SWarner Losh -o fat_type=32 \ 628*a7ebe2d4SWarner Losh -o sectors_per_cluster=1 \ 629*a7ebe2d4SWarner Losh -o volume_label=EFISYS \ 630*a7ebe2d4SWarner Losh -s 100m \ 631*a7ebe2d4SWarner Losh ${esp} ${mt} >> ${LOGDIR}/imagebuild.log 2>&1 632*a7ebe2d4SWarner Losh rm -f ${mt} 633*a7ebe2d4SWarner Losh} 634*a7ebe2d4SWarner Losh 635*a7ebe2d4SWarner Loshmake_base_images() { 636*a7ebe2d4SWarner Losh echo "=== Phase 2: Creating base filesystem images ===" 637*a7ebe2d4SWarner Losh 638*a7ebe2d4SWarner Losh # UFS images - one per BIOS loader variant, plus one for EFI (uses lua) 639*a7ebe2d4SWarner Losh if [ -n "$(param bios_loaders)" ]; then 640*a7ebe2d4SWarner Losh for v in $(param bios_loaders); do 641*a7ebe2d4SWarner Losh make_one_ufs $v 642*a7ebe2d4SWarner Losh done 643*a7ebe2d4SWarner Losh else 644*a7ebe2d4SWarner Losh # EFI-only arches still need one UFS image 645*a7ebe2d4SWarner Losh make_one_ufs lua 646*a7ebe2d4SWarner Losh fi 647*a7ebe2d4SWarner Losh 648*a7ebe2d4SWarner Losh # ZFS images (if supported) 649*a7ebe2d4SWarner Losh if has zfs; then 650*a7ebe2d4SWarner Losh if [ -n "$(param bios_loaders)" ]; then 651*a7ebe2d4SWarner Losh for v in $(param bios_loaders); do 652*a7ebe2d4SWarner Losh make_one_zfs $v 653*a7ebe2d4SWarner Losh done 654*a7ebe2d4SWarner Losh else 655*a7ebe2d4SWarner Losh make_one_zfs lua 656*a7ebe2d4SWarner Losh fi 657*a7ebe2d4SWarner Losh fi 658*a7ebe2d4SWarner Losh 659*a7ebe2d4SWarner Losh # ESP images (if EFI is supported) 660*a7ebe2d4SWarner Losh if has efi; then 661*a7ebe2d4SWarner Losh for l in $(param efi_loaders); do 662*a7ebe2d4SWarner Losh make_one_esp $l 663*a7ebe2d4SWarner Losh done 664*a7ebe2d4SWarner Losh fi 665*a7ebe2d4SWarner Losh 666*a7ebe2d4SWarner Losh # Linuxboot initrd + ESP (if supported and Linux kernel is available) 667*a7ebe2d4SWarner Losh if has linuxboot; then 668*a7ebe2d4SWarner Losh linux_kernel=$(find_linux_kernel) || true 669*a7ebe2d4SWarner Losh if [ -n "${linux_kernel}" ]; then 670*a7ebe2d4SWarner Losh make_linuxboot_initrd 671*a7ebe2d4SWarner Losh # EFI arches chainload the kernel+initrd off an ESP; platforms 672*a7ebe2d4SWarner Losh # without EFI hand them to QEMU directly (-kernel/-initrd), so 673*a7ebe2d4SWarner Losh # they need no ESP. 674*a7ebe2d4SWarner Losh has efi && make_linuxboot_esp ${linux_kernel} 675*a7ebe2d4SWarner Losh else 676*a7ebe2d4SWarner Losh echo " WARNING: Linux kernel not found for linuxboot, skipping" 677*a7ebe2d4SWarner Losh echo " Expected in: ${ARCH_OBJDIR}/../../linuxboot/data/output/" 678*a7ebe2d4SWarner Losh fi 679*a7ebe2d4SWarner Losh fi 680*a7ebe2d4SWarner Losh 681*a7ebe2d4SWarner Losh echo "Base images created in ${IMGDIR}" 682*a7ebe2d4SWarner Losh} 683*a7ebe2d4SWarner Losh 684*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 685*a7ebe2d4SWarner Losh# Phase 3: Assemble disk images and register tests 686*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 687*a7ebe2d4SWarner Losh 688*a7ebe2d4SWarner Losh# Test registration - uses temp files since /bin/sh doesn't have arrays. 689*a7ebe2d4SWarner Losh# ${TESTLIST} and ${OUTDIR} are set per-arch by setup_arch_env; the list is 690*a7ebe2d4SWarner Losh# truncated in build_all before each arch's images are (re)assembled. 691*a7ebe2d4SWarner Loshregister_test() { 692*a7ebe2d4SWarner Losh name=$1 693*a7ebe2d4SWarner Losh shift 694*a7ebe2d4SWarner Losh echo "$*" > ${OUTDIR}/test-cmd-${name}.sh 695*a7ebe2d4SWarner Losh echo "${name}" >> ${TESTLIST} 696*a7ebe2d4SWarner Losh} 697*a7ebe2d4SWarner Losh 698*a7ebe2d4SWarner Losh# Like register_test, but for tests that need a real tap(4) interface 699*a7ebe2d4SWarner Losh# (assigned later by netboot_network_setup, once every arch is built) instead 700*a7ebe2d4SWarner Losh# of QEMU's slirp net. The command is written out now with a placeholder tap 701*a7ebe2d4SWarner Losh# name; netboot_network_setup patches it in once the tap is assigned. 702*a7ebe2d4SWarner Losh# ${NETBOOT_PLAN} accumulates across every arch (unlike ${TESTLIST}, it is not 703*a7ebe2d4SWarner Losh# truncated per-arch), so it must already exist by the time build_all runs -- 704*a7ebe2d4SWarner Losh# see Main. $5 optionally names a different command builder than the default 705*a7ebe2d4SWarner Losh# qemu_netboot (e.g. qemu_netboot_ramdisk) -- it's always called as 706*a7ebe2d4SWarner Losh# "builder __NETBOOT_TAP__ ${fw}", so a non-default builder that doesn't need 707*a7ebe2d4SWarner Losh# ${fw} just ignores its second arg. 708*a7ebe2d4SWarner Loshregister_netboot_test() { 709*a7ebe2d4SWarner Losh name=$1 710*a7ebe2d4SWarner Losh tftpdir=$2 711*a7ebe2d4SWarner Losh bootfile=$3 712*a7ebe2d4SWarner Losh fw=$4 713*a7ebe2d4SWarner Losh builder=${5:-qemu_netboot} 714*a7ebe2d4SWarner Losh cmdfile=${OUTDIR}/test-cmd-${name}.sh 715*a7ebe2d4SWarner Losh echo "$(${builder} __NETBOOT_TAP__ ${fw})" > ${cmdfile} 716*a7ebe2d4SWarner Losh echo "${name}" >> ${TESTLIST} 717*a7ebe2d4SWarner Losh echo "${cmdfile} ${tftpdir} ${bootfile}" >> ${NETBOOT_PLAN} 718*a7ebe2d4SWarner Losh} 719*a7ebe2d4SWarner Losh 720*a7ebe2d4SWarner Loshassemble_efi_gpt() { 721*a7ebe2d4SWarner Losh echo " Assembling EFI+GPT images..." 722*a7ebe2d4SWarner Losh for loader in $(param efi_loaders); do 723*a7ebe2d4SWarner Losh esp=${IMGDIR}/${loader}.esp 724*a7ebe2d4SWarner Losh fstypes="ufs" 725*a7ebe2d4SWarner Losh has zfs && fstypes="ufs zfs" 726*a7ebe2d4SWarner Losh for fs in ${fstypes}; do 727*a7ebe2d4SWarner Losh name="efi-gpt-${fs}-${loader}" 728*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 729*a7ebe2d4SWarner Losh 730*a7ebe2d4SWarner Losh # For EFI, the stage-3 in the filesystem doesn't matter, use lua variant 731*a7ebe2d4SWarner Losh case ${fs} in 732*a7ebe2d4SWarner Losh ufs) fsimg=${IMGDIR}/bootable-ufs-lua.img; ptype="freebsd-ufs" ;; 733*a7ebe2d4SWarner Losh zfs) fsimg=${IMGDIR}/bootable-zfs-lua.img; ptype="freebsd-zfs" ;; 734*a7ebe2d4SWarner Losh esac 735*a7ebe2d4SWarner Losh 736*a7ebe2d4SWarner Losh mkimg -s gpt \ 737*a7ebe2d4SWarner Losh -p efi:=${esp} \ 738*a7ebe2d4SWarner Losh -p ${ptype}:=${fsimg} \ 739*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 740*a7ebe2d4SWarner Losh 741*a7ebe2d4SWarner Losh register_test ${name} $(qemu_efi ${img}) 742*a7ebe2d4SWarner Losh done 743*a7ebe2d4SWarner Losh done 744*a7ebe2d4SWarner Losh} 745*a7ebe2d4SWarner Losh 746*a7ebe2d4SWarner Loshassemble_efi_mbr() { 747*a7ebe2d4SWarner Losh echo " Assembling EFI+MBR images..." 748*a7ebe2d4SWarner Losh for loader in $(param efi_loaders); do 749*a7ebe2d4SWarner Losh esp=${IMGDIR}/${loader}.esp 750*a7ebe2d4SWarner Losh name="efi-mbr-ufs-${loader}" 751*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 752*a7ebe2d4SWarner Losh ufs=${IMGDIR}/bootable-ufs-lua.img 753*a7ebe2d4SWarner Losh 754*a7ebe2d4SWarner Losh mkimg -s bsd -p freebsd-ufs:=${ufs} -o ${img}.s2 >> ${LOGDIR}/imagebuild.log 2>&1 755*a7ebe2d4SWarner Losh mkimg -a 1 -s mbr -p efi:=${esp} -p freebsd:=${img}.s2 -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 756*a7ebe2d4SWarner Losh rm -f ${img}.s2 757*a7ebe2d4SWarner Losh 758*a7ebe2d4SWarner Losh register_test ${name} $(qemu_efi ${img}) 759*a7ebe2d4SWarner Losh done 760*a7ebe2d4SWarner Losh} 761*a7ebe2d4SWarner Losh 762*a7ebe2d4SWarner Loshassemble_bios_gpt() { 763*a7ebe2d4SWarner Losh echo " Assembling BIOS+GPT images..." 764*a7ebe2d4SWarner Losh for variant in $(param bios_loaders); do 765*a7ebe2d4SWarner Losh # UFS 766*a7ebe2d4SWarner Losh name="bios-gpt-ufs-loader_${variant}" 767*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 768*a7ebe2d4SWarner Losh ufs=${IMGDIR}/bootable-ufs-${variant}.img 769*a7ebe2d4SWarner Losh 770*a7ebe2d4SWarner Losh mkimg -s gpt -b ${DESTDIR}/boot/pmbr \ 771*a7ebe2d4SWarner Losh -p freebsd-boot:=${DESTDIR}/boot/gptboot \ 772*a7ebe2d4SWarner Losh -p freebsd-ufs:=${ufs} \ 773*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 774*a7ebe2d4SWarner Losh 775*a7ebe2d4SWarner Losh register_test ${name} $(qemu_bios ${img}) 776*a7ebe2d4SWarner Losh 777*a7ebe2d4SWarner Losh # ZFS 778*a7ebe2d4SWarner Losh if has zfs; then 779*a7ebe2d4SWarner Losh name="bios-gpt-zfs-loader_${variant}" 780*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 781*a7ebe2d4SWarner Losh zfs=${IMGDIR}/bootable-zfs-${variant}.img 782*a7ebe2d4SWarner Losh 783*a7ebe2d4SWarner Losh mkimg -s gpt -b ${DESTDIR}/boot/pmbr \ 784*a7ebe2d4SWarner Losh -p freebsd-boot:=${DESTDIR}/boot/gptzfsboot \ 785*a7ebe2d4SWarner Losh -p freebsd-zfs:=${zfs} \ 786*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 787*a7ebe2d4SWarner Losh 788*a7ebe2d4SWarner Losh register_test ${name} $(qemu_bios ${img}) 789*a7ebe2d4SWarner Losh fi 790*a7ebe2d4SWarner Losh done 791*a7ebe2d4SWarner Losh} 792*a7ebe2d4SWarner Losh 793*a7ebe2d4SWarner Loshassemble_bios_mbr() { 794*a7ebe2d4SWarner Losh echo " Assembling BIOS+MBR images..." 795*a7ebe2d4SWarner Losh for variant in $(param bios_loaders); do 796*a7ebe2d4SWarner Losh name="bios-mbr-ufs-loader_${variant}" 797*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 798*a7ebe2d4SWarner Losh ufs=${IMGDIR}/bootable-ufs-${variant}.img 799*a7ebe2d4SWarner Losh 800*a7ebe2d4SWarner Losh mkimg -s bsd -b ${DESTDIR}/boot/boot \ 801*a7ebe2d4SWarner Losh -p freebsd-ufs:=${ufs} -o ${img}.s1 >> ${LOGDIR}/imagebuild.log 2>&1 802*a7ebe2d4SWarner Losh # Note: boot0sio has a longish timeout, and does work but 803*a7ebe2d4SWarner Losh # takes longer than 30s so we use mbr. 804*a7ebe2d4SWarner Losh mkimg -a 1 -s mbr -b ${DESTDIR}/boot/mbr \ 805*a7ebe2d4SWarner Losh -p freebsd:=${img}.s1 -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 806*a7ebe2d4SWarner Losh rm -f ${img}.s1 807*a7ebe2d4SWarner Losh 808*a7ebe2d4SWarner Losh register_test ${name} $(qemu_bios ${img}) 809*a7ebe2d4SWarner Losh done 810*a7ebe2d4SWarner Losh} 811*a7ebe2d4SWarner Losh 812*a7ebe2d4SWarner Losh# Hybrid GPT images: ESP + freebsd-boot + filesystem, tested with both BIOS and EFI 813*a7ebe2d4SWarner Loshassemble_both_gpt() { 814*a7ebe2d4SWarner Losh echo " Assembling hybrid BIOS+EFI GPT images..." 815*a7ebe2d4SWarner Losh for l in $(param bios_loaders); do 816*a7ebe2d4SWarner Losh loader="loader_${l}" 817*a7ebe2d4SWarner Losh esp=${IMGDIR}/${loader}.esp 818*a7ebe2d4SWarner Losh fstypes="ufs" 819*a7ebe2d4SWarner Losh has zfs && fstypes="ufs zfs" 820*a7ebe2d4SWarner Losh for fs in ${fstypes}; do 821*a7ebe2d4SWarner Losh name_base="both-gpt-${fs}-${loader}" 822*a7ebe2d4SWarner Losh img=${IMGDIR}/${name_base}.img 823*a7ebe2d4SWarner Losh 824*a7ebe2d4SWarner Losh case ${fs} in 825*a7ebe2d4SWarner Losh ufs) 826*a7ebe2d4SWarner Losh fsimg=${IMGDIR}/bootable-ufs-lua.img 827*a7ebe2d4SWarner Losh ptype="freebsd-ufs" 828*a7ebe2d4SWarner Losh bootblk=${DESTDIR}/boot/gptboot 829*a7ebe2d4SWarner Losh ;; 830*a7ebe2d4SWarner Losh zfs) 831*a7ebe2d4SWarner Losh fsimg=${IMGDIR}/bootable-zfs-lua.img 832*a7ebe2d4SWarner Losh ptype="freebsd-zfs" 833*a7ebe2d4SWarner Losh bootblk=${DESTDIR}/boot/gptzfsboot 834*a7ebe2d4SWarner Losh ;; 835*a7ebe2d4SWarner Losh esac 836*a7ebe2d4SWarner Losh 837*a7ebe2d4SWarner Losh mkimg -b ${DESTDIR}/boot/pmbr -s gpt \ 838*a7ebe2d4SWarner Losh -p efi:=${esp} \ 839*a7ebe2d4SWarner Losh -p freebsd-boot:=${bootblk} \ 840*a7ebe2d4SWarner Losh -p ${ptype}:=${fsimg} \ 841*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 842*a7ebe2d4SWarner Losh 843*a7ebe2d4SWarner Losh register_test "${name_base}-bios" $(qemu_bios ${img}) 844*a7ebe2d4SWarner Losh register_test "${name_base}-efi" $(qemu_efi ${img}) 845*a7ebe2d4SWarner Losh done 846*a7ebe2d4SWarner Losh done 847*a7ebe2d4SWarner Losh} 848*a7ebe2d4SWarner Losh 849*a7ebe2d4SWarner Losh# Hybrid MBR images: ESP + freebsd(boot+ufs), tested with both BIOS and EFI 850*a7ebe2d4SWarner Loshassemble_both_mbr() { 851*a7ebe2d4SWarner Losh echo " Assembling hybrid BIOS+EFI MBR images..." 852*a7ebe2d4SWarner Losh for l in $(param bios_loaders); do 853*a7ebe2d4SWarner Losh loader="loader_${l}" 854*a7ebe2d4SWarner Losh esp=${IMGDIR}/${loader}.esp 855*a7ebe2d4SWarner Losh name_base="both-mbr-ufs-${loader}" 856*a7ebe2d4SWarner Losh img=${IMGDIR}/${name_base}.img 857*a7ebe2d4SWarner Losh ufs=${IMGDIR}/bootable-ufs-lua.img 858*a7ebe2d4SWarner Losh 859*a7ebe2d4SWarner Losh mkimg -s bsd -b ${DESTDIR}/boot/boot \ 860*a7ebe2d4SWarner Losh -p freebsd-ufs:=${ufs} -o ${img}.s2 >> ${LOGDIR}/imagebuild.log 2>&1 861*a7ebe2d4SWarner Losh mkimg -a 2 -s mbr -b ${DESTDIR}/boot/mbr \ 862*a7ebe2d4SWarner Losh -p efi:=${esp} \ 863*a7ebe2d4SWarner Losh -p freebsd:=${img}.s2 -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 864*a7ebe2d4SWarner Losh rm -f ${img}.s2 865*a7ebe2d4SWarner Losh 866*a7ebe2d4SWarner Losh register_test "${name_base}-bios" $(qemu_bios ${img}) 867*a7ebe2d4SWarner Losh register_test "${name_base}-efi" $(qemu_efi ${img}) 868*a7ebe2d4SWarner Losh done 869*a7ebe2d4SWarner Losh} 870*a7ebe2d4SWarner Losh 871*a7ebe2d4SWarner Loshassemble_cd() { 872*a7ebe2d4SWarner Losh echo " Assembling CD images..." 873*a7ebe2d4SWarner Losh 874*a7ebe2d4SWarner Losh # mtree to add loader and fstab to image 875*a7ebe2d4SWarner Losh mt1=$(mktemp ${OUTDIR}/cd-mtree.XXXXXX) 876*a7ebe2d4SWarner Losh variant=lua 877*a7ebe2d4SWarner Losh cat > ${mt1} <<EOF 878*a7ebe2d4SWarner Losh./boot/loader type=file mode=0644 contents=${DESTDIR}/boot/loader_${variant} 879*a7ebe2d4SWarner Losh./etc/fstab type=file mode=0644 contents=${OUTDIR}/fstab.cd 880*a7ebe2d4SWarner LoshEOF 881*a7ebe2d4SWarner Losh 882*a7ebe2d4SWarner Losh # cdboot - BIOS CD boot via El Torito 883*a7ebe2d4SWarner Losh name="bios-cd-cdboot" 884*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.iso 885*a7ebe2d4SWarner Losh makefs -t cd9660 \ 886*a7ebe2d4SWarner Losh -o bootimage=i386\;${DESTDIR}/boot/cdboot \ 887*a7ebe2d4SWarner Losh -o no-emul-boot \ 888*a7ebe2d4SWarner Losh -o rockridge \ 889*a7ebe2d4SWarner Losh -o label=FBSDTEST \ 890*a7ebe2d4SWarner Losh ${img} ${mt1} ${DESTDIR} >> ${LOGDIR}/imagebuild.log 2>&1 891*a7ebe2d4SWarner Losh register_test ${name} $(qemu_bios_cdrom ${img}) 892*a7ebe2d4SWarner Losh 893*a7ebe2d4SWarner Losh # isoboot - hybrid BIOS+EFI CD (tests isoboot for BIOS, loader.efi for EFI) 894*a7ebe2d4SWarner Losh name="hybrid-cd-isoboot" 895*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.iso 896*a7ebe2d4SWarner Losh espfile=$(mktemp ${OUTDIR}/efiboot.XXXXXX) 897*a7ebe2d4SWarner Losh make_cd_esp ${espfile} ${DESTDIR}/boot/loader_lua.efi 898*a7ebe2d4SWarner Losh 899*a7ebe2d4SWarner Losh makefs -t cd9660 \ 900*a7ebe2d4SWarner Losh -o bootimage=i386\;${DESTDIR}/boot/cdboot \ 901*a7ebe2d4SWarner Losh -o no-emul-boot \ 902*a7ebe2d4SWarner Losh -o bootimage=i386\;${espfile} \ 903*a7ebe2d4SWarner Losh -o no-emul-boot \ 904*a7ebe2d4SWarner Losh -o platformid=efi \ 905*a7ebe2d4SWarner Losh -o rockridge \ 906*a7ebe2d4SWarner Losh -o label=FBSDTEST \ 907*a7ebe2d4SWarner Losh ${img} ${mt1} ${DESTDIR} >> ${LOGDIR}/imagebuild.log 2>&1 908*a7ebe2d4SWarner Losh 909*a7ebe2d4SWarner Losh # Overlay hybrid GPT for isoboot 910*a7ebe2d4SWarner Losh imgsize=$(stat -f %z "${img}") 911*a7ebe2d4SWarner Losh 912*a7ebe2d4SWarner Losh # Find the EFI partition in the ISO to reference it 913*a7ebe2d4SWarner Losh espstart="" 914*a7ebe2d4SWarner Losh espsize_cd="" 915*a7ebe2d4SWarner Losh for entry in $(etdump --format shell ${img}); do 916*a7ebe2d4SWarner Losh eval ${entry} 917*a7ebe2d4SWarner Losh if [ "${et_platform}" = "efi" ]; then 918*a7ebe2d4SWarner Losh espstart=$(expr ${et_lba} \* 2048) 919*a7ebe2d4SWarner Losh espsize_cd=$(expr ${et_sectors} \* 512) 920*a7ebe2d4SWarner Losh break 921*a7ebe2d4SWarner Losh fi 922*a7ebe2d4SWarner Losh done 923*a7ebe2d4SWarner Losh 924*a7ebe2d4SWarner Losh if [ -n "${espstart}" ]; then 925*a7ebe2d4SWarner Losh hybrid=$(mktemp ${OUTDIR}/hybrid.XXXXXX) 926*a7ebe2d4SWarner Losh mkimg -s gpt \ 927*a7ebe2d4SWarner Losh --capacity ${imgsize} \ 928*a7ebe2d4SWarner Losh -b ${DESTDIR}/boot/pmbr \ 929*a7ebe2d4SWarner Losh -p freebsd-boot:=${DESTDIR}/boot/isoboot \ 930*a7ebe2d4SWarner Losh -p efi::${espsize_cd}:${espstart} \ 931*a7ebe2d4SWarner Losh -o ${hybrid} >> ${LOGDIR}/imagebuild.log 2>&1 932*a7ebe2d4SWarner Losh dd if=${hybrid} of=${img} bs=32k count=1 conv=notrunc >> ${LOGDIR}/imagebuild.log 2>&1 933*a7ebe2d4SWarner Losh rm -f ${hybrid} 934*a7ebe2d4SWarner Losh fi 935*a7ebe2d4SWarner Losh 936*a7ebe2d4SWarner Losh rm -f ${espfile} 937*a7ebe2d4SWarner Losh 938*a7ebe2d4SWarner Losh # Test the hybrid ISO with both BIOS and EFI 939*a7ebe2d4SWarner Losh register_test "${name}-bios" $(qemu_bios_cdrom ${img}) 940*a7ebe2d4SWarner Losh register_test "${name}-efi" $(qemu_efi_cdrom ${img}) 941*a7ebe2d4SWarner Losh 942*a7ebe2d4SWarner Losh rm -f ${mt1} 943*a7ebe2d4SWarner Losh} 944*a7ebe2d4SWarner Losh 945*a7ebe2d4SWarner Loshassemble_ofw() { 946*a7ebe2d4SWarner Losh echo " Assembling Open Firmware images..." 947*a7ebe2d4SWarner Losh # APM partitioned disk with boot1.hfs 948*a7ebe2d4SWarner Losh fstypes="ufs" 949*a7ebe2d4SWarner Losh has zfs && fstypes="ufs zfs" 950*a7ebe2d4SWarner Losh for fs in ${fstypes}; do 951*a7ebe2d4SWarner Losh name="ofw-apm-${fs}" 952*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 953*a7ebe2d4SWarner Losh 954*a7ebe2d4SWarner Losh case ${fs} in 955*a7ebe2d4SWarner Losh ufs) fsimg=${IMGDIR}/bootable-ufs-lua.img; ptype="freebsd-ufs" ;; 956*a7ebe2d4SWarner Losh zfs) fsimg=${IMGDIR}/bootable-zfs-lua.img; ptype="freebsd-zfs" ;; 957*a7ebe2d4SWarner Losh esac 958*a7ebe2d4SWarner Losh 959*a7ebe2d4SWarner Losh mkimg -a 1 -s apm \ 960*a7ebe2d4SWarner Losh -p freebsd-boot:=${DESTDIR}/boot/boot1.hfs \ 961*a7ebe2d4SWarner Losh -p ${ptype}:=${fsimg} \ 962*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 963*a7ebe2d4SWarner Losh 964*a7ebe2d4SWarner Losh register_test ${name} $(qemu_ofw ${img}) 965*a7ebe2d4SWarner Losh done 966*a7ebe2d4SWarner Losh} 967*a7ebe2d4SWarner Losh 968*a7ebe2d4SWarner Losh# Open Firmware bootable CD. Mirrors release/powerpc/mkisoimages.sh: build the 969*a7ebe2d4SWarner Losh# Apple/OF "macppc" boot image by dd'ing /boot/loader into the hfs-boot block 970*a7ebe2d4SWarner Losh# at its "Loader START" offset, then hand that to makefs as the El Torito 971*a7ebe2d4SWarner Losh# no-emul boot image. OpenBIOS finds it via cd:,\\:tbxi. 972*a7ebe2d4SWarner Loshassemble_ofw_cd() { 973*a7ebe2d4SWarner Losh echo " Assembling Open Firmware CD image..." 974*a7ebe2d4SWarner Losh name="ofw-cd" 975*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.iso 976*a7ebe2d4SWarner Losh 977*a7ebe2d4SWarner Losh # cd9660 root fstab overlay 978*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/ofwcd-mtree.XXXXXX) 979*a7ebe2d4SWarner Losh echo "./etc/fstab type=file mode=0644 contents=${OUTDIR}/fstab.cd" > ${mt} 980*a7ebe2d4SWarner Losh 981*a7ebe2d4SWarner Losh # Apple/OF boot block with the loader embedded at "Loader START". 982*a7ebe2d4SWarner Losh bootblock=$(mktemp ${OUTDIR}/hfs-boot.XXXXXX) 983*a7ebe2d4SWarner Losh uudecode -p ${SRCTOP}/release/powerpc/hfs-boot.bz2.uu | bunzip2 > ${bootblock} 984*a7ebe2d4SWarner Losh offset=$(hd ${bootblock} | grep 'Loader START' | cut -f 1 -d ' ') 985*a7ebe2d4SWarner Losh offset=$((0x${offset} / 512)) 986*a7ebe2d4SWarner Losh dd if=${DESTDIR}/boot/loader of=${bootblock} seek=${offset} conv=notrunc \ 987*a7ebe2d4SWarner Losh >> ${LOGDIR}/imagebuild.log 2>&1 988*a7ebe2d4SWarner Losh 989*a7ebe2d4SWarner Losh makefs -t cd9660 \ 990*a7ebe2d4SWarner Losh -o bootimage=macppc\;${bootblock} \ 991*a7ebe2d4SWarner Losh -o no-emul-boot \ 992*a7ebe2d4SWarner Losh -o rockridge \ 993*a7ebe2d4SWarner Losh -o label=FBSDTEST \ 994*a7ebe2d4SWarner Losh ${img} ${mt} ${DESTDIR} >> ${LOGDIR}/imagebuild.log 2>&1 995*a7ebe2d4SWarner Losh 996*a7ebe2d4SWarner Losh rm -f ${bootblock} ${mt} 997*a7ebe2d4SWarner Losh register_test ${name} $(qemu_ofw_cdrom ${img}) 998*a7ebe2d4SWarner Losh} 999*a7ebe2d4SWarner Losh 1000*a7ebe2d4SWarner Losh# pseries PReP disk boot: MBR with a PReP boot partition (boot1.elf) and the 1001*a7ebe2d4SWarner Losh# UFS root directly on an MBR partition -- no BSD label, matching freebsd-ci 1002*a7ebe2d4SWarner Losh# (whose BSD-slice container does not cross-build from amd64). SLOF runs 1003*a7ebe2d4SWarner Losh# boot1.elf from the PReP partition, which loads /boot/loader from the UFS. 1004*a7ebe2d4SWarner Loshassemble_prep() { 1005*a7ebe2d4SWarner Losh echo " Assembling pseries PReP (MBR) images..." 1006*a7ebe2d4SWarner Losh name="prep-mbr-ufs" 1007*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 1008*a7ebe2d4SWarner Losh ufs=${IMGDIR}/bootable-ufs-lua.img 1009*a7ebe2d4SWarner Losh 1010*a7ebe2d4SWarner Losh mkimg -a 1 -s mbr \ 1011*a7ebe2d4SWarner Losh -p prepboot:=${DESTDIR}/boot/boot1.elf \ 1012*a7ebe2d4SWarner Losh -p freebsd:=${ufs} \ 1013*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 1014*a7ebe2d4SWarner Losh 1015*a7ebe2d4SWarner Losh register_test ${name} $(qemu_prep ${img}) 1016*a7ebe2d4SWarner Losh} 1017*a7ebe2d4SWarner Losh 1018*a7ebe2d4SWarner Losh# pseries CD boot. SLOF reads \ppc\bootinfo.txt (CHRP boot script) and runs 1019*a7ebe2d4SWarner Losh# the OF loader from the CD. Mirrors the chrp-boot half of 1020*a7ebe2d4SWarner Losh# release/powerpc/mkisoimages.sh (the macppc/Apple half is mac99-only). 1021*a7ebe2d4SWarner Loshassemble_pseries_cd() { 1022*a7ebe2d4SWarner Losh echo " Assembling pseries CD image..." 1023*a7ebe2d4SWarner Losh name="prep-cd" 1024*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.iso 1025*a7ebe2d4SWarner Losh 1026*a7ebe2d4SWarner Losh mt=$(mktemp ${OUTDIR}/pseriescd-mtree.XXXXXX) 1027*a7ebe2d4SWarner Losh bootinfo=$(mktemp ${OUTDIR}/bootinfo.XXXXXX) 1028*a7ebe2d4SWarner Losh cat > ${bootinfo} <<EOF 1029*a7ebe2d4SWarner Losh<chrp-boot> 1030*a7ebe2d4SWarner Losh<description>FreeBSD Install</description> 1031*a7ebe2d4SWarner Losh<os-name>FreeBSD</os-name> 1032*a7ebe2d4SWarner Losh<boot-script>boot &device;:,\ppc\chrp\loader</boot-script> 1033*a7ebe2d4SWarner Losh</chrp-boot> 1034*a7ebe2d4SWarner LoshEOF 1035*a7ebe2d4SWarner Losh cat > ${mt} <<EOF 1036*a7ebe2d4SWarner Losh./etc/fstab type=file mode=0644 contents=${OUTDIR}/fstab.cd 1037*a7ebe2d4SWarner Losh./ppc type=dir mode=0755 1038*a7ebe2d4SWarner Losh./ppc/bootinfo.txt type=file mode=0644 contents=${bootinfo} 1039*a7ebe2d4SWarner Losh./ppc/chrp type=dir mode=0755 1040*a7ebe2d4SWarner Losh./ppc/chrp/loader type=file mode=0644 contents=${DESTDIR}/boot/loader 1041*a7ebe2d4SWarner LoshEOF 1042*a7ebe2d4SWarner Losh makefs -t cd9660 \ 1043*a7ebe2d4SWarner Losh -o chrp-boot \ 1044*a7ebe2d4SWarner Losh -o rockridge \ 1045*a7ebe2d4SWarner Losh -o label=FBSDTEST \ 1046*a7ebe2d4SWarner Losh ${img} ${mt} ${DESTDIR} >> ${LOGDIR}/imagebuild.log 2>&1 1047*a7ebe2d4SWarner Losh rm -f ${mt} ${bootinfo} 1048*a7ebe2d4SWarner Losh register_test ${name} $(qemu_prep_cdrom ${img}) 1049*a7ebe2d4SWarner Losh} 1050*a7ebe2d4SWarner Losh 1051*a7ebe2d4SWarner Loshassemble_linuxboot() { 1052*a7ebe2d4SWarner Losh echo " Assembling linuxboot images..." 1053*a7ebe2d4SWarner Losh esp=${IMGDIR}/linuxboot.esp 1054*a7ebe2d4SWarner Losh fstypes="ufs" 1055*a7ebe2d4SWarner Losh has zfs && fstypes="ufs zfs" 1056*a7ebe2d4SWarner Losh for fs in ${fstypes}; do 1057*a7ebe2d4SWarner Losh name="linuxboot-gpt-${fs}" 1058*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 1059*a7ebe2d4SWarner Losh 1060*a7ebe2d4SWarner Losh case ${fs} in 1061*a7ebe2d4SWarner Losh ufs) fsimg=${IMGDIR}/bootable-ufs-lua.img; ptype="freebsd-ufs" ;; 1062*a7ebe2d4SWarner Losh zfs) fsimg=${IMGDIR}/bootable-zfs-lua.img; ptype="freebsd-zfs" ;; 1063*a7ebe2d4SWarner Losh esac 1064*a7ebe2d4SWarner Losh 1065*a7ebe2d4SWarner Losh mkimg -s gpt \ 1066*a7ebe2d4SWarner Losh -p efi:=${esp} \ 1067*a7ebe2d4SWarner Losh -p ${ptype}:=${fsimg} \ 1068*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 1069*a7ebe2d4SWarner Losh 1070*a7ebe2d4SWarner Losh register_test ${name} $(qemu_efi ${img}) 1071*a7ebe2d4SWarner Losh done 1072*a7ebe2d4SWarner Losh} 1073*a7ebe2d4SWarner Losh 1074*a7ebe2d4SWarner Losh# Linuxboot for platforms without an ESP: the kernel+initrd go on the QEMU 1075*a7ebe2d4SWarner Losh# command line, and the disk is just the FreeBSD root filesystem in a GPT 1076*a7ebe2d4SWarner Losh# (no ESP partition). loader.kboot mounts root from that disk. 1077*a7ebe2d4SWarner Loshassemble_linuxboot_direct() { 1078*a7ebe2d4SWarner Losh echo " Assembling linuxboot (direct kernel/initrd) images..." 1079*a7ebe2d4SWarner Losh linux_kernel=$(find_linux_kernel) || return 0 1080*a7ebe2d4SWarner Losh initrd=${IMGDIR}/linuxboot-initrd.cpio.gz 1081*a7ebe2d4SWarner Losh fstypes="ufs" 1082*a7ebe2d4SWarner Losh has zfs && fstypes="ufs zfs" 1083*a7ebe2d4SWarner Losh for fs in ${fstypes}; do 1084*a7ebe2d4SWarner Losh name="linuxboot-${fs}" 1085*a7ebe2d4SWarner Losh img=${IMGDIR}/${name}.img 1086*a7ebe2d4SWarner Losh 1087*a7ebe2d4SWarner Losh case ${fs} in 1088*a7ebe2d4SWarner Losh ufs) fsimg=${IMGDIR}/bootable-ufs-lua.img; ptype="freebsd-ufs" ;; 1089*a7ebe2d4SWarner Losh zfs) fsimg=${IMGDIR}/bootable-zfs-lua.img; ptype="freebsd-zfs" ;; 1090*a7ebe2d4SWarner Losh esac 1091*a7ebe2d4SWarner Losh 1092*a7ebe2d4SWarner Losh mkimg -s gpt \ 1093*a7ebe2d4SWarner Losh -p ${ptype}:=${fsimg} \ 1094*a7ebe2d4SWarner Losh -o ${img} >> ${LOGDIR}/imagebuild.log 2>&1 1095*a7ebe2d4SWarner Losh 1096*a7ebe2d4SWarner Losh register_test ${name} $(qemu_linuxboot ${img} ${linux_kernel} ${initrd}) 1097*a7ebe2d4SWarner Losh done 1098*a7ebe2d4SWarner Losh} 1099*a7ebe2d4SWarner Losh 1100*a7ebe2d4SWarner Losh# Netboot: stage a TFTP root and register PXE/EFI network-boot tests. The 1101*a7ebe2d4SWarner Losh# loader fetches the kernel + an md_image RAM root over TFTP and mounts 1102*a7ebe2d4SWarner Losh# /dev/md0. The bootable UFS image is reused as the RAM root, so the kernel 1103*a7ebe2d4SWarner Losh# must have "options MD_ROOT" (amd64/aarch64 do; riscv64/armv7 GENERIC do 1104*a7ebe2d4SWarner Losh# not -- see the plan). netboot-bios/netboot-efi go over a real tap(4) + 1105*a7ebe2d4SWarner Losh# dnsmasq (register_netboot_test / netboot_network_setup) so DHCP can carry a 1106*a7ebe2d4SWarner Losh# root-path. 1107*a7ebe2d4SWarner Loshassemble_netboot() { 1108*a7ebe2d4SWarner Losh echo " Assembling netboot images..." 1109*a7ebe2d4SWarner Losh tftp=${OUTDIR}/tftp 1110*a7ebe2d4SWarner Losh rm -rf ${tftp} 1111*a7ebe2d4SWarner Losh mkdir -p ${tftp} 1112*a7ebe2d4SWarner Losh cp -a ${DESTDIR}/boot ${tftp}/boot 1113*a7ebe2d4SWarner Losh cp ${IMGDIR}/bootable-ufs-lua.img ${tftp}/boot/mdroot.img 1114*a7ebe2d4SWarner Losh 1115*a7ebe2d4SWarner Losh # Serve the kernel and RAM root as .xz: xzfs_fsops (stand/libsa/xzfs.c) 1116*a7ebe2d4SWarner Losh # transparently retries "<name>.xz" whenever "<name>" isn't found, 1117*a7ebe2d4SWarner Losh # exactly like gzipfs already does for kernel.gz, so this cuts the bytes 1118*a7ebe2d4SWarner Losh # that have to cross TFTP with no loader.conf change -- mdroot_name below 1119*a7ebe2d4SWarner Losh # still names the plain, unsuffixed "mdroot.img", and pxeboot/loader.efi 1120*a7ebe2d4SWarner Losh # (fetched directly by firmware, not through libsa) are untouched. Both 1121*a7ebe2d4SWarner Losh # the amd64 BIOS loader (i386/loader/Makefile: LOADER_XZ_SUPPORT) and 1122*a7ebe2d4SWarner Losh # every EFI loader (efi/loader/conf.c: unconditional) have xzfs compiled 1123*a7ebe2d4SWarner Losh # in, so netboot-bios and netboot-efi -- which share this tftp tree -- 1124*a7ebe2d4SWarner Losh # both exercise the compressed path. 1125*a7ebe2d4SWarner Losh # 1126*a7ebe2d4SWarner Losh # -6 (xz's own default), not -9: -9's 64MB LZMA2 dictionary needs one 1127*a7ebe2d4SWarner Losh # contiguous vmalloc() of that size before xz_dec_run() can decode 1128*a7ebe2d4SWarner Losh # anything, and the BIOS loader's *entire* heap is a fixed 64MB 1129*a7ebe2d4SWarner Losh # (HEAP_MIN in i386/libi386/biosmem.c) regardless of VM memory -- so a 1130*a7ebe2d4SWarner Losh # 64MB dictionary request against a 64MB heap that already holds other 1131*a7ebe2d4SWarner Losh # loader state can never succeed (XZ_MEM_ERROR, every read, forever). 1132*a7ebe2d4SWarner Losh # -6's 8MB dictionary leaves ample headroom and loses ~nothing on data 1133*a7ebe2d4SWarner Losh # this size. 1134*a7ebe2d4SWarner Losh xz -6 -f ${tftp}/boot/kernel/kernel 1135*a7ebe2d4SWarner Losh xz -6 -f ${tftp}/boot/mdroot.img 1136*a7ebe2d4SWarner Losh 1137*a7ebe2d4SWarner Losh # Boot the kernel + RAM root off the network instead of a disk (appended to 1138*a7ebe2d4SWarner Losh # the arch-correct loader.conf build_tree already wrote). 1139*a7ebe2d4SWarner Losh cat >> ${tftp}/boot/loader.conf <<EOF 1140*a7ebe2d4SWarner Loshmdroot_load="YES" 1141*a7ebe2d4SWarner Loshmdroot_type="md_image" 1142*a7ebe2d4SWarner Loshmdroot_name="/boot/mdroot.img" 1143*a7ebe2d4SWarner Loshvfs.root.mountfrom="ufs:/dev/md0" 1144*a7ebe2d4SWarner LoshEOF 1145*a7ebe2d4SWarner Losh 1146*a7ebe2d4SWarner Losh # BIOS PXE: the NIC option ROM chainloads pxeboot (the DHCP bootfile). 1147*a7ebe2d4SWarner Losh # Registered via register_netboot_test, not register_test: these need a 1148*a7ebe2d4SWarner Losh # real tap(4) + dnsmasq root-path (see netboot_network_setup) rather than 1149*a7ebe2d4SWarner Losh # slirp, which can't send one. 1150*a7ebe2d4SWarner Losh # 1151*a7ebe2d4SWarner Losh # netboot-bios legitimately runs past the default timeout: it's TFTP 1152*a7ebe2d4SWarner Losh # (not xzfs decode) that's the bottleneck here -- EFI's memdisk=<url> 1153*a7ebe2d4SWarner Losh # path decodes 150MB .xz images in about a minute, so the loader's xz 1154*a7ebe2d4SWarner Losh # decoder isn't the bottleneck; this is plain TFTP bandwidth for the 1155*a7ebe2d4SWarner Losh # kernel+mdroot fetch. Measured wins on real hardware: whole-suite boot 1156*a7ebe2d4SWarner Losh # time dropped from ~25 min to ~18 min with xzfs in place. TFTP-level 1157*a7ebe2d4SWarner Losh # fixes (e.g. HTTP instead, once BIOS can do that) are the only further 1158*a7ebe2d4SWarner Losh # lever; don't chase this again without one. 1159*a7ebe2d4SWarner Losh if has bios; then 1160*a7ebe2d4SWarner Losh cp ${DESTDIR}/boot/pxeboot ${tftp}/pxeboot 1161*a7ebe2d4SWarner Losh register_netboot_test netboot-bios ${tftp} pxeboot bios 1162*a7ebe2d4SWarner Losh fi 1163*a7ebe2d4SWarner Losh # EFI: stage loader.efi for the iPXE-chainload test below. This can't use 1164*a7ebe2d4SWarner Losh # the NIC's own PXE ROM the way netboot-bios does -- our installed OVMF 1165*a7ebe2d4SWarner Losh # build (qemu's bundled edk2-x86_64-code.fd) has no NetworkPkg/PXE driver 1166*a7ebe2d4SWarner Losh # at all (confirmed: zero PXE/HTTPBoot strings in the firmware, and 1167*a7ebe2d4SWarner Losh # BdsDxe drops straight to "EFI Internal Shell" with no network boot 1168*a7ebe2d4SWarner Losh # option ever offered, independent of vars pflash/bootindex/-boot n). So 1169*a7ebe2d4SWarner Losh # EFI netboot instead brings its own iPXE (see the ramdisk block below) 1170*a7ebe2d4SWarner Losh # to do DHCP/TFTP; netboot-efi's own chain script (also below) omits 1171*a7ebe2d4SWarner Losh # memdisk= so loader.efi does its own BOOTP call and gets a real 1172*a7ebe2d4SWarner Losh # DHCP root-path, unlike the RAM-disk tests. 1173*a7ebe2d4SWarner Losh if has efi; then 1174*a7ebe2d4SWarner Losh cp ${DESTDIR}/boot/loader_lua.efi ${tftp}/loader.efi 1175*a7ebe2d4SWarner Losh fi 1176*a7ebe2d4SWarner Losh 1177*a7ebe2d4SWarner Losh # RAM-disk netboot: iPXE (from -hda) chains loader.efi with memdisk=<url>, 1178*a7ebe2d4SWarner Losh # which the loader boots entirely from RAM -- sidestepping the NFS/root-path 1179*a7ebe2d4SWarner Losh # problem above. iPXE EFI is x86-only in the install, so this is gated on 1180*a7ebe2d4SWarner Losh # netboot_ipxe (amd64 today). See qemu_netboot_ramdisk / do-memddisk-efi. 1181*a7ebe2d4SWarner Losh # netboot-efi (real root-path, no memdisk=) piggybacks on the same iPXE 1182*a7ebe2d4SWarner Losh # image/vars, since it needs the same OVMF-has-no-PXE workaround. 1183*a7ebe2d4SWarner Losh if [ -n "$(param netboot_ipxe)" ] && have_file "$(param netboot_ipxe)" && \ 1184*a7ebe2d4SWarner Losh have_file "$(param netboot_efi_vars)"; then 1185*a7ebe2d4SWarner Losh # Writable copies: QEMU opens -hda and the edk2 vars pflash read-write, 1186*a7ebe2d4SWarner Losh # but the installed originals are root-owned. 1187*a7ebe2d4SWarner Losh cp -f $(param netboot_ipxe) ${OUTDIR}/netboot-ipxe.img 1188*a7ebe2d4SWarner Losh cp -f $(param netboot_efi_vars) ${OUTDIR}/netboot-vars.fd 1189*a7ebe2d4SWarner Losh 1190*a7ebe2d4SWarner Losh if has efi; then 1191*a7ebe2d4SWarner Losh cat > ${tftp}/boot-efi.ipxe <<EOF 1192*a7ebe2d4SWarner Losh#!ipxe 1193*a7ebe2d4SWarner Loshchain tftp://\${next-server}/loader.efi 1194*a7ebe2d4SWarner LoshEOF 1195*a7ebe2d4SWarner Losh register_netboot_test netboot-efi ${tftp} /boot-efi.ipxe "" qemu_netboot_ramdisk 1196*a7ebe2d4SWarner Losh fi 1197*a7ebe2d4SWarner Losh 1198*a7ebe2d4SWarner Losh cat > ${tftp}/boot.ipxe <<EOF 1199*a7ebe2d4SWarner Losh#!ipxe 1200*a7ebe2d4SWarner Loshchain tftp://\${next-server}/loader.efi memdisk=tftp://\${next-server}/boot/mdroot.img 1201*a7ebe2d4SWarner LoshEOF 1202*a7ebe2d4SWarner Losh register_netboot_test netboot-ramdisk ${tftp} /boot.ipxe "" qemu_netboot_ramdisk 1203*a7ebe2d4SWarner Losh 1204*a7ebe2d4SWarner Losh # Same mechanism, but the image is compressed, exercising each codec 1205*a7ebe2d4SWarner Losh # in stand/efi/loader/decompress.c. Compress a throwaway copy rather 1206*a7ebe2d4SWarner Losh # than ${IMGDIR}/bootable-ufs-lua.img itself: gzip/bzip2/xz remove 1207*a7ebe2d4SWarner Losh # their input on success, and this image is shared with other tests. 1208*a7ebe2d4SWarner Losh # Compressing a real file (not a pipe) lets zstd embed the frame 1209*a7ebe2d4SWarner Losh # content size, so zstd_init() can size the output buffer exactly 1210*a7ebe2d4SWarner Losh # instead of guessing 4x and growing/copying as it decompresses. 1211*a7ebe2d4SWarner Losh for spec in gzip:gz bzip2:bz2 xz:xz zstd:zst; do 1212*a7ebe2d4SWarner Losh tool=${spec%%:*} 1213*a7ebe2d4SWarner Losh ext=${spec#*:} 1214*a7ebe2d4SWarner Losh img=${tftp}/boot/mdroot-${tool}.img 1215*a7ebe2d4SWarner Losh cp ${IMGDIR}/bootable-ufs-lua.img ${img} 1216*a7ebe2d4SWarner Losh case ${tool} in 1217*a7ebe2d4SWarner Losh zstd) zstd -f --rm ${img} >> ${LOGDIR}/imagebuild.log 2>&1 ;; 1218*a7ebe2d4SWarner Losh *) ${tool} -f ${img} >> ${LOGDIR}/imagebuild.log 2>&1 ;; 1219*a7ebe2d4SWarner Losh esac 1220*a7ebe2d4SWarner Losh cat > ${tftp}/boot-${tool}.ipxe <<EOF 1221*a7ebe2d4SWarner Losh#!ipxe 1222*a7ebe2d4SWarner Loshchain tftp://\${next-server}/loader.efi memdisk=tftp://\${next-server}/boot/mdroot-${tool}.img.${ext} 1223*a7ebe2d4SWarner LoshEOF 1224*a7ebe2d4SWarner Losh register_netboot_test netboot-ramdisk-${tool} ${tftp} /boot-${tool}.ipxe "" qemu_netboot_ramdisk 1225*a7ebe2d4SWarner Losh done 1226*a7ebe2d4SWarner Losh fi 1227*a7ebe2d4SWarner Losh 1228*a7ebe2d4SWarner Losh # BIOS RAM-disk netboot: iPXE loads syslinux memdisk with a *bootable* disk 1229*a7ebe2d4SWarner Losh # image as its initrd. memdisk boots the image's MBR and installs a MEMDISK 1230*a7ebe2d4SWarner Losh # memory disk; the BIOS loader detects it (biosmemdisk.c -> hint.md.0.*), so 1231*a7ebe2d4SWarner Losh # the kernel gets md0 and roots via the UFS label. The BIOS loader has no 1232*a7ebe2d4SWarner Losh # memdisk= arg, hence this different mechanism. 1233*a7ebe2d4SWarner Losh if has bios && [ -n "$(param netboot_memdisk)" ] && \ 1234*a7ebe2d4SWarner Losh have_file "$(param netboot_memdisk)"; then 1235*a7ebe2d4SWarner Losh cp $(param netboot_memdisk) ${tftp}/memdisk 1236*a7ebe2d4SWarner Losh cp ${IMGDIR}/bios-mbr-ufs-loader_lua.img ${tftp}/bootdisk.img 1237*a7ebe2d4SWarner Losh cat > ${tftp}/boot-bios.ipxe <<EOF 1238*a7ebe2d4SWarner Losh#!ipxe 1239*a7ebe2d4SWarner Loshkernel tftp://\${next-server}/memdisk 1240*a7ebe2d4SWarner Loshinitrd tftp://\${next-server}/bootdisk.img 1241*a7ebe2d4SWarner Loshboot 1242*a7ebe2d4SWarner LoshEOF 1243*a7ebe2d4SWarner Losh register_netboot_test netboot-bios-memdisk ${tftp} /boot-bios.ipxe bios 1244*a7ebe2d4SWarner Losh fi 1245*a7ebe2d4SWarner Losh} 1246*a7ebe2d4SWarner Losh 1247*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 1248*a7ebe2d4SWarner Losh# Netboot host networking: vmnet(4) + dnsmasq 1249*a7ebe2d4SWarner Losh# 1250*a7ebe2d4SWarner Losh# QEMU's slirp/user-mode net cannot send DHCP root-path (option 17); without 1251*a7ebe2d4SWarner Losh# one, the loader's netproto defaults to NFS for every file fetch after the 1252*a7ebe2d4SWarner Losh# initial TFTP-delivered bootfile (net_parse_rootpath()/NETPROTO_DEFAULT), which 1253*a7ebe2d4SWarner Losh# is why netboot-bios/ netboot-efi otherwise fail here (no NFS server). 1254*a7ebe2d4SWarner Losh# 1255*a7ebe2d4SWarner Losh# This uses vmnet(4), not tap(4), even though both are clones of the same 1256*a7ebe2d4SWarner Losh# if_tuntap(4) driver and QEMU treats them identically (-netdev tap,ifname=). 1257*a7ebe2d4SWarner Losh# tap(4)'s close handler unconditionally runs if_down()+if_purgeaddrs() on last 1258*a7ebe2d4SWarner Losh# close -- so the interface would lose its address and go down every time QEMU 1259*a7ebe2d4SWarner Losh# exits, and re-adding it needs root. vmnet(4) explicitly skips that, so the 1260*a7ebe2d4SWarner Losh# address assigned once survives every subsequent QEMU open/close. 1261*a7ebe2d4SWarner Losh# 1262*a7ebe2d4SWarner Losh# Creating the interface and giving it an address both require root regardless 1263*a7ebe2d4SWarner Losh# of net.link.tap.user_open (that sysctl only gates opening the cloning device 1264*a7ebe2d4SWarner Losh# itself). So the root setup below runs once, chowns the resulting /dev/vmnetN 1265*a7ebe2d4SWarner Losh# nodes to the invoking user (so QEMU, unprivileged, can open them directly -- 1266*a7ebe2d4SWarner Losh# see net/tap-bsd.c: it opens /dev/<ifname> directly when that device already 1267*a7ebe2d4SWarner Losh# exists, only falling back to the /dev/tap cloning device otherwise), and 1268*a7ebe2d4SWarner Losh# leaves everything running. Later invocations detect the existing state via a 1269*a7ebe2d4SWarner Losh# content hash and skip sudo entirely: "once per boot", not "once per run". 1270*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 1271*a7ebe2d4SWarner Losh 1272*a7ebe2d4SWarner Losh# Deterministically slice a /30 out of $(param netboot_subnet_base) (a /16, 1273*a7ebe2d4SWarner Losh# e.g. 198.18.0.0) for plan line ${1} (0-based). Prints "gw client". 1274*a7ebe2d4SWarner Loshnetboot_subnet_for() { 1275*a7ebe2d4SWarner Losh idx=$1 1276*a7ebe2d4SWarner Losh base=$(param netboot_subnet_base) 1277*a7ebe2d4SWarner Losh o1o2=$(echo ${base} | cut -d. -f1-2) 1278*a7ebe2d4SWarner Losh o3=$(($(echo ${base} | cut -d. -f3) + idx / 64)) 1279*a7ebe2d4SWarner Losh o4=$(((idx % 64) * 4)) 1280*a7ebe2d4SWarner Losh echo "${o1o2}.${o3}.$((o4 + 1)) ${o1o2}.${o3}.$((o4 + 2))" 1281*a7ebe2d4SWarner Losh} 1282*a7ebe2d4SWarner Losh 1283*a7ebe2d4SWarner Losh# Runs after every arch is built (so ${NETBOOT_PLAN} is complete) and before 1284*a7ebe2d4SWarner Losh# run_all_tests. Assigns each registered netboot test its own vmnet + /30, 1285*a7ebe2d4SWarner Losh# escalates via sudo only if the host doesn't already match that plan, then 1286*a7ebe2d4SWarner Losh# patches the __NETBOOT_TAP__ placeholder in each test's command file. 1287*a7ebe2d4SWarner Loshnetboot_network_setup() { 1288*a7ebe2d4SWarner Losh [ -s "${NETBOOT_PLAN}" ] || return 0 1289*a7ebe2d4SWarner Losh echo "=== Netboot: configuring vmnet(4) + dnsmasq ===" 1290*a7ebe2d4SWarner Losh mkdir -p "${NETBOOT_STATE_DIR}" 1291*a7ebe2d4SWarner Losh 1292*a7ebe2d4SWarner Losh resolved=${NETBOOT_STATE_DIR}/plan.new 1293*a7ebe2d4SWarner Losh : > "${resolved}" 1294*a7ebe2d4SWarner Losh idx=0 1295*a7ebe2d4SWarner Losh while read cmdfile tftpdir bootfile; do 1296*a7ebe2d4SWarner Losh vmnet="vmnet$((100 + idx))" # offset away from any operator-managed vmnets 1297*a7ebe2d4SWarner Losh set -- $(netboot_subnet_for ${idx}) 1298*a7ebe2d4SWarner Losh gw=$1 1299*a7ebe2d4SWarner Losh client=$2 1300*a7ebe2d4SWarner Losh echo "${vmnet} ${gw} ${client} 30 ${tftpdir} ${bootfile} ${cmdfile}" >> "${resolved}" 1301*a7ebe2d4SWarner Losh idx=$((idx + 1)) 1302*a7ebe2d4SWarner Losh done < "${NETBOOT_PLAN}" 1303*a7ebe2d4SWarner Losh 1304*a7ebe2d4SWarner Losh newhash=$(md5 -q "${resolved}") 1305*a7ebe2d4SWarner Losh oldhash="" 1306*a7ebe2d4SWarner Losh [ -f "${NETBOOT_STATE_DIR}/state.hash" ] && oldhash=$(cat "${NETBOOT_STATE_DIR}/state.hash") 1307*a7ebe2d4SWarner Losh 1308*a7ebe2d4SWarner Losh # dnsmasq runs as "nobody" (it drops root after binding), so kill -0 1309*a7ebe2d4SWarner Losh # from this unprivileged process would always fail with EPERM even when 1310*a7ebe2d4SWarner Losh # it's alive; check process existence via ps instead, which doesn't 1311*a7ebe2d4SWarner Losh # require signal permission. 1312*a7ebe2d4SWarner Losh converged=false 1313*a7ebe2d4SWarner Losh if [ "${newhash}" = "${oldhash}" ] && [ -s "${NETBOOT_STATE_DIR}/dnsmasq.pid" ] \ 1314*a7ebe2d4SWarner Losh && ps -p "$(cat ${NETBOOT_STATE_DIR}/dnsmasq.pid)" > /dev/null 2>&1; then 1315*a7ebe2d4SWarner Losh converged=true 1316*a7ebe2d4SWarner Losh while read vmnet gw client prefix tftpdir bootfile cmdfile; do 1317*a7ebe2d4SWarner Losh ifconfig "${vmnet}" > /dev/null 2>&1 || { converged=false; break; } 1318*a7ebe2d4SWarner Losh done < "${resolved}" 1319*a7ebe2d4SWarner Losh fi 1320*a7ebe2d4SWarner Losh 1321*a7ebe2d4SWarner Losh if ${converged}; then 1322*a7ebe2d4SWarner Losh echo " Existing vmnet(4)/dnsmasq setup already matches -- no sudo needed." 1323*a7ebe2d4SWarner Losh else 1324*a7ebe2d4SWarner Losh echo " Host networking missing or stale; requesting sudo once to (re)create it..." 1325*a7ebe2d4SWarner Losh cp "${resolved}" "${NETBOOT_STATE_DIR}/plan" 1326*a7ebe2d4SWarner Losh sudo "$0" --netboot-helper "${NETBOOT_STATE_DIR}/plan" \ 1327*a7ebe2d4SWarner Losh || die "netboot vmnet(4)/dnsmasq setup (sudo) failed" 1328*a7ebe2d4SWarner Losh fi 1329*a7ebe2d4SWarner Losh 1330*a7ebe2d4SWarner Losh while read vmnet gw client prefix tftpdir bootfile cmdfile; do 1331*a7ebe2d4SWarner Losh sed -i '' "s/__NETBOOT_TAP__/${vmnet}/" "${cmdfile}" 1332*a7ebe2d4SWarner Losh done < "${resolved}" 1333*a7ebe2d4SWarner Losh} 1334*a7ebe2d4SWarner Losh 1335*a7ebe2d4SWarner Losh# Root-side setup, only reached via `sudo "$0" --netboot-helper <planfile>` 1336*a7ebe2d4SWarner Losh# (see netboot_network_setup). ${1} has the same "vmnet gw client prefix 1337*a7ebe2d4SWarner Losh# tftpdir bootfile cmdfile" lines as netboot_network_setup's resolved plan. 1338*a7ebe2d4SWarner Loshnetboot_helper() { 1339*a7ebe2d4SWarner Losh planfile=$1 1340*a7ebe2d4SWarner Losh [ -r "${planfile}" ] || die "netboot helper: cannot read plan ${planfile}" 1341*a7ebe2d4SWarner Losh invoker=${SUDO_UID:-$(id -u)} 1342*a7ebe2d4SWarner Losh mkdir -p "${NETBOOT_STATE_DIR}" 1343*a7ebe2d4SWarner Losh 1344*a7ebe2d4SWarner Losh # Converge from scratch rather than trusting the caller's hash check: 1345*a7ebe2d4SWarner Losh # tear down anything left over from a previous (possibly interrupted) run. 1346*a7ebe2d4SWarner Losh if [ -s "${NETBOOT_STATE_DIR}/dnsmasq.pid" ]; then 1347*a7ebe2d4SWarner Losh kill "$(cat ${NETBOOT_STATE_DIR}/dnsmasq.pid)" 2>/dev/null || true 1348*a7ebe2d4SWarner Losh rm -f "${NETBOOT_STATE_DIR}/dnsmasq.pid" 1349*a7ebe2d4SWarner Losh fi 1350*a7ebe2d4SWarner Losh for vmnet in $(ifconfig -g boot-test 2>/dev/null); do 1351*a7ebe2d4SWarner Losh ifconfig "${vmnet}" destroy 1352*a7ebe2d4SWarner Losh done 1353*a7ebe2d4SWarner Losh 1354*a7ebe2d4SWarner Losh conf=${NETBOOT_STATE_DIR}/dnsmasq.conf 1355*a7ebe2d4SWarner Losh cat > "${conf}" <<EOF 1356*a7ebe2d4SWarner Loshport=0 1357*a7ebe2d4SWarner Loshbind-interfaces 1358*a7ebe2d4SWarner Loshenable-tftp 1359*a7ebe2d4SWarner Loshlog-dhcp 1360*a7ebe2d4SWarner Loshpid-file=${NETBOOT_STATE_DIR}/dnsmasq.pid 1361*a7ebe2d4SWarner Loshdhcp-leasefile=${NETBOOT_STATE_DIR}/dnsmasq.leases 1362*a7ebe2d4SWarner Losh 1363*a7ebe2d4SWarner Losh# QEMU's PXE ROM is iPXE; it self-identifies as vendor-class 1364*a7ebe2d4SWarner Losh# "PXEClient:Arch:00000:UNDI:002001" (stand/libsa/bootp.c's own request just 1365*a7ebe2d4SWarner Losh# says "PXEClient", no ":Arch:..." suffix, so this substring match is 1366*a7ebe2d4SWarner Losh# iPXE-only). iPXE's autoboot() gives DHCP root-path priority over 1367*a7ebe2d4SWarner Losh# chainloading the DHCP filename -- if root-path is set it tries to 1368*a7ebe2d4SWarner Losh# sanboot(8) it instead, which fails outright for a tftp:// URI ("Could not 1369*a7ebe2d4SWarner Losh# open SAN device"). So root-path below is withheld from iPXE's own 1370*a7ebe2d4SWarner Losh# negotiation and only given once stand/libsa/bootp.c does its own separate 1371*a7ebe2d4SWarner Losh# BOOTP call after pxeboot/loader.efi has already been chainloaded. 1372*a7ebe2d4SWarner Loshdhcp-vendorclass=set:ipxerom,PXEClient:Arch 1373*a7ebe2d4SWarner LoshEOF 1374*a7ebe2d4SWarner Losh 1375*a7ebe2d4SWarner Losh while read vmnet gw client prefix tftpdir bootfile cmdfile; do 1376*a7ebe2d4SWarner Losh ifconfig "${vmnet}" create group boot-test 1377*a7ebe2d4SWarner Losh ifconfig "${vmnet}" inet "${gw}/${prefix}" up 1378*a7ebe2d4SWarner Losh chown "${invoker}" "/dev/${vmnet}" 1379*a7ebe2d4SWarner Losh 1380*a7ebe2d4SWarner Losh # dnsmasq's dhcp-boot only sets DHCP option 67 (bootfile-name), never 1381*a7ebe2d4SWarner Losh # the classic fixed-length BOOTP "file" field -- confirmed by comparing 1382*a7ebe2d4SWarner Losh # against QEMU's own slirp DHCP server, which sets that field directly 1383*a7ebe2d4SWarner Losh # and gets a plain "Filename: pxeboot" chainload with no further 1384*a7ebe2d4SWarner Losh # ceremony. Without it, and since iPXE requested option 43 (PXE vendor 1385*a7ebe2d4SWarner Losh # info) in its Parameter-Request list, iPXE assumes it must run the full 1386*a7ebe2d4SWarner Losh # PXE boot-server-discovery dance instead of trusting option 67 -- seen 1387*a7ebe2d4SWarner Losh # on the wire as a TFTP RRQ with an empty filename, then "No 1388*a7ebe2d4SWarner Losh # configuration methods succeeded" / "PXEBS ... Connection timed 1389*a7ebe2d4SWarner Losh # out". Telling it not to via PXE discovery-control (option 43 1390*a7ebe2d4SWarner Losh # sub-option 6, value 8 = "use bootfile name from the DHCP packet, don't 1391*a7ebe2d4SWarner Losh # discover") fixes it directly, without dnsmasq's heavier --pxe-service 1392*a7ebe2d4SWarner Losh # boot-menu machinery (which defaults discovery-control to 3, still 1393*a7ebe2d4SWarner Losh # triggering discovery). 1394*a7ebe2d4SWarner Losh cat >> "${conf}" <<EOF 1395*a7ebe2d4SWarner Loshinterface=${vmnet} 1396*a7ebe2d4SWarner Loshdhcp-range=set:${vmnet},${client},${client},255.255.255.252,1h 1397*a7ebe2d4SWarner Loshdhcp-boot=tag:${vmnet},${bootfile} 1398*a7ebe2d4SWarner Loshdhcp-option=tag:${vmnet},tag:ipxerom,encap:43,6,8 1399*a7ebe2d4SWarner Loshdhcp-option=tag:${vmnet},tag:!ipxerom,17,"tftp://${gw}/" 1400*a7ebe2d4SWarner Loshtftp-root=${tftpdir},${vmnet} 1401*a7ebe2d4SWarner LoshEOF 1402*a7ebe2d4SWarner Losh done < "${planfile}" 1403*a7ebe2d4SWarner Losh 1404*a7ebe2d4SWarner Losh dnsmasq --conf-file="${conf}" 1405*a7ebe2d4SWarner Losh 1406*a7ebe2d4SWarner Losh # dnsmasq daemonizes itself; wait for the pidfile before trusting it. 1407*a7ebe2d4SWarner Losh tries=0 1408*a7ebe2d4SWarner Losh while [ ! -s "${NETBOOT_STATE_DIR}/dnsmasq.pid" ] && [ ${tries} -lt 5 ]; do 1409*a7ebe2d4SWarner Losh sleep 1 1410*a7ebe2d4SWarner Losh tries=$((tries + 1)) 1411*a7ebe2d4SWarner Losh done 1412*a7ebe2d4SWarner Losh [ -s "${NETBOOT_STATE_DIR}/dnsmasq.pid" ] || die "dnsmasq did not start" 1413*a7ebe2d4SWarner Losh 1414*a7ebe2d4SWarner Losh md5 -q "${planfile}" > "${NETBOOT_STATE_DIR}/state.hash" 1415*a7ebe2d4SWarner Losh chown "${invoker}" "${NETBOOT_STATE_DIR}" "${conf}" "${planfile}" \ 1416*a7ebe2d4SWarner Losh "${NETBOOT_STATE_DIR}/dnsmasq.pid" "${NETBOOT_STATE_DIR}/state.hash" 1417*a7ebe2d4SWarner Losh} 1418*a7ebe2d4SWarner Losh 1419*a7ebe2d4SWarner Losh# Manual cleanup: `sudo sh boot-test.sh --netboot-teardown`. Not run 1420*a7ebe2d4SWarner Losh# automatically -- leaving the setup running is what makes it "once per 1421*a7ebe2d4SWarner Losh# boot" instead of "once per run" (see netboot_network_setup). 1422*a7ebe2d4SWarner Loshnetboot_teardown() { 1423*a7ebe2d4SWarner Losh if [ -s "${NETBOOT_STATE_DIR}/dnsmasq.pid" ]; then 1424*a7ebe2d4SWarner Losh kill "$(cat ${NETBOOT_STATE_DIR}/dnsmasq.pid)" 2>/dev/null || true 1425*a7ebe2d4SWarner Losh fi 1426*a7ebe2d4SWarner Losh for vmnet in $(ifconfig -g boot-test 2>/dev/null); do 1427*a7ebe2d4SWarner Losh ifconfig "${vmnet}" destroy 1428*a7ebe2d4SWarner Losh done 1429*a7ebe2d4SWarner Losh rm -rf "${NETBOOT_STATE_DIR}" 1430*a7ebe2d4SWarner Losh echo "Netboot vmnet(4)/dnsmasq setup torn down." 1431*a7ebe2d4SWarner Losh} 1432*a7ebe2d4SWarner Losh 1433*a7ebe2d4SWarner Loshassemble_all_images() { 1434*a7ebe2d4SWarner Losh echo "=== Phase 3: Assembling disk images ===" 1435*a7ebe2d4SWarner Losh 1436*a7ebe2d4SWarner Losh if has efi; then 1437*a7ebe2d4SWarner Losh if [ -r "$(param efi_firmware)" ]; then 1438*a7ebe2d4SWarner Losh assemble_efi_gpt 1439*a7ebe2d4SWarner Losh if has mbr; then 1440*a7ebe2d4SWarner Losh assemble_efi_mbr 1441*a7ebe2d4SWarner Losh fi 1442*a7ebe2d4SWarner Losh elif [ -n "$(param efi_firmware)" ]; then 1443*a7ebe2d4SWarner Losh echo "WARNING: EFI firmware not found at $(param efi_firmware), skipping EFI tests" 1444*a7ebe2d4SWarner Losh else 1445*a7ebe2d4SWarner Losh # riscv64 uses u-boot with EFI payload 1446*a7ebe2d4SWarner Losh assemble_efi_gpt 1447*a7ebe2d4SWarner Losh fi 1448*a7ebe2d4SWarner Losh fi 1449*a7ebe2d4SWarner Losh 1450*a7ebe2d4SWarner Losh if has bios; then 1451*a7ebe2d4SWarner Losh assemble_bios_gpt 1452*a7ebe2d4SWarner Losh if has mbr; then 1453*a7ebe2d4SWarner Losh assemble_bios_mbr 1454*a7ebe2d4SWarner Losh fi 1455*a7ebe2d4SWarner Losh fi 1456*a7ebe2d4SWarner Losh 1457*a7ebe2d4SWarner Losh if has bios && has efi && [ -r "$(param efi_firmware)" ]; then 1458*a7ebe2d4SWarner Losh assemble_both_gpt 1459*a7ebe2d4SWarner Losh if has mbr; then 1460*a7ebe2d4SWarner Losh assemble_both_mbr 1461*a7ebe2d4SWarner Losh fi 1462*a7ebe2d4SWarner Losh fi 1463*a7ebe2d4SWarner Losh 1464*a7ebe2d4SWarner Losh if has cd; then 1465*a7ebe2d4SWarner Losh if has prep; then 1466*a7ebe2d4SWarner Losh assemble_pseries_cd # pseries SLOF CHRP CD 1467*a7ebe2d4SWarner Losh elif has ofw; then 1468*a7ebe2d4SWarner Losh assemble_ofw_cd # mac99 Apple/OF CD 1469*a7ebe2d4SWarner Losh else 1470*a7ebe2d4SWarner Losh assemble_cd # x86 El Torito 1471*a7ebe2d4SWarner Losh fi 1472*a7ebe2d4SWarner Losh fi 1473*a7ebe2d4SWarner Losh 1474*a7ebe2d4SWarner Losh if has prep; then 1475*a7ebe2d4SWarner Losh assemble_prep 1476*a7ebe2d4SWarner Losh fi 1477*a7ebe2d4SWarner Losh 1478*a7ebe2d4SWarner Losh # Used for mac99 emulation, though kernel issues prevent testing 1479*a7ebe2d4SWarner Losh if has ofw; then 1480*a7ebe2d4SWarner Losh assemble_ofw 1481*a7ebe2d4SWarner Losh fi 1482*a7ebe2d4SWarner Losh 1483*a7ebe2d4SWarner Losh if has linuxboot; then 1484*a7ebe2d4SWarner Losh if [ -f "${IMGDIR}/linuxboot.esp" ]; then 1485*a7ebe2d4SWarner Losh assemble_linuxboot 1486*a7ebe2d4SWarner Losh elif [ -f "${IMGDIR}/linuxboot-initrd.cpio.gz" ]; then 1487*a7ebe2d4SWarner Losh assemble_linuxboot_direct 1488*a7ebe2d4SWarner Losh fi 1489*a7ebe2d4SWarner Losh fi 1490*a7ebe2d4SWarner Losh 1491*a7ebe2d4SWarner Losh if has netboot; then 1492*a7ebe2d4SWarner Losh assemble_netboot 1493*a7ebe2d4SWarner Losh fi 1494*a7ebe2d4SWarner Losh} 1495*a7ebe2d4SWarner Losh 1496*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 1497*a7ebe2d4SWarner Losh# Phase 4: Run tests in parallel 1498*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 1499*a7ebe2d4SWarner Losh 1500*a7ebe2d4SWarner Loshrun_one_test() { 1501*a7ebe2d4SWarner Losh name=$1 1502*a7ebe2d4SWarner Losh log="${LOGDIR}/${name}.log" 1503*a7ebe2d4SWarner Losh cmd=$(cat ${OUTDIR}/test-cmd-${name}.sh) 1504*a7ebe2d4SWarner Losh 1505*a7ebe2d4SWarner Losh expect -c " 1506*a7ebe2d4SWarner Losh set timeout ${TIMEOUT} 1507*a7ebe2d4SWarner Losh log_file -noappend \"${log}\" 1508*a7ebe2d4SWarner Losh spawn {*}${cmd} 1509*a7ebe2d4SWarner Losh expect { 1510*a7ebe2d4SWarner Losh \"SUCCESS\" { exit 0 } 1511*a7ebe2d4SWarner Losh timeout { exit 1 } 1512*a7ebe2d4SWarner Losh eof { exit 2 } 1513*a7ebe2d4SWarner Losh } 1514*a7ebe2d4SWarner Losh " >/dev/null 2>&1 1515*a7ebe2d4SWarner Losh return $? 1516*a7ebe2d4SWarner Losh} 1517*a7ebe2d4SWarner Losh 1518*a7ebe2d4SWarner Loshwait_for_slot() { 1519*a7ebe2d4SWarner Losh # Wait until fewer than MAX_JOBS are running 1520*a7ebe2d4SWarner Losh while true; do 1521*a7ebe2d4SWarner Losh running=0 1522*a7ebe2d4SWarner Losh for p in ${active_pids}; do 1523*a7ebe2d4SWarner Losh if kill -0 $p 2>/dev/null; then 1524*a7ebe2d4SWarner Losh running=$((running + 1)) 1525*a7ebe2d4SWarner Losh fi 1526*a7ebe2d4SWarner Losh done 1527*a7ebe2d4SWarner Losh [ ${running} -lt ${MAX_JOBS} ] && break 1528*a7ebe2d4SWarner Losh sleep 1 1529*a7ebe2d4SWarner Losh done 1530*a7ebe2d4SWarner Losh} 1531*a7ebe2d4SWarner Losh 1532*a7ebe2d4SWarner Losh# Launch every arch's tests together and wait once, so a run of N arches costs 1533*a7ebe2d4SWarner Losh# roughly one timeout rather than N. Each `run_one_test &` snapshots the 1534*a7ebe2d4SWarner Losh# per-arch OUTDIR/LOGDIR/TIMEOUT that setup_arch_env just set, so backgrounded 1535*a7ebe2d4SWarner Losh# jobs keep their own arch context even as we move on to the next arch. 1536*a7ebe2d4SWarner Loshrun_all_tests() { 1537*a7ebe2d4SWarner Losh echo "=== Phase 4: Running tests ===" 1538*a7ebe2d4SWarner Losh 1539*a7ebe2d4SWarner Losh total=0 1540*a7ebe2d4SWarner Losh skipped=0 1541*a7ebe2d4SWarner Losh active_pids="" 1542*a7ebe2d4SWarner Losh results_map=$(mktemp -t boot-test-results) 1543*a7ebe2d4SWarner Losh 1544*a7ebe2d4SWarner Losh for arch in ${ARCHES}; do 1545*a7ebe2d4SWarner Losh setup_arch_env "${arch}" 1546*a7ebe2d4SWarner Losh [ -s "${TESTLIST}" ] || continue 1547*a7ebe2d4SWarner Losh 1548*a7ebe2d4SWarner Losh while read name; do 1549*a7ebe2d4SWarner Losh # Apply test filter if given 1550*a7ebe2d4SWarner Losh if [ -n "${TEST_FILTER}" ]; then 1551*a7ebe2d4SWarner Losh echo "${name}" | grep -qE "${TEST_FILTER}" || { 1552*a7ebe2d4SWarner Losh skipped=$((skipped + 1)) 1553*a7ebe2d4SWarner Losh continue 1554*a7ebe2d4SWarner Losh } 1555*a7ebe2d4SWarner Losh fi 1556*a7ebe2d4SWarner Losh 1557*a7ebe2d4SWarner Losh total=$((total + 1)) 1558*a7ebe2d4SWarner Losh 1559*a7ebe2d4SWarner Losh # Job throttling (global across all arches) 1560*a7ebe2d4SWarner Losh if [ ${MAX_JOBS} -gt 0 ]; then 1561*a7ebe2d4SWarner Losh wait_for_slot 1562*a7ebe2d4SWarner Losh fi 1563*a7ebe2d4SWarner Losh 1564*a7ebe2d4SWarner Losh run_one_test "${name}" & 1565*a7ebe2d4SWarner Losh pid=$! 1566*a7ebe2d4SWarner Losh active_pids="${active_pids} ${pid}" 1567*a7ebe2d4SWarner Losh echo "${pid} ${arch} ${name}" >> ${results_map} 1568*a7ebe2d4SWarner Losh done < ${TESTLIST} 1569*a7ebe2d4SWarner Losh done 1570*a7ebe2d4SWarner Losh 1571*a7ebe2d4SWarner Losh [ ${total} -gt 0 ] || die "No tests registered. Run without -B first." 1572*a7ebe2d4SWarner Losh echo " ${total} tests launched (${skipped} skipped by filter), waiting..." 1573*a7ebe2d4SWarner Losh echo "" 1574*a7ebe2d4SWarner Losh 1575*a7ebe2d4SWarner Losh # Collect results - disable errexit since wait returns the child's exit status 1576*a7ebe2d4SWarner Losh set +e 1577*a7ebe2d4SWarner Losh pass=0 1578*a7ebe2d4SWarner Losh fail=0 1579*a7ebe2d4SWarner Losh timeout_count=0 1580*a7ebe2d4SWarner Losh while read pid arch name; do 1581*a7ebe2d4SWarner Losh wait ${pid} 1582*a7ebe2d4SWarner Losh rc=$? 1583*a7ebe2d4SWarner Losh case ${rc} in 1584*a7ebe2d4SWarner Losh 0) 1585*a7ebe2d4SWarner Losh result="PASS" 1586*a7ebe2d4SWarner Losh pass=$((pass + 1)) 1587*a7ebe2d4SWarner Losh ;; 1588*a7ebe2d4SWarner Losh 1) 1589*a7ebe2d4SWarner Losh result="TIMEOUT" 1590*a7ebe2d4SWarner Losh timeout_count=$((timeout_count + 1)) 1591*a7ebe2d4SWarner Losh ;; 1592*a7ebe2d4SWarner Losh *) 1593*a7ebe2d4SWarner Losh result="FAILED" 1594*a7ebe2d4SWarner Losh fail=$((fail + 1)) 1595*a7ebe2d4SWarner Losh ;; 1596*a7ebe2d4SWarner Losh esac 1597*a7ebe2d4SWarner Losh printf " %-12s %-40s %s\n" "${arch}" "${name}" "${result}" 1598*a7ebe2d4SWarner Losh done < ${results_map} 1599*a7ebe2d4SWarner Losh 1600*a7ebe2d4SWarner Losh echo "" 1601*a7ebe2d4SWarner Losh echo "=== Results: ${pass} passed, ${fail} failed, ${timeout_count} timed out (of ${total}) ===" 1602*a7ebe2d4SWarner Losh 1603*a7ebe2d4SWarner Losh rm -f ${results_map} 1604*a7ebe2d4SWarner Losh [ ${fail} -eq 0 ] && [ ${timeout_count} -eq 0 ] 1605*a7ebe2d4SWarner Losh} 1606*a7ebe2d4SWarner Losh 1607*a7ebe2d4SWarner Losh# Build the tree and images for each arch, one arch at a time. Each arch runs 1608*a7ebe2d4SWarner Losh# in a subshell so its setup_arch_env globals stay isolated and a build failure 1609*a7ebe2d4SWarner Losh# (set -e) is caught here instead of aborting the whole run. 1610*a7ebe2d4SWarner Loshbuild_all() { 1611*a7ebe2d4SWarner Losh for arch in ${ARCHES}; do 1612*a7ebe2d4SWarner Losh echo "############################################################" 1613*a7ebe2d4SWarner Losh echo "# ${arch}: building" 1614*a7ebe2d4SWarner Losh echo "############################################################" 1615*a7ebe2d4SWarner Losh if ! ( 1616*a7ebe2d4SWarner Losh setup_arch_env "${arch}" 1617*a7ebe2d4SWarner Losh need_cmd "$(param qemu_bin)" 1618*a7ebe2d4SWarner Losh ${SKIP_BUILD} || build_tree 1619*a7ebe2d4SWarner Losh if ! ${SKIP_IMAGES}; then 1620*a7ebe2d4SWarner Losh [ -d "${DESTDIR}" ] || \ 1621*a7ebe2d4SWarner Losh die "No boot tree at ${DESTDIR}. Run without -b first." 1622*a7ebe2d4SWarner Losh : > ${TESTLIST} # fresh test list before (re)assembling 1623*a7ebe2d4SWarner Losh make_base_images 1624*a7ebe2d4SWarner Losh assemble_all_images 1625*a7ebe2d4SWarner Losh fi 1626*a7ebe2d4SWarner Losh ); then 1627*a7ebe2d4SWarner Losh echo " ${arch}: BUILD FAILED" 1628*a7ebe2d4SWarner Losh fi 1629*a7ebe2d4SWarner Losh done 1630*a7ebe2d4SWarner Losh} 1631*a7ebe2d4SWarner Losh 1632*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 1633*a7ebe2d4SWarner Losh# Main 1634*a7ebe2d4SWarner Losh# -------------------------------------------------------------------------- 1635*a7ebe2d4SWarner Losh 1636*a7ebe2d4SWarner Losh# --netboot-helper/--netboot-teardown are internal/manual entry points that 1637*a7ebe2d4SWarner Losh# skip the whole build+test flow below; see netboot_network_setup. 1638*a7ebe2d4SWarner Loshcase "${NETBOOT_MODE}" in 1639*a7ebe2d4SWarner Losh helper) netboot_helper "${NETBOOT_HELPER_PLAN}"; exit $? ;; 1640*a7ebe2d4SWarner Losh teardown) netboot_teardown; exit $? ;; 1641*a7ebe2d4SWarner Loshesac 1642*a7ebe2d4SWarner Losh 1643*a7ebe2d4SWarner Losh# Preflight: universal tools (per-arch qemu binaries are checked in build_all). 1644*a7ebe2d4SWarner Loshfor prog in jq expect makefs mkimg; do 1645*a7ebe2d4SWarner Losh need_cmd "${prog}" 1646*a7ebe2d4SWarner Loshdone 1647*a7ebe2d4SWarner Losh 1648*a7ebe2d4SWarner Loshecho "FreeBSD boot loader test suite: ${ARCHES}" 1649*a7ebe2d4SWarner Loshecho "" 1650*a7ebe2d4SWarner Losh 1651*a7ebe2d4SWarner Losh# Accumulates netboot test plan lines across every arch (unlike ${TESTLIST}, 1652*a7ebe2d4SWarner Losh# which build_all truncates per-arch) -- see register_netboot_test. 1653*a7ebe2d4SWarner LoshNETBOOT_PLAN=$(mktemp -t boot-test-netboot-plan) 1654*a7ebe2d4SWarner Losh 1655*a7ebe2d4SWarner Loshif $do_report_dirs; then 1656*a7ebe2d4SWarner Losh for arch in ${ARCHES}; do 1657*a7ebe2d4SWarner Losh setup_arch_env "${arch}" 1658*a7ebe2d4SWarner Losh echo "${arch} settings:" 1659*a7ebe2d4SWarner Losh echo " TIMEOUT=${TIMEOUT}" 1660*a7ebe2d4SWarner Losh echo " ARCH_OBJDIR=${ARCH_OBJDIR}" 1661*a7ebe2d4SWarner Losh echo " OUTDIR=${OUTDIR}" 1662*a7ebe2d4SWarner Losh echo " LOGDIR=${LOGDIR}" 1663*a7ebe2d4SWarner Losh echo " DESTDIR=${DESTDIR}" 1664*a7ebe2d4SWarner Losh echo " TESTLIST=${TESTLIST}" 1665*a7ebe2d4SWarner Losh done 1666*a7ebe2d4SWarner Losh exit 0 1667*a7ebe2d4SWarner Loshfi 1668*a7ebe2d4SWarner Losh 1669*a7ebe2d4SWarner Loshbuild_all 1670*a7ebe2d4SWarner Loshnetboot_network_setup 1671*a7ebe2d4SWarner Loshif run_all_tests; then 1672*a7ebe2d4SWarner Losh rc=0 1673*a7ebe2d4SWarner Loshelse 1674*a7ebe2d4SWarner Losh rc=$? 1675*a7ebe2d4SWarner Loshfi 1676*a7ebe2d4SWarner Loshrm -f "${NETBOOT_PLAN}" 1677*a7ebe2d4SWarner Loshexit ${rc} 1678