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