Extend tools/testing/selftests/kho/vmtest.sh and init.c to run a second
QEMU test verifying read-only KHO ('kho_ro=<fdt_phys>') after the
standard KHO test on both x86_64 and aarch64:
- Build a second kernel configured with CONFIG_KEXEC_HANDOVER_READONLY=y
(and CONFIG_KEXEC_HANDOVER=n).
- Pass KHO_RO_CMDLINE ('nokaslr kho_bootmem=64M,64M,64M' on x86_64,
'cma=116M@0x40c00000' on aarch64) to the first kernel in the read-only
test run so KHO allocations are placed in predictable physical ranges.
- When booted with 'kho_ro_test', read /debugfs/kho/out/fdt_phys in
init.c and kexec into the read-only kernel with 'kho_ro=<fdt_phys>'
and architecture-specific memory parameters
('memmap=792M$16M memmap=24M$1000M' on x86_64,
'mem=128M cma=12M@0x40000000' on aarch64) so the preserved pages are
excluded from the boot memory map and mapped via kho_ro_map_folio().
- Verify 'KHO restore succeeded' from test_kho in read-only mode.
Signed-off-by: Pasha Tatashin <[email protected]>
---
tools/testing/selftests/kho/arm64.conf | 3 +
tools/testing/selftests/kho/init.c | 38 ++++++++-
tools/testing/selftests/kho/vmtest.sh | 103 +++++++++++++++++--------
tools/testing/selftests/kho/x86.conf | 1 +
4 files changed, 112 insertions(+), 33 deletions(-)
diff --git a/tools/testing/selftests/kho/arm64.conf
b/tools/testing/selftests/kho/arm64.conf
index ee696807cd35..36dd8c46060c 100644
--- a/tools/testing/selftests/kho/arm64.conf
+++ b/tools/testing/selftests/kho/arm64.conf
@@ -4,6 +4,9 @@ CONFIG_SERIAL_AMBA_PL010=y
CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_CMA=y
+CONFIG_DMA_CMA=y
"
KERNEL_IMAGE="Image"
KERNEL_CMDLINE="console=ttyAMA0"
+KHO_RO_CMDLINE="cma=116M@0x40c00000"
diff --git a/tools/testing/selftests/kho/init.c
b/tools/testing/selftests/kho/init.c
index 88a41b6eba95..272338f186be 100644
--- a/tools/testing/selftests/kho/init.c
+++ b/tools/testing/selftests/kho/init.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
@@ -12,6 +13,15 @@
#define COMMAND_LINE_SIZE 2048
#define KERNEL_IMAGE "/kernel"
+#define FDT_PHYS_PATH "/debugfs/kho/out/fdt_phys"
+
+#if defined(__x86_64__)
+#define KHO_RO_MEM_PARAM "memmap=792M$16M memmap=24M$1000M"
+#elif defined(__aarch64__)
+#define KHO_RO_MEM_PARAM "mem=128M cma=12M@0x40000000"
+#else
+#define KHO_RO_MEM_PARAM ""
+#endif
static int mount_filesystems(void)
{
@@ -32,7 +42,8 @@ static long kexec_file_load(int kernel_fd, int initrd_fd,
static int kexec_load(void)
{
char cmdline[COMMAND_LINE_SIZE];
- ssize_t len;
+ ssize_t len, fdt_len;
+ char fdt_phys[64];
int fd, err;
fd = open("/proc/cmdline", O_RDONLY);
@@ -41,11 +52,34 @@ static int kexec_load(void)
len = read(fd, cmdline, sizeof(cmdline));
close(fd);
- if (len < 0)
+ if (len <= 0)
return -1;
/* replace \n with \0 */
cmdline[len - 1] = 0;
+
+ if (strstr(cmdline, "kho_ro_test")) {
+ fd = open(FDT_PHYS_PATH, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ fdt_len = read(fd, fdt_phys, sizeof(fdt_phys) - 1);
+ close(fd);
+ if (fdt_len <= 0)
+ return -1;
+
+ if (fdt_phys[fdt_len - 1] == '\n')
+ fdt_len--;
+ fdt_phys[fdt_len] = 0;
+
+ len = (len - 1) + snprintf(cmdline + len - 1,
+ sizeof(cmdline) - (len - 1),
+ " kho_ro=%s " KHO_RO_MEM_PARAM,
+ fdt_phys) + 1;
+ if (len <= 0 || (size_t)len > sizeof(cmdline))
+ return -1;
+ }
+
fd = open(KERNEL_IMAGE, O_RDONLY);
if (fd < 0)
return -1;
diff --git a/tools/testing/selftests/kho/vmtest.sh
b/tools/testing/selftests/kho/vmtest.sh
index 0014bd76e88d..b3659e4fd1c9 100755
--- a/tools/testing/selftests/kho/vmtest.sh
+++ b/tools/testing/selftests/kho/vmtest.sh
@@ -35,32 +35,25 @@ trap cleanup EXIT
function skip() {
local msg=${1:-""}
- ktap_test_skip "$msg"
+ while [[ "$KTAP_TESTNO" -le "$KSFT_NUM_TESTS" ]]; do
+ ktap_test_skip "$msg"
+ done
exit "$KSFT_SKIP"
}
-function fail() {
- local msg=${1:-""}
-
- ktap_test_fail "$msg"
- exit "$KSFT_FAIL"
-}
-
function build_kernel() {
local build_dir=$1
local make_cmd=$2
local arch_kconfig=$3
local kimage=$4
+ local kho_mode=${5:-"rw"}
local kho_config="$tmp_dir/kho.config"
local kconfig="$build_dir/.config"
# enable initrd, KHO and KHO test in kernel configuration
- tee "$kconfig" > "$kho_config" <<EOF
+ cat > "$kho_config" <<EOF
CONFIG_BLK_DEV_INITRD=y
-CONFIG_KEXEC_HANDOVER=y
-CONFIG_KEXEC_HANDOVER_DEBUG=y
-CONFIG_KEXEC_HANDOVER_DEBUGFS=y
CONFIG_TEST_KEXEC_HANDOVER=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_VM=y
@@ -70,20 +63,50 @@ CONFIG_DEFERRED_STRUCT_PAGE_INIT=y
$arch_kconfig
EOF
+ if [[ "$kho_mode" == "ro" ]]; then
+ cat >> "$kho_config" <<EOF
+# CONFIG_KEXEC_HANDOVER is not set
+CONFIG_KEXEC_HANDOVER_READONLY=y
+EOF
+ else
+ cat >> "$kho_config" <<EOF
+CONFIG_KEXEC_HANDOVER=y
+CONFIG_KEXEC_HANDOVER_DEBUG=y
+CONFIG_KEXEC_HANDOVER_DEBUGFS=y
+EOF
+ fi
+
make_cmd="$make_cmd -C $kernel_dir O=$build_dir"
- $make_cmd olddefconfig
+ local need_config=0
+ if [[ ! -f "$kconfig" ]]; then
+ need_config=1
+ else
+ while read -r opt ; do
+ [[ -z "$opt" ]] && continue
+ grep -q "^$opt$" "$kconfig" || need_config=1
+ done < "$kho_config"
+ fi
+
+ if [[ "$need_config" -eq 1 ]]; then
+ cp "$kho_config" "$kconfig"
+ $make_cmd olddefconfig
+ fi
# verify that kernel confiration has all necessary options
while read -r opt ; do
- grep "$opt" "$kconfig" &>/dev/null || skip "$opt is missing"
+ [[ -z "$opt" ]] && continue
+ grep -q "^$opt$" "$kconfig" || skip "$opt is missing"
done < "$kho_config"
$make_cmd "$kimage"
- $make_cmd headers_install INSTALL_HDR_PATH="$headers_dir"
+ if [[ "$kho_mode" != "ro" ]]; then
+ $make_cmd headers_install INSTALL_HDR_PATH="$headers_dir"
+ fi
}
function mkinitrd() {
local kernel=$1
+ local out_initrd=$2
"$CROSS_COMPILE"gcc -s -static -Os -nostdinc -nostdlib \
-fno-asynchronous-unwind-tables -fno-ident \
@@ -100,25 +123,31 @@ file /init $tmp_dir/init 0755 0 0
file /kernel $kernel 0644 0 0
EOF
- "$build_dir/usr/gen_init_cpio" "$tmp_dir/cpio_list" > "$initrd"
+ "$build_dir/usr/gen_init_cpio" "$tmp_dir/cpio_list" > "$out_initrd"
}
function run_qemu() {
local qemu_cmd=$1
local cmdline=$2
local kernel=$3
+ local initrd_path=$4
+ local test_name=$5
local serial="$tmp_dir/qemu.serial"
cmdline="$cmdline kho=on panic=-1"
- $qemu_cmd -m 1G -smp 2 -no-reboot -nographic -nodefaults \
- -accel kvm -accel hvf -accel tcg \
- -serial file:"$serial" \
- -append "$cmdline" \
- -kernel "$kernel" \
- -initrd "$initrd"
-
- grep "KHO restore succeeded" "$serial" &> /dev/null || fail "KHO failed"
+ rm -f "$serial"
+ if $qemu_cmd -m 1G -smp 2 -no-reboot -nographic -nodefaults \
+ -accel kvm -accel hvf -accel tcg \
+ -serial file:"$serial" \
+ -append "$cmdline" \
+ -kernel "$kernel" \
+ -initrd "$initrd_path" && \
+ grep "KHO restore succeeded" "$serial" &> /dev/null; then
+ ktap_test_pass "$test_name succeeded"
+ else
+ ktap_test_fail "$test_name failed"
+ fi
}
function target_to_arch() {
@@ -164,7 +193,7 @@ function main() {
done
ktap_print_header
- ktap_set_plan 1
+ ktap_set_plan 2
if [[ "$target" != "$(uname -m)" ]] && [[ -z "$CROSS_COMPILE" ]]; then
skip "Cross-platform testing needs to specify CROSS_COMPILE"
@@ -174,17 +203,29 @@ function main() {
local arch=$(target_to_arch "$target")
source "$test_dir/$arch.conf"
- # build the kernel and create initrd
+ # build the kernels and create initrds
# initrd includes the kernel image that will be kexec'ed
local make_cmd="make ARCH=$arch CROSS_COMPILE=$CROSS_COMPILE -j$jobs"
- build_kernel "$build_dir" "$make_cmd" "$QEMU_KCONFIG" "$KERNEL_IMAGE"
+ build_kernel "$build_dir" "$make_cmd" "$QEMU_KCONFIG" "$KERNEL_IMAGE"
"rw"
- local kernel="$build_dir/arch/$arch/boot/$KERNEL_IMAGE"
- mkinitrd "$kernel"
+ local kernel="$tmp_dir/$KERNEL_IMAGE"
+ cp "$build_dir/arch/$arch/boot/$KERNEL_IMAGE" "$kernel"
+ mkinitrd "$kernel" "$initrd"
+
+ local build_dir_ro="${build_dir}-ro"
+ if [[ ! -d "$build_dir_ro" ]]; then
+ cp -a "$build_dir" "$build_dir_ro"
+ fi
+ build_kernel "$build_dir_ro" "$make_cmd" "$QEMU_KCONFIG"
"$KERNEL_IMAGE" "ro"
+ local kernel_ro="$build_dir_ro/arch/$arch/boot/$KERNEL_IMAGE"
+ local initrd_ro="$tmp_dir/initrd-ro.cpio"
+ mkinitrd "$kernel_ro" "$initrd_ro"
- run_qemu "$QEMU_CMD" "$KERNEL_CMDLINE" "$kernel"
+ trap - ERR
- ktap_test_pass "KHO succeeded"
+ run_qemu "$QEMU_CMD" "$KERNEL_CMDLINE" "$kernel" "$initrd" "KHO"
+ run_qemu "$QEMU_CMD" "$KERNEL_CMDLINE $KHO_RO_CMDLINE kho_ro_test" \
+ "$kernel" "$initrd_ro" "KHO read-only"
}
main "$@"
diff --git a/tools/testing/selftests/kho/x86.conf
b/tools/testing/selftests/kho/x86.conf
index b419e610ca22..2c097c5bd6c6 100644
--- a/tools/testing/selftests/kho/x86.conf
+++ b/tools/testing/selftests/kho/x86.conf
@@ -5,3 +5,4 @@ CONFIG_SERIAL_8250_CONSOLE=y
"
KERNEL_IMAGE="bzImage"
KERNEL_CMDLINE="console=ttyS0"
+KHO_RO_CMDLINE="nokaslr kho_bootmem=64M,64M,64M"
--
2.56.0.176.g3575e65682-goog