cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

vmtest.sh (9890B)


      1#!/bin/bash
      2# SPDX-License-Identifier: GPL-2.0
      3
      4set -u
      5set -e
      6
      7# This script currently only works for x86_64 and s390x, as
      8# it is based on the VM image used by the BPF CI, which is
      9# available only for these architectures.
     10ARCH="$(uname -m)"
     11case "${ARCH}" in
     12s390x)
     13	QEMU_BINARY=qemu-system-s390x
     14	QEMU_CONSOLE="ttyS1"
     15	QEMU_FLAGS=(-smp 2)
     16	BZIMAGE="arch/s390/boot/compressed/vmlinux"
     17	;;
     18x86_64)
     19	QEMU_BINARY=qemu-system-x86_64
     20	QEMU_CONSOLE="ttyS0,115200"
     21	QEMU_FLAGS=(-cpu host -smp 8)
     22	BZIMAGE="arch/x86/boot/bzImage"
     23	;;
     24*)
     25	echo "Unsupported architecture"
     26	exit 1
     27	;;
     28esac
     29DEFAULT_COMMAND="./test_progs"
     30MOUNT_DIR="mnt"
     31ROOTFS_IMAGE="root.img"
     32OUTPUT_DIR="$HOME/.bpf_selftests"
     33KCONFIG_URL="https://raw.githubusercontent.com/libbpf/libbpf/master/travis-ci/vmtest/configs/config-latest.${ARCH}"
     34KCONFIG_API_URL="https://api.github.com/repos/libbpf/libbpf/contents/travis-ci/vmtest/configs/config-latest.${ARCH}"
     35INDEX_URL="https://raw.githubusercontent.com/libbpf/ci/master/INDEX"
     36NUM_COMPILE_JOBS="$(nproc)"
     37LOG_FILE_BASE="$(date +"bpf_selftests.%Y-%m-%d_%H-%M-%S")"
     38LOG_FILE="${LOG_FILE_BASE}.log"
     39EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
     40
     41usage()
     42{
     43	cat <<EOF
     44Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]
     45
     46<command> is the command you would normally run when you are in
     47tools/testing/selftests/bpf. e.g:
     48
     49	$0 -- ./test_progs -t test_lsm
     50
     51If no command is specified and a debug shell (-s) is not requested,
     52"${DEFAULT_COMMAND}" will be run by default.
     53
     54If you build your kernel using KBUILD_OUTPUT= or O= options, these
     55can be passed as environment variables to the script:
     56
     57  O=<kernel_build_path> $0 -- ./test_progs -t test_lsm
     58
     59or
     60
     61  KBUILD_OUTPUT=<kernel_build_path> $0 -- ./test_progs -t test_lsm
     62
     63Options:
     64
     65	-i)		Update the rootfs image with a newer version.
     66	-d)		Update the output directory (default: ${OUTPUT_DIR})
     67	-j)		Number of jobs for compilation, similar to -j in make
     68			(default: ${NUM_COMPILE_JOBS})
     69	-s)		Instead of powering off the VM, start an interactive
     70			shell. If <command> is specified, the shell runs after
     71			the command finishes executing
     72EOF
     73}
     74
     75unset URLS
     76populate_url_map()
     77{
     78	if ! declare -p URLS &> /dev/null; then
     79		# URLS contain the mapping from file names to URLs where
     80		# those files can be downloaded from.
     81		declare -gA URLS
     82		while IFS=$'\t' read -r name url; do
     83			URLS["$name"]="$url"
     84		done < <(curl -Lsf ${INDEX_URL})
     85	fi
     86}
     87
     88download()
     89{
     90	local file="$1"
     91
     92	if [[ ! -v URLS[$file] ]]; then
     93		echo "$file not found" >&2
     94		return 1
     95	fi
     96
     97	echo "Downloading $file..." >&2
     98	curl -Lsf "${URLS[$file]}" "${@:2}"
     99}
    100
    101newest_rootfs_version()
    102{
    103	{
    104	for file in "${!URLS[@]}"; do
    105		if [[ $file =~ ^"${ARCH}"/libbpf-vmtest-rootfs-(.*)\.tar\.zst$ ]]; then
    106			echo "${BASH_REMATCH[1]}"
    107		fi
    108	done
    109	} | sort -rV | head -1
    110}
    111
    112download_rootfs()
    113{
    114	local rootfsversion="$1"
    115	local dir="$2"
    116
    117	if ! which zstd &> /dev/null; then
    118		echo 'Could not find "zstd" on the system, please install zstd'
    119		exit 1
    120	fi
    121
    122	download "${ARCH}/libbpf-vmtest-rootfs-$rootfsversion.tar.zst" |
    123		zstd -d | sudo tar -C "$dir" -x
    124}
    125
    126recompile_kernel()
    127{
    128	local kernel_checkout="$1"
    129	local make_command="$2"
    130
    131	cd "${kernel_checkout}"
    132
    133	${make_command} olddefconfig
    134	${make_command}
    135}
    136
    137mount_image()
    138{
    139	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
    140	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
    141
    142	sudo mount -o loop "${rootfs_img}" "${mount_dir}"
    143}
    144
    145unmount_image()
    146{
    147	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
    148
    149	sudo umount "${mount_dir}" &> /dev/null
    150}
    151
    152update_selftests()
    153{
    154	local kernel_checkout="$1"
    155	local selftests_dir="${kernel_checkout}/tools/testing/selftests/bpf"
    156
    157	cd "${selftests_dir}"
    158	${make_command}
    159
    160	# Mount the image and copy the selftests to the image.
    161	mount_image
    162	sudo rm -rf "${mount_dir}/root/bpf"
    163	sudo cp -r "${selftests_dir}" "${mount_dir}/root"
    164	unmount_image
    165}
    166
    167update_init_script()
    168{
    169	local init_script_dir="${OUTPUT_DIR}/${MOUNT_DIR}/etc/rcS.d"
    170	local init_script="${init_script_dir}/S50-startup"
    171	local command="$1"
    172	local exit_command="$2"
    173
    174	mount_image
    175
    176	if [[ ! -d "${init_script_dir}" ]]; then
    177		cat <<EOF
    178Could not find ${init_script_dir} in the mounted image.
    179This likely indicates a bad rootfs image, Please download
    180a new image by passing "-i" to the script
    181EOF
    182		exit 1
    183
    184	fi
    185
    186	sudo bash -c "echo '#!/bin/bash' > ${init_script}"
    187
    188	if [[ "${command}" != "" ]]; then
    189		sudo bash -c "cat >>${init_script}" <<EOF
    190# Have a default value in the exit status file
    191# incase the VM is forcefully stopped.
    192echo "130" > "/root/${EXIT_STATUS_FILE}"
    193
    194{
    195	cd /root/bpf
    196	echo ${command}
    197	stdbuf -oL -eL ${command}
    198	echo "\$?" > "/root/${EXIT_STATUS_FILE}"
    199} 2>&1 | tee "/root/${LOG_FILE}"
    200# Ensure that the logs are written to disk
    201sync
    202EOF
    203	fi
    204
    205	sudo bash -c "echo ${exit_command} >> ${init_script}"
    206	sudo chmod a+x "${init_script}"
    207	unmount_image
    208}
    209
    210create_vm_image()
    211{
    212	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
    213	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
    214
    215	rm -rf "${rootfs_img}"
    216	touch "${rootfs_img}"
    217	chattr +C "${rootfs_img}" >/dev/null 2>&1 || true
    218
    219	truncate -s 2G "${rootfs_img}"
    220	mkfs.ext4 -q "${rootfs_img}"
    221
    222	mount_image
    223	download_rootfs "$(newest_rootfs_version)" "${mount_dir}"
    224	unmount_image
    225}
    226
    227run_vm()
    228{
    229	local kernel_bzimage="$1"
    230	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
    231
    232	if ! which "${QEMU_BINARY}" &> /dev/null; then
    233		cat <<EOF
    234Could not find ${QEMU_BINARY}
    235Please install qemu or set the QEMU_BINARY environment variable.
    236EOF
    237		exit 1
    238	fi
    239
    240	${QEMU_BINARY} \
    241		-nodefaults \
    242		-display none \
    243		-serial mon:stdio \
    244		"${QEMU_FLAGS[@]}" \
    245		-enable-kvm \
    246		-m 4G \
    247		-drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \
    248		-kernel "${kernel_bzimage}" \
    249		-append "root=/dev/vda rw console=${QEMU_CONSOLE}"
    250}
    251
    252copy_logs()
    253{
    254	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
    255	local log_file="${mount_dir}/root/${LOG_FILE}"
    256	local exit_status_file="${mount_dir}/root/${EXIT_STATUS_FILE}"
    257
    258	mount_image
    259	sudo cp ${log_file} "${OUTPUT_DIR}"
    260	sudo cp ${exit_status_file} "${OUTPUT_DIR}"
    261	sudo rm -f ${log_file}
    262	unmount_image
    263}
    264
    265is_rel_path()
    266{
    267	local path="$1"
    268
    269	[[ ${path:0:1} != "/" ]]
    270}
    271
    272update_kconfig()
    273{
    274	local kconfig_file="$1"
    275	local update_command="curl -sLf ${KCONFIG_URL} -o ${kconfig_file}"
    276	# Github does not return the "last-modified" header when retrieving the
    277	# raw contents of the file. Use the API call to get the last-modified
    278	# time of the kernel config and only update the config if it has been
    279	# updated after the previously cached config was created. This avoids
    280	# unnecessarily compiling the kernel and selftests.
    281	if [[ -f "${kconfig_file}" ]]; then
    282		local last_modified_date="$(curl -sL -D - "${KCONFIG_API_URL}" -o /dev/null | \
    283			grep "last-modified" | awk -F ': ' '{print $2}')"
    284		local remote_modified_timestamp="$(date -d "${last_modified_date}" +"%s")"
    285		local local_creation_timestamp="$(stat -c %Y "${kconfig_file}")"
    286
    287		if [[ "${remote_modified_timestamp}" -gt "${local_creation_timestamp}" ]]; then
    288			${update_command}
    289		fi
    290	else
    291		${update_command}
    292	fi
    293}
    294
    295main()
    296{
    297	local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
    298	local kernel_checkout=$(realpath "${script_dir}"/../../../../)
    299	# By default the script searches for the kernel in the checkout directory but
    300	# it also obeys environment variables O= and KBUILD_OUTPUT=
    301	local kernel_bzimage="${kernel_checkout}/${BZIMAGE}"
    302	local command="${DEFAULT_COMMAND}"
    303	local update_image="no"
    304	local exit_command="poweroff -f"
    305	local debug_shell="no"
    306
    307	while getopts 'hskid:j:' opt; do
    308		case ${opt} in
    309		i)
    310			update_image="yes"
    311			;;
    312		d)
    313			OUTPUT_DIR="$OPTARG"
    314			;;
    315		j)
    316			NUM_COMPILE_JOBS="$OPTARG"
    317			;;
    318		s)
    319			command=""
    320			debug_shell="yes"
    321			exit_command="bash"
    322			;;
    323		h)
    324			usage
    325			exit 0
    326			;;
    327		\? )
    328			echo "Invalid Option: -$OPTARG"
    329			usage
    330			exit 1
    331			;;
    332		: )
    333			echo "Invalid Option: -$OPTARG requires an argument"
    334			usage
    335			exit 1
    336			;;
    337		esac
    338	done
    339	shift $((OPTIND -1))
    340
    341	if [[ $# -eq 0  && "${debug_shell}" == "no" ]]; then
    342		echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
    343	else
    344		command="$@"
    345	fi
    346
    347	local kconfig_file="${OUTPUT_DIR}/latest.config"
    348	local make_command="make -j ${NUM_COMPILE_JOBS} KCONFIG_CONFIG=${kconfig_file}"
    349
    350	# Figure out where the kernel is being built.
    351	# O takes precedence over KBUILD_OUTPUT.
    352	if [[ "${O:=""}" != "" ]]; then
    353		if is_rel_path "${O}"; then
    354			O="$(realpath "${PWD}/${O}")"
    355		fi
    356		kernel_bzimage="${O}/${BZIMAGE}"
    357		make_command="${make_command} O=${O}"
    358	elif [[ "${KBUILD_OUTPUT:=""}" != "" ]]; then
    359		if is_rel_path "${KBUILD_OUTPUT}"; then
    360			KBUILD_OUTPUT="$(realpath "${PWD}/${KBUILD_OUTPUT}")"
    361		fi
    362		kernel_bzimage="${KBUILD_OUTPUT}/${BZIMAGE}"
    363		make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}"
    364	fi
    365
    366	populate_url_map
    367
    368	local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
    369	local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
    370
    371	echo "Output directory: ${OUTPUT_DIR}"
    372
    373	mkdir -p "${OUTPUT_DIR}"
    374	mkdir -p "${mount_dir}"
    375	update_kconfig "${kconfig_file}"
    376
    377	recompile_kernel "${kernel_checkout}" "${make_command}"
    378
    379	if [[ "${update_image}" == "no" && ! -f "${rootfs_img}" ]]; then
    380		echo "rootfs image not found in ${rootfs_img}"
    381		update_image="yes"
    382	fi
    383
    384	if [[ "${update_image}" == "yes" ]]; then
    385		create_vm_image
    386	fi
    387
    388	update_selftests "${kernel_checkout}" "${make_command}"
    389	update_init_script "${command}" "${exit_command}"
    390	run_vm "${kernel_bzimage}"
    391	if [[ "${command}" != "" ]]; then
    392		copy_logs
    393		echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
    394	fi
    395}
    396
    397catch()
    398{
    399	local exit_code=$1
    400	local exit_status_file="${OUTPUT_DIR}/${EXIT_STATUS_FILE}"
    401	# This is just a cleanup and the directory may
    402	# have already been unmounted. So, don't let this
    403	# clobber the error code we intend to return.
    404	unmount_image || true
    405	if [[ -f "${exit_status_file}" ]]; then
    406		exit_code="$(cat ${exit_status_file})"
    407	fi
    408	exit ${exit_code}
    409}
    410
    411trap 'catch "$?"' EXIT
    412
    413main "$@"