1# SPDX-License-Identifier: BSD-2-Clause 2# 3# Copyright (c) 2024 The FreeBSD Foundation 4# 5# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org> 6# under sponsorship from the FreeBSD Foundation. 7# 8# Makefile for CI testing. 9# 10# User-driven targets: 11# ci: Run CI tests 12# ci-smoke: Run smoke tests which is simply booting the image 13# ci-full: Run full tests 14# 15# Variables affecting the build process: 16# TARGET/TARGET_ARCH: architecture of built release (default: same as build host) 17# KERNELCONF: kernel configuration to use 18# USE_QEMU: Use QEMU for testing rather than bhyve 19# 20 21WORLDDIR?= ${.CURDIR}/../.. 22RELEASEDIR= ${WORLDDIR}/release 23MAKECONF?= /dev/null 24SRCCONF?= /dev/null 25_MEMORY!=sysctl -n hw.physmem 2>/dev/null 26PARALLEL_JOBS!=sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null 27TOTAL_MEMORY!=expr ${_MEMORY} / 1073741824 28KERNCONF?= GENERIC 29LOCALBASE?= /usr/local 30EXTRA_MAKE_FLAGS?= 31 32.if !defined(TARGET) || empty(TARGET) 33TARGET= ${MACHINE} 34.endif 35.if !defined(TARGET_ARCH) || empty(TARGET_ARCH) 36. if ${TARGET} == ${MACHINE} 37TARGET_ARCH= ${MACHINE_ARCH} 38. else 39TARGET_ARCH= ${TARGET} 40. endif 41.endif 42IMAKE= ${MAKE} TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} 43 44.if defined(CROSS_TOOLCHAIN) || !empty(CROSS_TOOLCHAIN) 45CROSS_TOOLCHAIN_PARAM= "CROSS_TOOLCHAIN=${CROSS_TOOLCHAIN}" 46.endif 47 48# Define OSRELEASE by using newvers.sh 49.if !defined(OSRELEASE) || empty(OSRELEASE) 50. for _V in TYPE BRANCH REVISION 51. if !defined(${_V}) || empty(${_V}) 52${_V}!= eval $$(awk '/^${_V}=/{print}' ${.CURDIR}/../../sys/conf/newvers.sh); echo $$${_V} 53. endif 54. endfor 55. for _V in ${TARGET_ARCH} 56. if !empty(TARGET:M${_V}) 57OSRELEASE= ${TYPE}-${REVISION}-${BRANCH}-${TARGET} 58VOLUME_LABEL= ${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET} 59. else 60OSRELEASE= ${TYPE}-${REVISION}-${BRANCH}-${TARGET}-${TARGET_ARCH} 61VOLUME_LABEL= ${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET_ARCH} 62. endif 63. endfor 64.endif 65 66.if exists(${.CURDIR}/tools/ci.conf) && !defined(CICONF) 67CICONF?= ${.CURDIR}/tools/ci.conf 68.endif 69SWAPSIZE?= 1g 70VMFS?= ufs 71FORMAT= raw 72CIIMAGE= ci-${OSRELEASE}-${GITREV}-${KERNCONF}.${FORMAT} 73CIDISK?= ${.OBJDIR}/${CIIMAGE} 74VMSIZE?= 6g 75CITYPE?= full 76META_TAR!=mktemp /tmp/meta.XXXXXX 77META_DIR!=mktemp -d /tmp/meta.XXXXXX 78META_DIROUT!=mktemp -d /tmp/meta.XXXXXX 79DISC_CAM!=truncate -s 128m /tmp/disk-cam 80EXTRA_DISK_NUM?=5 81DISK_NUMBERS!=jot - 1 ${EXTRA_DISK_NUM} 82BHYVE_EXTRA_DISK_PARAM?= 83BHYVE_EXTRA_DISK_PARAM+=-s 4:0,ahci-hd,/tmp/disk-cam 84.for i in ${DISK_NUMBERS} 85NEW_DISK!=truncate -s 128m /tmp/disk${i} 86BHYVE_EXTRA_DISK_PARAM+=-s $$((${i} + 4)):0,virtio-blk,/tmp/disk${i} 87CLEANFILES+=/tmp/disk${i} 88.endfor 89TEST_VM_NAME= ci-${OSRELEASE}-${GITREV}-${KERNCONF} 90.if ${TOTAL_MEMORY} >= 16 91VM_MEM=8 92.elif ${TOTAL_MEMORY} >=4 93VM_MEM=${TOTAL_MEMORY} 94.else 95echo "Please increase the memory to at least 4GB" 96exit 0 97.endif 98VM_MEM_SIZE?=${VM_MEM}g 99TIMEOUT_MS?=5400000 100TIMEOUT=$$((${TIMEOUT_MS} / 1000)) 101TIMEOUT_EXPECT=$$((${TIMEOUT} - 60)) 102TIMEOUT_VM=$$((${TIMEOUT_EXPECT} - 120)) 103.if exists(${.CURDIR}/Makefile.${TARGET_ARCH}) 104. include "${.CURDIR}/Makefile.${TARGET_ARCH}" 105.endif 106.if ${TARGET_ARCH} != ${MACHINE_ARCH} 107. if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" ) 108QEMUSTATIC=/usr/local/bin/qemu-${QEMU_ARCH}-static 109QEMUTGT=portinstall-qemu 110. endif 111.endif 112QEMUTGT?= 113QEMU_DEVICES?= 114QEMU_EXTRA_PARAM?= 115QEMU_MACHINE?=virt 116QEMUBIN=/usr/local/bin/qemu-system-${QEMU_ARCH} 117.if ${PARALLEL_JOBS} >= ${QEMU_MAX_CPU_COUNT} 118QEMU_CPU_COUNT=${QEMU_MAX_CPU_COUNT} 119.else 120QEMU_CPU_COUNT=${PARALLEL_JOBS} 121.endif 122.if ${VM_MEM} >= ${QEMU_MAX_MEM_SIZE} 123VM_MEM_SIZE=${QEMU_MAX_MEM_SIZE}g 124.else 125VM_MEM_SIZE=${VM_MEM}g 126.endif 127VMGUEST!=sysctl -n kern.vm_guest 2>/dev/null || true 128.if ${VMGUEST} != "none" 129USE_QEMU?=1 130.endif 131KLDFILEMONISLOADED!=kldload -q -n filemon 2>/dev/null && echo "1" || echo "0" 132.if ${KLDFILEMONISLOADED} == "1" 133METAMODE?=-DWITH_META_MODE 134.endif 135 136CLEANFILES+= ${.OBJDIR}/${CIIMAGE} ${.OBJDIR}/ci.img ${META_TAR} 137IMAGEDIR= ${.OBJDIR}/ci-buildimage 138CLEANDIRS+= ${IMAGEDIR} 139 140portinstall: portinstall-pkg portinstall-qemu portinstall-expect portinstall-${TARGET_ARCH:tl} .PHONY 141 142portinstall-pkg: .PHONY 143.if !exists(/usr/local/sbin/pkg-static) 144 env ASSUME_ALWAYS_YES=yes pkg bootstrap 145.endif 146 147portinstall-qemu: portinstall-pkg .PHONY 148.if !exists(/usr/local/bin/qemu-${QEMU_ARCH}-static) 149 env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu-user-static 150.endif 151.if !exists(${QEMUBIN}) 152 env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu@nox11 153.endif 154 155portinstall-expect: portinstall-pkg .PHONY 156.if !exists(/usr/local/bin/expect) 157 env ASSUME_ALWAYS_YES=yes pkg install lang/expect 158.endif 159 160beforeclean: .PHONY 161 chflags -R noschg ${IMAGEDIR} 162 163.include <bsd.obj.mk> 164clean: beforeclean .PHONY 165 166cleandir: beforeclean .PHONY 167 168ci-buildworld: .PHONY 169 @echo "Building world for ${TARGET_ARCH}" 170 ${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \ 171 ${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \ 172 ${EXTRA_MAKE_FLAGS} buildworld > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \ 173 (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) 174 175 176ci-buildkernel: ci-buildworld-${TARGET_ARCH:tl} .PHONY 177 @echo "Building kernel for ${TARGET_ARCH}" 178 ${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \ 179 ${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \ 180 ${EXTRA_MAKE_FLAGS} KERNCONF=${KERNCONF} \ 181 buildkernel > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \ 182 (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) 183 184ci-buildimage: ${QEMUTGT} ci-buildkernel-${TARGET_ARCH:tl} .PHONY 185 @echo "Building ci image for ${TARGET_ARCH}" 186 mkdir -p ${.OBJDIR}/${.TARGET} 187 env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \ 188 QEMUSTATIC=${QEMUSTATIC} CITYPE=${CITYPE} \ 189 ${RELEASEDIR}/scripts/mk-vmimage.sh \ 190 -C ${RELEASEDIR}/tools/vmimage.subr -d ${.OBJDIR}/${.TARGET} -F ${VMFS} \ 191 -i ${.OBJDIR}/ci.img -s ${VMSIZE} -f ${FORMAT} \ 192 -S ${WORLDDIR} -o ${.OBJDIR}/${CIIMAGE} -c ${CICONF} \ 193 > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \ 194 (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false) 195 touch ${.TARGET} 196 197ci-set-smoke-var: .PHONY 198CITYPE=smoke 199 200ci-set-full-var: .PHONY 201CITYPE=full 202 203ci-create-meta: .PHONY 204 truncate -s 512M ${META_TAR} 205 tar rvf ${META_TAR} -C ${META_DIR} . 206 207ci-extract-meta: .PHONY 208 tar xfv ${META_TAR} -C ${META_DIROUT} 209 rm -rf ${META_TAR} ${META_DIR} 210 @echo "Extracted kyua reports to ${META_DIROUT}" 211 212ci-runtest: ci-buildimage-${TARGET_ARCH:tl} portinstall .PHONY 213.if ${MACHINE} == "amd64" && ( ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" ) && ( !defined(USE_QEMU) || empty(USE_QEMU) ) 214 /usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy || true 215 expect -c "set timeout ${TIMEOUT_EXPECT}; \ 216 spawn /usr/bin/timeout -k 5s 30s /usr/sbin/bhyveload \ 217 -c stdio -m ${VM_MEM_SIZE} -d ${CIDISK} ${TEST_VM_NAME}; \ 218 expect { eof }; \ 219 exit [lindex [wait] 3]" 220 expect -c "set timeout ${TIMEOUT_EXPECT}; \ 221 spawn /usr/bin/timeout -k 60 ${TIMEOUT_VM} /usr/sbin/bhyve \ 222 -c ${PARALLEL_JOBS} -m ${VM_MEM_SIZE} -A -H -P \ 223 -s 0:0,hostbridge \ 224 -s 1:0,lpc \ 225 -s 2:0,virtio-blk,${CIDISK} \ 226 -s 3:0,virtio-blk,${META_TAR} \ 227 ${BHYVE_EXTRA_DISK_PARAM} \ 228 -l com1,stdio \ 229 ${TEST_VM_NAME}; \ 230 expect { eof }" 231 /usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy 232.else 233 timeout -k 60 ${TIMEOUT_VM} ${QEMUBIN} \ 234 -machine ${QEMU_MACHINE} \ 235 -smp ${QEMU_CPU_COUNT} \ 236 -m ${VM_MEM_SIZE} \ 237 -nographic \ 238 -no-reboot \ 239 ${QEMU_EXTRA_PARAM} \ 240 -device virtio-blk,drive=hd0 \ 241 -device virtio-blk,drive=hd1 \ 242 -blockdev driver=raw,node-name=hd0,file.driver=file,file.filename=${CIDISK} \ 243 -blockdev driver=raw,node-name=hd1,file.driver=file,file.filename=${META_TAR} \ 244 ${QEMU_DEVICES} 245.endif 246 247ci-checktarget: .PHONY 248.if ${TARGET_ARCH} != "aarch64" && \ 249 ${TARGET_ARCH} != "amd64" && \ 250 ${TARGET_ARCH} != "armv7" && \ 251 ${TARGET_ARCH} != "powerpc64" && \ 252 ${TARGET_ARCH} != "powerpc64le" && \ 253 ${TARGET_ARCH} != "riscv64" 254 @false 255.ERROR: 256 @echo "Error: ${TARGET_ARCH} is not supported on ${TYPE} ${REVISION} ${BRANCH}" 257.endif 258 259ci-smoke: ci-set-smoke-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .PHONY 260 261ci-full: ci-set-full-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .WAIT ci-extract-meta .PHONY 262 263ci: ci-${CITYPE:tl} .PHONY 264 265.include "${RELEASEDIR}/Makefile.inc1" 266