brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · 79505d2 Raw
476 lines · bash
1#!/bin/bash2# SPDX-License-Identifier: GPL-2.03 4set -e5 6# This script currently only works for the following platforms,7# as it is based on the VM image used by the BPF CI, which is8# available only for these architectures. We can also specify9# the local rootfs image generated by the following script:10# https://github.com/libbpf/ci/blob/main/rootfs/mkrootfs_debian.sh11PLATFORM="${PLATFORM:-$(uname -m)}"12case "${PLATFORM}" in13s390x)14	QEMU_BINARY=qemu-system-s390x15	QEMU_CONSOLE="ttyS1"16	HOST_FLAGS=(-smp 2 -enable-kvm)17	CROSS_FLAGS=(-smp 2)18	BZIMAGE="arch/s390/boot/vmlinux"19	ARCH="s390"20	;;21x86_64)22	QEMU_BINARY=qemu-system-x86_6423	QEMU_CONSOLE="ttyS0,115200"24	HOST_FLAGS=(-cpu host -enable-kvm -smp 8)25	CROSS_FLAGS=(-smp 8)26	BZIMAGE="arch/x86/boot/bzImage"27	ARCH="x86"28	;;29aarch64)30	QEMU_BINARY=qemu-system-aarch6431	QEMU_CONSOLE="ttyAMA0,115200"32	HOST_FLAGS=(-M virt,gic-version=3 -cpu host -enable-kvm -smp 8)33	CROSS_FLAGS=(-M virt,gic-version=3 -cpu cortex-a76 -smp 8)34	BZIMAGE="arch/arm64/boot/Image"35	ARCH="arm64"36	;;37riscv64)38	# required qemu version v7.2.0+39	QEMU_BINARY=qemu-system-riscv6440	QEMU_CONSOLE="ttyS0,115200"41	HOST_FLAGS=(-M virt -cpu host -enable-kvm -smp 8)42	CROSS_FLAGS=(-M virt -cpu rv64,sscofpmf=true -smp 8)43	BZIMAGE="arch/riscv/boot/Image"44	ARCH="riscv"45	;;46*)47	echo "Unsupported architecture"48	exit 149	;;50esac51DEFAULT_COMMAND="./test_progs"52MOUNT_DIR="mnt"53LOCAL_ROOTFS_IMAGE=""54ROOTFS_IMAGE="root.img"55OUTPUT_DIR="$HOME/.bpf_selftests"56KCONFIG_REL_PATHS=("tools/testing/selftests/bpf/config"57	"tools/testing/selftests/bpf/config.vm"58	"tools/testing/selftests/bpf/config.${PLATFORM}")59INDEX_URL="https://raw.githubusercontent.com/libbpf/ci/master/INDEX"60NUM_COMPILE_JOBS="$(nproc)"61LOG_FILE_BASE="$(date +"bpf_selftests.%Y-%m-%d_%H-%M-%S")"62LOG_FILE="${LOG_FILE_BASE}.log"63EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"64 65usage()66{67	cat <<EOF68Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]69 70<command> is the command you would normally run when you are in71tools/testing/selftests/bpf. e.g:72 73	$0 -- ./test_progs -t test_lsm74 75If no command is specified and a debug shell (-s) is not requested,76"${DEFAULT_COMMAND}" will be run by default.77 78Using PLATFORM= and CROSS_COMPILE= options will enable cross platform testing:79 80  PLATFORM=<platform> CROSS_COMPILE=<toolchain> $0 -- ./test_progs -t test_lsm81 82If you build your kernel using KBUILD_OUTPUT= or O= options, these83can be passed as environment variables to the script:84 85  O=<kernel_build_path> $0 -- ./test_progs -t test_lsm86 87or88 89  KBUILD_OUTPUT=<kernel_build_path> $0 -- ./test_progs -t test_lsm90 91Options:92 93	-l)             Specify the path to the local rootfs image.94	-i)		Update the rootfs image with a newer version.95	-d)		Update the output directory (default: ${OUTPUT_DIR})96	-j)		Number of jobs for compilation, similar to -j in make97			(default: ${NUM_COMPILE_JOBS})98	-s)		Instead of powering off the VM, start an interactive99			shell. If <command> is specified, the shell runs after100			the command finishes executing101EOF102}103 104unset URLS105populate_url_map()106{107	if ! declare -p URLS &> /dev/null; then108		# URLS contain the mapping from file names to URLs where109		# those files can be downloaded from.110		declare -gA URLS111		while IFS=$'\t' read -r name url; do112			URLS["$name"]="$url"113		done < <(curl -Lsf ${INDEX_URL})114	fi115}116 117newest_rootfs_version()118{119	{120	for file in "${!URLS[@]}"; do121		if [[ $file =~ ^"${PLATFORM}"/libbpf-vmtest-rootfs-(.*)\.tar\.zst$ ]]; then122			echo "${BASH_REMATCH[1]}"123		fi124	done125	} | sort -rV | head -1126}127 128download_rootfs()129{130	populate_url_map131 132	local rootfsversion="$(newest_rootfs_version)"133	local file="${PLATFORM}/libbpf-vmtest-rootfs-$rootfsversion.tar.zst"134 135	if [[ ! -v URLS[$file] ]]; then136		echo "$file not found" >&2137		return 1138	fi139 140	echo "Downloading $file..." >&2141	curl -Lsf "${URLS[$file]}" "${@:2}"142}143 144load_rootfs()145{146	local dir="$1"147 148	if ! which zstd &> /dev/null; then149		echo 'Could not find "zstd" on the system, please install zstd'150		exit 1151	fi152 153	if [[ -n "${LOCAL_ROOTFS_IMAGE}" ]]; then154		cat "${LOCAL_ROOTFS_IMAGE}" | zstd -d | sudo tar -C "$dir" -x155	else156		download_rootfs | zstd -d | sudo tar -C "$dir" -x157	fi158}159 160recompile_kernel()161{162	local kernel_checkout="$1"163	local make_command="$2"164 165	cd "${kernel_checkout}"166 167	${make_command} olddefconfig168	${make_command}169}170 171mount_image()172{173	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"174	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"175 176	sudo mount -o loop "${rootfs_img}" "${mount_dir}"177}178 179unmount_image()180{181	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"182 183	sudo umount "${mount_dir}" &> /dev/null184}185 186update_selftests()187{188	local kernel_checkout="$1"189	local selftests_dir="${kernel_checkout}/tools/testing/selftests/bpf"190 191	cd "${selftests_dir}"192	${make_command}193 194	# Mount the image and copy the selftests to the image.195	mount_image196	sudo rm -rf "${mount_dir}/root/bpf"197	sudo cp -r "${selftests_dir}" "${mount_dir}/root"198	unmount_image199}200 201update_init_script()202{203	local init_script_dir="${OUTPUT_DIR}/${MOUNT_DIR}/etc/rcS.d"204	local init_script="${init_script_dir}/S50-startup"205	local command="$1"206	local exit_command="$2"207 208	mount_image209 210	if [[ ! -d "${init_script_dir}" ]]; then211		cat <<EOF212Could not find ${init_script_dir} in the mounted image.213This likely indicates a bad rootfs image, Please download214a new image by passing "-i" to the script215EOF216		exit 1217 218	fi219 220	sudo bash -c "echo '#!/bin/bash' > ${init_script}"221 222	if [[ "${command}" != "" ]]; then223		sudo bash -c "cat >>${init_script}" <<EOF224# Have a default value in the exit status file225# incase the VM is forcefully stopped.226echo "130" > "/root/${EXIT_STATUS_FILE}"227 228{229	cd /root/bpf230	echo ${command}231	stdbuf -oL -eL ${command}232	echo "\$?" > "/root/${EXIT_STATUS_FILE}"233} 2>&1 | tee "/root/${LOG_FILE}"234# Ensure that the logs are written to disk235sync236EOF237	fi238 239	sudo bash -c "echo ${exit_command} >> ${init_script}"240	sudo chmod a+x "${init_script}"241	unmount_image242}243 244create_vm_image()245{246	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"247	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"248 249	rm -rf "${rootfs_img}"250	touch "${rootfs_img}"251	chattr +C "${rootfs_img}" >/dev/null 2>&1 || true252 253	truncate -s 2G "${rootfs_img}"254	mkfs.ext4 -q "${rootfs_img}"255 256	mount_image257	load_rootfs "${mount_dir}"258	unmount_image259}260 261run_vm()262{263	local kernel_bzimage="$1"264	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"265 266	if ! which "${QEMU_BINARY}" &> /dev/null; then267		cat <<EOF268Could not find ${QEMU_BINARY}269Please install qemu or set the QEMU_BINARY environment variable.270EOF271		exit 1272	fi273 274	if [[ "${PLATFORM}" != "$(uname -m)" ]]; then275		QEMU_FLAGS=("${CROSS_FLAGS[@]}")276	else277		QEMU_FLAGS=("${HOST_FLAGS[@]}")278	fi279 280	${QEMU_BINARY} \281		-nodefaults \282		-display none \283		-serial mon:stdio \284		"${QEMU_FLAGS[@]}" \285		-m 4G \286		-drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \287		-kernel "${kernel_bzimage}" \288		-append "root=/dev/vda rw console=${QEMU_CONSOLE}"289}290 291copy_logs()292{293	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"294	local log_file="${mount_dir}/root/${LOG_FILE}"295	local exit_status_file="${mount_dir}/root/${EXIT_STATUS_FILE}"296 297	mount_image298	sudo cp ${log_file} "${OUTPUT_DIR}"299	sudo cp ${exit_status_file} "${OUTPUT_DIR}"300	sudo rm -f ${log_file}301	unmount_image302}303 304is_rel_path()305{306	local path="$1"307 308	[[ ${path:0:1} != "/" ]]309}310 311do_update_kconfig()312{313	local kernel_checkout="$1"314	local kconfig_file="$2"315 316	rm -f "$kconfig_file" 2> /dev/null317 318	for config in "${KCONFIG_REL_PATHS[@]}"; do319		local kconfig_src="${kernel_checkout}/${config}"320		cat "$kconfig_src" >> "$kconfig_file"321	done322}323 324update_kconfig()325{326	local kernel_checkout="$1"327	local kconfig_file="$2"328 329	if [[ -f "${kconfig_file}" ]]; then330		local local_modified="$(stat -c %Y "${kconfig_file}")"331 332		for config in "${KCONFIG_REL_PATHS[@]}"; do333			local kconfig_src="${kernel_checkout}/${config}"334			local src_modified="$(stat -c %Y "${kconfig_src}")"335			# Only update the config if it has been updated after the336			# previously cached config was created. This avoids337			# unnecessarily compiling the kernel and selftests.338			if [[ "${src_modified}" -gt "${local_modified}" ]]; then339				do_update_kconfig "$kernel_checkout" "$kconfig_file"340				# Once we have found one outdated configuration341				# there is no need to check other ones.342				break343			fi344		done345	else346		do_update_kconfig "$kernel_checkout" "$kconfig_file"347	fi348}349 350catch()351{352	local exit_code=$1353	local exit_status_file="${OUTPUT_DIR}/${EXIT_STATUS_FILE}"354	# This is just a cleanup and the directory may355	# have already been unmounted. So, don't let this356	# clobber the error code we intend to return.357	unmount_image || true358	if [[ -f "${exit_status_file}" ]]; then359		exit_code="$(cat ${exit_status_file})"360	fi361	exit ${exit_code}362}363 364main()365{366	local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"367	local kernel_checkout=$(realpath "${script_dir}"/../../../../)368	# By default the script searches for the kernel in the checkout directory but369	# it also obeys environment variables O= and KBUILD_OUTPUT=370	local kernel_bzimage="${kernel_checkout}/${BZIMAGE}"371	local command="${DEFAULT_COMMAND}"372	local update_image="no"373	local exit_command="poweroff -f"374	local debug_shell="no"375 376	while getopts ':hskl:id:j:' opt; do377		case ${opt} in378		l)379			LOCAL_ROOTFS_IMAGE="$OPTARG"380			;;381		i)382			update_image="yes"383			;;384		d)385			OUTPUT_DIR="$OPTARG"386			;;387		j)388			NUM_COMPILE_JOBS="$OPTARG"389			;;390		s)391			command=""392			debug_shell="yes"393			exit_command="bash"394			;;395		h)396			usage397			exit 0398			;;399		\? )400			echo "Invalid Option: -$OPTARG"401			usage402			exit 1403			;;404		: )405			echo "Invalid Option: -$OPTARG requires an argument"406			usage407			exit 1408			;;409		esac410	done411	shift $((OPTIND -1))412 413	trap 'catch "$?"' EXIT414 415	if [[ "${PLATFORM}" != "$(uname -m)" ]] && [[ -z "${CROSS_COMPILE}" ]]; then416		echo "Cross-platform testing needs to specify CROSS_COMPILE"417		exit 1418	fi419 420	if [[ $# -eq 0  && "${debug_shell}" == "no" ]]; then421		echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"422	else423		command="$@"424	fi425 426	local kconfig_file="${OUTPUT_DIR}/latest.config"427	local make_command="make ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} \428			    -j ${NUM_COMPILE_JOBS} KCONFIG_CONFIG=${kconfig_file}"429 430	# Figure out where the kernel is being built.431	# O takes precedence over KBUILD_OUTPUT.432	if [[ "${O:=""}" != "" ]]; then433		if is_rel_path "${O}"; then434			O="$(realpath "${PWD}/${O}")"435		fi436		kernel_bzimage="${O}/${BZIMAGE}"437		make_command="${make_command} O=${O}"438	elif [[ "${KBUILD_OUTPUT:=""}" != "" ]]; then439		if is_rel_path "${KBUILD_OUTPUT}"; then440			KBUILD_OUTPUT="$(realpath "${PWD}/${KBUILD_OUTPUT}")"441		fi442		kernel_bzimage="${KBUILD_OUTPUT}/${BZIMAGE}"443		make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}"444	fi445 446	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"447	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"448 449	echo "Output directory: ${OUTPUT_DIR}"450 451	mkdir -p "${OUTPUT_DIR}"452	mkdir -p "${mount_dir}"453	update_kconfig "${kernel_checkout}" "${kconfig_file}"454 455	recompile_kernel "${kernel_checkout}" "${make_command}"456 457	if [[ "${update_image}" == "no" && ! -f "${rootfs_img}" ]]; then458		echo "rootfs image not found in ${rootfs_img}"459		update_image="yes"460	fi461 462	if [[ "${update_image}" == "yes" ]]; then463		create_vm_image464	fi465 466	update_selftests "${kernel_checkout}" "${make_command}"467	update_init_script "${command}" "${exit_command}"468	run_vm "${kernel_bzimage}"469	if [[ "${command}" != "" ]]; then470		copy_logs471		echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"472	fi473}474 475main "$@"476