| #!/bin/bash | |
| # SPDX-License-Identifier: GPL-2.0 | |
| set -ue | |
| CROSS_COMPILE="${CROSS_COMPILE:-""}" | |
| test_dir=$(realpath "$(dirname "$0")") | |
| kernel_dir=$(realpath "$test_dir/../../../..") | |
| workspace_dir="" | |
| headers_dir="" | |
| initrd="" | |
| KEEP_WORKSPACE=0 | |
| source "$test_dir/../kselftest/ktap_helpers.sh" | |
| function get_arch_conf() { | |
| local arch=$1 | |
| if [[ "$arch" == "arm64" ]]; then | |
| QEMU_CMD="qemu-system-aarch64 -M virt -cpu max" | |
| KERNEL_IMAGE="Image" | |
| KERNEL_CMDLINE="console=ttyAMA0" | |
| elif [[ "$arch" == "x86" ]]; then | |
| QEMU_CMD="qemu-system-x86_64" | |
| KERNEL_IMAGE="bzImage" | |
| KERNEL_CMDLINE="console=ttyS0" | |
| else | |
| echo "Unsupported architecture: $arch" | |
| exit 1 | |
| fi | |
| } | |
| function usage() { | |
| cat <<EOF | |
| $0 [-d build_dir] [-j jobs] [-t target_arch] [-T test_name] [-w workspace_dir] [-k] [-h] | |
| Options: | |
| -d) path to the kernel build directory (default: .luo_test_build.<arch>) | |
| -j) number of jobs for compilation | |
| -t) run test for target_arch (aarch64, x86_64) | |
| -T) test name to run (default: luo_kexec_simple) | |
| -w) custom workspace directory (default: creates temp dir) | |
| -k) keep workspace directory after successful test | |
| -h) display this help | |
| EOF | |
| } | |
| function cleanup() { | |
| if [ "$KEEP_WORKSPACE" -eq 1 ]; then | |
| echo "# Workspace preserved at: $workspace_dir" | |
| else | |
| rm -fr "$workspace_dir" | |
| fi | |
| ktap_finished | |
| } | |
| function skip() { | |
| local msg=${1:-""} | |
| ktap_test_skip "$msg" | |
| exit "$KSFT_SKIP" | |
| } | |
| function fail() { | |
| local msg=${1:-""} | |
| ktap_test_fail "$msg" | |
| exit "$KSFT_FAIL" | |
| } | |
| function detect_cross_compile() { | |
| local target=$1 | |
| local host=$(uname -m) | |
| [[ "$host" == "arm64" ]] && host="aarch64" | |
| [[ "$target" == "arm64" ]] && target="aarch64" | |
| if [[ "$host" == "$target" ]]; then | |
| CROSS_COMPILE="" | |
| return | |
| fi | |
| if [[ -n "$CROSS_COMPILE" ]]; then | |
| return | |
| fi | |
| local candidate="" | |
| case "$target" in | |
| aarch64) candidate="aarch64-linux-gnu-" ;; | |
| x86_64) candidate="x86_64-linux-gnu-" ;; | |
| *) skip "Auto-detection for target '$target' not supported. Please set CROSS_COMPILE manually." ;; | |
| esac | |
| if command -v "${candidate}gcc" &> /dev/null; then | |
| CROSS_COMPILE="$candidate" | |
| else | |
| skip "Compiler '${candidate}gcc' not found. Please install it (e.g., 'apt install gcc-aarch64-linux-gnu') or set CROSS_COMPILE." | |
| fi | |
| } | |
| function build_kernel() { | |
| local build_dir=$1 | |
| local make_cmd=$2 | |
| local kimage=$3 | |
| local target_arch=$4 | |
| local luo_config="$build_dir/luo.config" | |
| local kconfig="$build_dir/.config" | |
| local common_conf="$test_dir/config" | |
| local arch_conf="$test_dir/config.$target_arch" | |
| echo "# Building kernel in: $build_dir" | |
| cat "$arch_conf" "$common_conf" | tee "$kconfig" > "$luo_config" | |
| $make_cmd olddefconfig | |
| # verify that kernel confiration has all necessary options | |
| while read -r opt ; do | |
| grep "$opt" "$kconfig" &>/dev/null || skip "$opt is missing" | |
| done < "$luo_config" | |
| $make_cmd "$kimage" | |
| $make_cmd headers_install INSTALL_HDR_PATH="$headers_dir" | |
| } | |
| function mkinitrd() { | |
| local build_dir=$1 | |
| local kernel_path=$2 | |
| local test_name=$3 | |
| # Compile the test binary and the init process | |
| "$CROSS_COMPILE"gcc -static -O2 -nostdinc -nostdlib \ | |
| -I "$headers_dir/include" \ | |
| -I "$kernel_dir/tools/include/nolibc" \ | |
| -I "$test_dir/lib/include" \ | |
| -o "$workspace_dir/test_binary" \ | |
| "$test_dir/$test_name.c" "$test_dir/lib/lu_utils.c" | |
| "$CROSS_COMPILE"gcc -s -static -Os -nostdinc -nostdlib \ | |
| -fno-asynchronous-unwind-tables -fno-ident \ | |
| -fno-stack-protector \ | |
| -I "$headers_dir/include" \ | |
| -I "$kernel_dir/tools/include/nolibc" \ | |
| -o "$workspace_dir/init" "$test_dir/init.c" | |
| cat > "$workspace_dir/cpio_list_inner" <<EOF | |
| dir /dev 0755 0 0 | |
| dir /proc 0755 0 0 | |
| dir /debugfs 0755 0 0 | |
| nod /dev/console 0600 0 0 c 5 1 | |
| file /init $workspace_dir/init 0755 0 0 | |
| file /test_binary $workspace_dir/test_binary 0755 0 0 | |
| EOF | |
| # Generate inner_initrd.cpio | |
| "$build_dir/scripts/gen_init_cpio" "$workspace_dir/cpio_list_inner" > "$workspace_dir/inner_initrd.cpio" | |
| cat > "$workspace_dir/cpio_list" <<EOF | |
| dir /dev 0755 0 0 | |
| dir /proc 0755 0 0 | |
| dir /debugfs 0755 0 0 | |
| nod /dev/console 0600 0 0 c 5 1 | |
| file /init $workspace_dir/init 0755 0 0 | |
| file /kernel $kernel_path 0644 0 0 | |
| file /test_binary $workspace_dir/test_binary 0755 0 0 | |
| file /initrd.img $workspace_dir/inner_initrd.cpio 0644 0 0 | |
| EOF | |
| # Generate the final initrd | |
| "$build_dir/scripts/gen_init_cpio" "$workspace_dir/cpio_list" > "$initrd" | |
| } | |
| function run_qemu() { | |
| local qemu_cmd=$1 | |
| local cmdline=$2 | |
| local kernel_path=$3 | |
| local serial="$workspace_dir/qemu.serial" | |
| cmdline="$cmdline liveupdate=on panic=-1" | |
| echo "# Serial Log: $serial" | |
| timeout 30s \ | |
| $qemu_cmd -m 1G -smp 2 -no-reboot -nographic -nodefaults \ | |
| -accel kvm -accel hvf -accel tcg \ | |
| -serial file:"$serial" \ | |
| -append "$cmdline" \ | |
| -kernel "$kernel_path" \ | |
| -initrd "$initrd" | |
| grep "TEST PASSED" "$serial" &> /dev/null || fail "Liveupdate failed" | |
| } | |
| function target_to_arch() { | |
| local target=$1 | |
| case $target in | |
| aarch64) echo "arm64" ;; | |
| x86_64) echo "x86" ;; | |
| *) skip "architecture $target is not supported" | |
| esac | |
| } | |
| function main() { | |
| local build_dir="" | |
| local jobs=$(nproc) | |
| local target="$(uname -m)" | |
| local test_name="luo_kexec_simple" | |
| local workspace_arg="" | |
| set -o errtrace | |
| trap fail ERR | |
| while getopts 'hd:j:t:T:w:k' opt; do | |
| case $opt in | |
| d) build_dir="$OPTARG" ;; | |
| j) jobs="$OPTARG" ;; | |
| t) target="$OPTARG" ;; | |
| T) test_name="$OPTARG" ;; | |
| w) workspace_arg="$OPTARG" ;; | |
| k) KEEP_WORKSPACE=1 ;; | |
| h) usage; exit 0 ;; | |
| *) echo "Unknown argument $opt"; usage; exit 1 ;; | |
| esac | |
| done | |
| ktap_print_header | |
| ktap_set_plan 1 | |
| trap cleanup EXIT | |
| if [ -n "$workspace_arg" ]; then | |
| workspace_dir="$(realpath -m "$workspace_arg")" | |
| mkdir -p "$workspace_dir" | |
| else | |
| workspace_dir=$(mktemp -d /tmp/luo-test.XXXXXXXX) | |
| fi | |
| echo "# Workspace created at: $workspace_dir" | |
| headers_dir="$workspace_dir/usr" | |
| initrd="$workspace_dir/initrd.cpio" | |
| detect_cross_compile "$target" | |
| local arch=$(target_to_arch "$target") | |
| if [ -z "$build_dir" ]; then | |
| build_dir="$kernel_dir/.luo_test_build.$arch" | |
| fi | |
| mkdir -p "$build_dir" | |
| build_dir=$(realpath "$build_dir") | |
| get_arch_conf "$arch" | |
| local make_cmd="make -s ARCH=$arch CROSS_COMPILE=$CROSS_COMPILE -j$jobs" | |
| local make_cmd_build="$make_cmd -C $kernel_dir O=$build_dir" | |
| build_kernel "$build_dir" "$make_cmd_build" "$KERNEL_IMAGE" "$target" | |
| local final_kernel="$build_dir/arch/$arch/boot/$KERNEL_IMAGE" | |
| mkinitrd "$build_dir" "$final_kernel" "$test_name" | |
| run_qemu "$QEMU_CMD" "$KERNEL_CMDLINE" "$final_kernel" | |
| ktap_test_pass "$test_name succeeded" | |
| } | |
| main "$@" |