| #!/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/usr/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/usr/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 tcg -accel hvf -accel kvm \ |
| -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 "$@" |