Linux Audio

Check our new training course

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