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 8ARCH="$(uname -m)" 9case "${ARCH}" in 10x86_64) 11 QEMU_BINARY=qemu-system-x86_64 12 BZIMAGE="arch/x86/boot/bzImage" 13 ;; 14*) 15 echo "Unsupported architecture" 16 exit 1 17 ;; 18esac 19SCRIPT_DIR="$(dirname $(realpath $0))" 20OUTPUT_DIR="$SCRIPT_DIR/results" 21KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}") 22B2C_URL="https://gitlab.freedesktop.org/mupuf/boot2container/-/raw/master/vm2c.py" 23NUM_COMPILE_JOBS="$(nproc)" 24LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")" 25LOG_FILE="${LOG_FILE_BASE}.log" 26EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status" 27CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1" 28 29TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}" 30DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" 31 32usage() 33{ 34 cat <<EOF 35Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>] 36 37<command> is the command you would normally run when you are in 38the source kernel direcory. e.g: 39 40 $0 -- ./tools/testing/selftests/hid/hid_bpf 41 42If no command is specified and a debug shell (-s) is not requested, 43"${DEFAULT_COMMAND}" will be run by default. 44 45If you build your kernel using KBUILD_OUTPUT= or O= options, these 46can be passed as environment variables to the script: 47 48 O=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf 49 50or 51 52 KBUILD_OUTPUT=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf 53 54Options: 55 56 -u) Update the boot2container script to a newer version. 57 -d) Update the output directory (default: ${OUTPUT_DIR}) 58 -j) Number of jobs for compilation, similar to -j in make 59 (default: ${NUM_COMPILE_JOBS}) 60 -s) Instead of powering off the VM, start an interactive 61 shell. If <command> is specified, the shell runs after 62 the command finishes executing 63EOF 64} 65 66download() 67{ 68 local file="$1" 69 70 echo "Downloading $file..." >&2 71 curl -Lsf "$file" -o "${@:2}" 72} 73 74recompile_kernel() 75{ 76 local kernel_checkout="$1" 77 local make_command="$2" 78 79 cd "${kernel_checkout}" 80 81 ${make_command} olddefconfig 82 ${make_command} headers 83 ${make_command} 84} 85 86update_selftests() 87{ 88 local kernel_checkout="$1" 89 local selftests_dir="${kernel_checkout}/tools/testing/selftests/hid" 90 91 cd "${selftests_dir}" 92 ${make_command} 93} 94 95run_vm() 96{ 97 local run_dir="$1" 98 local b2c="$2" 99 local kernel_bzimage="$3" 100 local command="$4" 101 local post_command="" 102 103 cd "${run_dir}" 104 105 if ! which "${QEMU_BINARY}" &> /dev/null; then 106 cat <<EOF 107Could not find ${QEMU_BINARY} 108Please install qemu or set the QEMU_BINARY environment variable. 109EOF 110 exit 1 111 fi 112 113 # alpine (used in post-container requires the PATH to have /bin 114 export PATH=$PATH:/bin 115 116 if [[ "${debug_shell}" != "yes" ]] 117 then 118 touch ${OUTPUT_DIR}/${LOG_FILE} 119 command="mount bpffs -t bpf /sys/fs/bpf/; set -o pipefail ; ${command} 2>&1 | tee ${OUTPUT_DIR}/${LOG_FILE}" 120 post_command="cat ${OUTPUT_DIR}/${LOG_FILE}" 121 else 122 command="mount bpffs -t bpf /sys/fs/bpf/; ${command}" 123 fi 124 125 set +e 126 $b2c --command "${command}" \ 127 --kernel ${kernel_bzimage} \ 128 --workdir ${OUTPUT_DIR} \ 129 --image ${CONTAINER_IMAGE} 130 131 echo $? > ${OUTPUT_DIR}/${EXIT_STATUS_FILE} 132 133 set -e 134 135 ${post_command} 136} 137 138is_rel_path() 139{ 140 local path="$1" 141 142 [[ ${path:0:1} != "/" ]] 143} 144 145do_update_kconfig() 146{ 147 local kernel_checkout="$1" 148 local kconfig_file="$2" 149 150 rm -f "$kconfig_file" 2> /dev/null 151 152 for config in "${KCONFIG_REL_PATHS[@]}"; do 153 local kconfig_src="${config}" 154 cat "$kconfig_src" >> "$kconfig_file" 155 done 156} 157 158update_kconfig() 159{ 160 local kernel_checkout="$1" 161 local kconfig_file="$2" 162 163 if [[ -f "${kconfig_file}" ]]; then 164 local local_modified="$(stat -c %Y "${kconfig_file}")" 165 166 for config in "${KCONFIG_REL_PATHS[@]}"; do 167 local kconfig_src="${config}" 168 local src_modified="$(stat -c %Y "${kconfig_src}")" 169 # Only update the config if it has been updated after the 170 # previously cached config was created. This avoids 171 # unnecessarily compiling the kernel and selftests. 172 if [[ "${src_modified}" -gt "${local_modified}" ]]; then 173 do_update_kconfig "$kernel_checkout" "$kconfig_file" 174 # Once we have found one outdated configuration 175 # there is no need to check other ones. 176 break 177 fi 178 done 179 else 180 do_update_kconfig "$kernel_checkout" "$kconfig_file" 181 fi 182} 183 184main() 185{ 186 local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" 187 local kernel_checkout=$(realpath "${script_dir}"/../../../../) 188 # By default the script searches for the kernel in the checkout directory but 189 # it also obeys environment variables O= and KBUILD_OUTPUT= 190 local kernel_bzimage="${kernel_checkout}/${BZIMAGE}" 191 local command="${DEFAULT_COMMAND}" 192 local update_b2c="no" 193 local debug_shell="no" 194 195 while getopts ':hsud:j:' opt; do 196 case ${opt} in 197 u) 198 update_b2c="yes" 199 ;; 200 d) 201 OUTPUT_DIR="$OPTARG" 202 ;; 203 j) 204 NUM_COMPILE_JOBS="$OPTARG" 205 ;; 206 s) 207 command="/bin/sh" 208 debug_shell="yes" 209 ;; 210 h) 211 usage 212 exit 0 213 ;; 214 \? ) 215 echo "Invalid Option: -$OPTARG" 216 usage 217 exit 1 218 ;; 219 : ) 220 echo "Invalid Option: -$OPTARG requires an argument" 221 usage 222 exit 1 223 ;; 224 esac 225 done 226 shift $((OPTIND -1)) 227 228 # trap 'catch "$?"' EXIT 229 230 if [[ "${debug_shell}" == "no" ]]; then 231 if [[ $# -eq 0 ]]; then 232 echo "No command specified, will run ${DEFAULT_COMMAND} in the vm" 233 else 234 command="$@" 235 236 if [[ "${command}" == "/bin/bash" || "${command}" == "bash" ]] 237 then 238 debug_shell="yes" 239 fi 240 fi 241 fi 242 243 local kconfig_file="${OUTPUT_DIR}/latest.config" 244 local make_command="make -j ${NUM_COMPILE_JOBS} KCONFIG_CONFIG=${kconfig_file}" 245 246 # Figure out where the kernel is being built. 247 # O takes precedence over KBUILD_OUTPUT. 248 if [[ "${O:=""}" != "" ]]; then 249 if is_rel_path "${O}"; then 250 O="$(realpath "${PWD}/${O}")" 251 fi 252 kernel_bzimage="${O}/${BZIMAGE}" 253 make_command="${make_command} O=${O}" 254 elif [[ "${KBUILD_OUTPUT:=""}" != "" ]]; then 255 if is_rel_path "${KBUILD_OUTPUT}"; then 256 KBUILD_OUTPUT="$(realpath "${PWD}/${KBUILD_OUTPUT}")" 257 fi 258 kernel_bzimage="${KBUILD_OUTPUT}/${BZIMAGE}" 259 make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}" 260 fi 261 262 local b2c="${OUTPUT_DIR}/vm2c.py" 263 264 echo "Output directory: ${OUTPUT_DIR}" 265 266 mkdir -p "${OUTPUT_DIR}" 267 update_kconfig "${kernel_checkout}" "${kconfig_file}" 268 269 recompile_kernel "${kernel_checkout}" "${make_command}" 270 271 if [[ "${update_b2c}" == "no" && ! -f "${b2c}" ]]; then 272 echo "vm2c script not found in ${b2c}" 273 update_b2c="yes" 274 fi 275 276 if [[ "${update_b2c}" == "yes" ]]; then 277 download $B2C_URL $b2c 278 chmod +x $b2c 279 fi 280 281 update_selftests "${kernel_checkout}" "${make_command}" 282 run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}" 283 if [[ "${debug_shell}" != "yes" ]]; then 284 echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}" 285 fi 286 287 exit $(cat ${OUTPUT_DIR}/${EXIT_STATUS_FILE}) 288} 289 290main "$@" 291