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