Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kho: add test for kexec handover

Testing kexec handover requires a kernel driver that will generate some
data and preserve it with KHO on the first boot and then restore that data
and verify it was preserved properly after kexec.

To facilitate such test, along with the kernel driver responsible for data
generation, preservation and restoration add a script that runs a kernel
in a VM with a minimal /init. The /init enables KHO, loads a kernel image
for kexec and runs kexec reboot. After the boot of the kexeced kernel,
the driver verifies that the data was properly preserved.

[rppt@kernel.org: fix section mismatch]
Link: https://lkml.kernel.org/r/aIiRC8fXiOXKbPM_@kernel.org
Link: https://lkml.kernel.org/r/20250727083733.2590139-1-rppt@kernel.org
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Cc: Alexander Graf <graf@amazon.com>
Cc: Changyuan Lyu <changyuanl@google.com>
Cc: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: Pratyush Yadav <pratyush@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Mike Rapoport (Microsoft) and committed by
Andrew Morton
b753522b d92dccd0

+627
+1
MAINTAINERS
··· 13353 13353 F: Documentation/core-api/kho/* 13354 13354 F: include/linux/kexec_handover.h 13355 13355 F: kernel/kexec_handover.c 13356 + F: tools/testing/selftests/kho/ 13356 13357 13357 13358 KEYS-ENCRYPTED 13358 13359 M: Mimi Zohar <zohar@linux.ibm.com>
+21
lib/Kconfig.debug
··· 3225 3225 3226 3226 If unsure, say N. 3227 3227 3228 + config TEST_KEXEC_HANDOVER 3229 + bool "Test for Kexec HandOver" 3230 + default n 3231 + depends on KEXEC_HANDOVER 3232 + help 3233 + This option enables test for Kexec HandOver (KHO). 3234 + The test consists of two parts: saving kernel data before kexec and 3235 + restoring the data after kexec and verifying that it was properly 3236 + handed over. This test module creates and saves data on the boot of 3237 + the first kernel and restores and verifies the data on the boot of 3238 + kexec'ed kernel. 3239 + 3240 + For detailed documentation about KHO, see Documentation/core-api/kho. 3241 + 3242 + To run the test run: 3243 + 3244 + tools/testing/selftests/kho/vmtest.sh -h 3245 + 3246 + If unsure, say N. 3247 + 3248 + 3228 3249 config INT_POW_KUNIT_TEST 3229 3250 tristate "Integer exponentiation (int_pow) test" if !KUNIT_ALL_TESTS 3230 3251 depends on KUNIT
+1
lib/Makefile
··· 102 102 obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o 103 103 obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o 104 104 obj-$(CONFIG_TEST_OBJPOOL) += test_objpool.o 105 + obj-$(CONFIG_TEST_KEXEC_HANDOVER) += test_kho.o 105 106 106 107 obj-$(CONFIG_TEST_FPU) += test_fpu.o 107 108 test_fpu-y := test_fpu_glue.o test_fpu_impl.o
+305
lib/test_kho.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Test module for KHO 4 + * Copyright (c) 2025 Microsoft Corporation. 5 + * 6 + * Authors: 7 + * Saurabh Sengar <ssengar@microsoft.com> 8 + * Mike Rapoport <rppt@kernel.org> 9 + */ 10 + 11 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 + 13 + #include <linux/mm.h> 14 + #include <linux/gfp.h> 15 + #include <linux/slab.h> 16 + #include <linux/kexec.h> 17 + #include <linux/libfdt.h> 18 + #include <linux/module.h> 19 + #include <linux/printk.h> 20 + #include <linux/vmalloc.h> 21 + #include <linux/kexec_handover.h> 22 + 23 + #include <net/checksum.h> 24 + 25 + #define KHO_TEST_MAGIC 0x4b484f21 /* KHO! */ 26 + #define KHO_TEST_FDT "kho_test" 27 + #define KHO_TEST_COMPAT "kho-test-v1" 28 + 29 + static long max_mem = (PAGE_SIZE << MAX_PAGE_ORDER) * 2; 30 + module_param(max_mem, long, 0644); 31 + 32 + struct kho_test_state { 33 + unsigned int nr_folios; 34 + struct folio **folios; 35 + struct folio *fdt; 36 + __wsum csum; 37 + }; 38 + 39 + static struct kho_test_state kho_test_state; 40 + 41 + static int kho_test_notifier(struct notifier_block *self, unsigned long cmd, 42 + void *v) 43 + { 44 + struct kho_test_state *state = &kho_test_state; 45 + struct kho_serialization *ser = v; 46 + int err = 0; 47 + 48 + switch (cmd) { 49 + case KEXEC_KHO_ABORT: 50 + return NOTIFY_DONE; 51 + case KEXEC_KHO_FINALIZE: 52 + /* Handled below */ 53 + break; 54 + default: 55 + return NOTIFY_BAD; 56 + } 57 + 58 + err |= kho_preserve_folio(state->fdt); 59 + err |= kho_add_subtree(ser, KHO_TEST_FDT, folio_address(state->fdt)); 60 + 61 + return err ? NOTIFY_BAD : NOTIFY_DONE; 62 + } 63 + 64 + static struct notifier_block kho_test_nb = { 65 + .notifier_call = kho_test_notifier, 66 + }; 67 + 68 + static int kho_test_save_data(struct kho_test_state *state, void *fdt) 69 + { 70 + phys_addr_t *folios_info __free(kvfree) = NULL; 71 + int err = 0; 72 + 73 + folios_info = kvmalloc_array(state->nr_folios, sizeof(*folios_info), 74 + GFP_KERNEL); 75 + if (!folios_info) 76 + return -ENOMEM; 77 + 78 + for (int i = 0; i < state->nr_folios; i++) { 79 + struct folio *folio = state->folios[i]; 80 + unsigned int order = folio_order(folio); 81 + 82 + folios_info[i] = virt_to_phys(folio_address(folio)) | order; 83 + 84 + err = kho_preserve_folio(folio); 85 + if (err) 86 + return err; 87 + } 88 + 89 + err |= fdt_begin_node(fdt, "data"); 90 + err |= fdt_property(fdt, "nr_folios", &state->nr_folios, 91 + sizeof(state->nr_folios)); 92 + err |= fdt_property(fdt, "folios_info", folios_info, 93 + state->nr_folios * sizeof(*folios_info)); 94 + err |= fdt_property(fdt, "csum", &state->csum, sizeof(state->csum)); 95 + err |= fdt_end_node(fdt); 96 + 97 + return err; 98 + } 99 + 100 + static int kho_test_prepare_fdt(struct kho_test_state *state) 101 + { 102 + const char compatible[] = KHO_TEST_COMPAT; 103 + unsigned int magic = KHO_TEST_MAGIC; 104 + ssize_t fdt_size; 105 + int err = 0; 106 + void *fdt; 107 + 108 + fdt_size = state->nr_folios * sizeof(phys_addr_t) + PAGE_SIZE; 109 + state->fdt = folio_alloc(GFP_KERNEL, get_order(fdt_size)); 110 + if (!state->fdt) 111 + return -ENOMEM; 112 + 113 + fdt = folio_address(state->fdt); 114 + 115 + err |= fdt_create(fdt, fdt_size); 116 + err |= fdt_finish_reservemap(fdt); 117 + 118 + err |= fdt_begin_node(fdt, ""); 119 + err |= fdt_property(fdt, "compatible", compatible, sizeof(compatible)); 120 + err |= fdt_property(fdt, "magic", &magic, sizeof(magic)); 121 + err |= kho_test_save_data(state, fdt); 122 + err |= fdt_end_node(fdt); 123 + 124 + err |= fdt_finish(fdt); 125 + 126 + if (err) 127 + folio_put(state->fdt); 128 + 129 + return err; 130 + } 131 + 132 + static int kho_test_generate_data(struct kho_test_state *state) 133 + { 134 + size_t alloc_size = 0; 135 + __wsum csum = 0; 136 + 137 + while (alloc_size < max_mem) { 138 + int order = get_random_u32() % NR_PAGE_ORDERS; 139 + struct folio *folio; 140 + unsigned int size; 141 + void *addr; 142 + 143 + /* cap allocation so that we won't exceed max_mem */ 144 + if (alloc_size + (PAGE_SIZE << order) > max_mem) { 145 + order = get_order(max_mem - alloc_size); 146 + if (order) 147 + order--; 148 + } 149 + size = PAGE_SIZE << order; 150 + 151 + folio = folio_alloc(GFP_KERNEL | __GFP_NORETRY, order); 152 + if (!folio) 153 + goto err_free_folios; 154 + 155 + state->folios[state->nr_folios++] = folio; 156 + addr = folio_address(folio); 157 + get_random_bytes(addr, size); 158 + csum = csum_partial(addr, size, csum); 159 + alloc_size += size; 160 + } 161 + 162 + state->csum = csum; 163 + return 0; 164 + 165 + err_free_folios: 166 + for (int i = 0; i < state->nr_folios; i++) 167 + folio_put(state->folios[i]); 168 + return -ENOMEM; 169 + } 170 + 171 + static int kho_test_save(void) 172 + { 173 + struct kho_test_state *state = &kho_test_state; 174 + struct folio **folios __free(kvfree) = NULL; 175 + unsigned long max_nr; 176 + int err; 177 + 178 + max_mem = PAGE_ALIGN(max_mem); 179 + max_nr = max_mem >> PAGE_SHIFT; 180 + 181 + folios = kvmalloc_array(max_nr, sizeof(*state->folios), GFP_KERNEL); 182 + if (!folios) 183 + return -ENOMEM; 184 + state->folios = folios; 185 + 186 + err = kho_test_generate_data(state); 187 + if (err) 188 + return err; 189 + 190 + err = kho_test_prepare_fdt(state); 191 + if (err) 192 + return err; 193 + 194 + return register_kho_notifier(&kho_test_nb); 195 + } 196 + 197 + static int kho_test_restore_data(const void *fdt, int node) 198 + { 199 + const unsigned int *nr_folios; 200 + const phys_addr_t *folios_info; 201 + const __wsum *old_csum; 202 + __wsum csum = 0; 203 + int len; 204 + 205 + node = fdt_path_offset(fdt, "/data"); 206 + 207 + nr_folios = fdt_getprop(fdt, node, "nr_folios", &len); 208 + if (!nr_folios || len != sizeof(*nr_folios)) 209 + return -EINVAL; 210 + 211 + old_csum = fdt_getprop(fdt, node, "csum", &len); 212 + if (!old_csum || len != sizeof(*old_csum)) 213 + return -EINVAL; 214 + 215 + folios_info = fdt_getprop(fdt, node, "folios_info", &len); 216 + if (!folios_info || len != sizeof(*folios_info) * *nr_folios) 217 + return -EINVAL; 218 + 219 + for (int i = 0; i < *nr_folios; i++) { 220 + unsigned int order = folios_info[i] & ~PAGE_MASK; 221 + phys_addr_t phys = folios_info[i] & PAGE_MASK; 222 + unsigned int size = PAGE_SIZE << order; 223 + struct folio *folio; 224 + 225 + folio = kho_restore_folio(phys); 226 + if (!folio) 227 + break; 228 + 229 + if (folio_order(folio) != order) 230 + break; 231 + 232 + csum = csum_partial(folio_address(folio), size, csum); 233 + folio_put(folio); 234 + } 235 + 236 + if (csum != *old_csum) 237 + return -EINVAL; 238 + 239 + return 0; 240 + } 241 + 242 + static int kho_test_restore(phys_addr_t fdt_phys) 243 + { 244 + void *fdt = phys_to_virt(fdt_phys); 245 + const unsigned int *magic; 246 + int node, len, err; 247 + 248 + node = fdt_path_offset(fdt, "/"); 249 + if (node < 0) 250 + return -EINVAL; 251 + 252 + if (fdt_node_check_compatible(fdt, node, KHO_TEST_COMPAT)) 253 + return -EINVAL; 254 + 255 + magic = fdt_getprop(fdt, node, "magic", &len); 256 + if (!magic || len != sizeof(*magic)) 257 + return -EINVAL; 258 + 259 + if (*magic != KHO_TEST_MAGIC) 260 + return -EINVAL; 261 + 262 + err = kho_test_restore_data(fdt, node); 263 + if (err) 264 + return err; 265 + 266 + pr_info("KHO restore succeeded\n"); 267 + return 0; 268 + } 269 + 270 + static int __init kho_test_init(void) 271 + { 272 + phys_addr_t fdt_phys; 273 + int err; 274 + 275 + err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys); 276 + if (!err) 277 + return kho_test_restore(fdt_phys); 278 + 279 + if (err != -ENOENT) { 280 + pr_warn("failed to retrieve %s FDT: %d\n", KHO_TEST_FDT, err); 281 + return err; 282 + } 283 + 284 + return kho_test_save(); 285 + } 286 + module_init(kho_test_init); 287 + 288 + static void kho_test_cleanup(void) 289 + { 290 + for (int i = 0; i < kho_test_state.nr_folios; i++) 291 + folio_put(kho_test_state.folios[i]); 292 + 293 + kvfree(kho_test_state.folios); 294 + } 295 + 296 + static void __exit kho_test_exit(void) 297 + { 298 + unregister_kho_notifier(&kho_test_nb); 299 + kho_test_cleanup(); 300 + } 301 + module_exit(kho_test_exit); 302 + 303 + MODULE_AUTHOR("Mike Rapoport <rppt@kernel.org>"); 304 + MODULE_DESCRIPTION("KHO test module"); 305 + MODULE_LICENSE("GPL");
+9
tools/testing/selftests/kho/arm64.conf
··· 1 + QEMU_CMD="qemu-system-aarch64 -M virt -cpu max" 2 + QEMU_KCONFIG=" 3 + CONFIG_SERIAL_AMBA_PL010=y 4 + CONFIG_SERIAL_AMBA_PL010_CONSOLE=y 5 + CONFIG_SERIAL_AMBA_PL011=y 6 + CONFIG_SERIAL_AMBA_PL011_CONSOLE=y 7 + " 8 + KERNEL_IMAGE="Image" 9 + KERNEL_CMDLINE="console=ttyAMA0"
+100
tools/testing/selftests/kho/init.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #ifndef NOLIBC 4 + #include <errno.h> 5 + #include <stdio.h> 6 + #include <unistd.h> 7 + #include <fcntl.h> 8 + #include <syscall.h> 9 + #include <sys/mount.h> 10 + #include <sys/reboot.h> 11 + #endif 12 + 13 + /* from arch/x86/include/asm/setup.h */ 14 + #define COMMAND_LINE_SIZE 2048 15 + 16 + /* from include/linux/kexex.h */ 17 + #define KEXEC_FILE_NO_INITRAMFS 0x00000004 18 + 19 + #define KHO_FINILIZE "/debugfs/kho/out/finalize" 20 + #define KERNEL_IMAGE "/kernel" 21 + 22 + static int mount_filesystems(void) 23 + { 24 + if (mount("debugfs", "/debugfs", "debugfs", 0, NULL) < 0) 25 + return -1; 26 + 27 + return mount("proc", "/proc", "proc", 0, NULL); 28 + } 29 + 30 + static int kho_enable(void) 31 + { 32 + const char enable[] = "1"; 33 + int fd; 34 + 35 + fd = open(KHO_FINILIZE, O_RDWR); 36 + if (fd < 0) 37 + return -1; 38 + 39 + if (write(fd, enable, sizeof(enable)) != sizeof(enable)) 40 + return 1; 41 + 42 + close(fd); 43 + return 0; 44 + } 45 + 46 + static long kexec_file_load(int kernel_fd, int initrd_fd, 47 + unsigned long cmdline_len, const char *cmdline, 48 + unsigned long flags) 49 + { 50 + return syscall(__NR_kexec_file_load, kernel_fd, initrd_fd, cmdline_len, 51 + cmdline, flags); 52 + } 53 + 54 + static int kexec_load(void) 55 + { 56 + char cmdline[COMMAND_LINE_SIZE]; 57 + ssize_t len; 58 + int fd, err; 59 + 60 + fd = open("/proc/cmdline", O_RDONLY); 61 + if (fd < 0) 62 + return -1; 63 + 64 + len = read(fd, cmdline, sizeof(cmdline)); 65 + close(fd); 66 + if (len < 0) 67 + return -1; 68 + 69 + /* replace \n with \0 */ 70 + cmdline[len - 1] = 0; 71 + fd = open(KERNEL_IMAGE, O_RDONLY); 72 + if (fd < 0) 73 + return -1; 74 + 75 + err = kexec_file_load(fd, -1, len, cmdline, KEXEC_FILE_NO_INITRAMFS); 76 + close(fd); 77 + 78 + return err ? : 0; 79 + } 80 + 81 + int main(int argc, char *argv[]) 82 + { 83 + if (mount_filesystems()) 84 + goto err_reboot; 85 + 86 + if (kho_enable()) 87 + goto err_reboot; 88 + 89 + if (kexec_load()) 90 + goto err_reboot; 91 + 92 + if (reboot(RB_KEXEC)) 93 + goto err_reboot; 94 + 95 + return 0; 96 + 97 + err_reboot: 98 + reboot(RB_AUTOBOOT); 99 + return -1; 100 + }
+183
tools/testing/selftests/kho/vmtest.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + set -ue 5 + 6 + CROSS_COMPILE="${CROSS_COMPILE:-""}" 7 + 8 + test_dir=$(realpath "$(dirname "$0")") 9 + kernel_dir=$(realpath "$test_dir/../../../..") 10 + 11 + tmp_dir=$(mktemp -d /tmp/kho-test.XXXXXXXX) 12 + headers_dir="$tmp_dir/usr" 13 + initrd_dir="$tmp_dir/initrd" 14 + initrd="$tmp_dir/initrd.cpio" 15 + 16 + source "$test_dir/../kselftest/ktap_helpers.sh" 17 + 18 + function usage() { 19 + cat <<EOF 20 + $0 [-d build_dir] [-j jobs] [-t target_arch] [-h] 21 + Options: 22 + -d) path to the kernel build directory 23 + -j) number of jobs for compilation, similar to -j in make 24 + -t) run test for target_arch, requires CROSS_COMPILE set 25 + supported targets: aarch64, x86_64 26 + -h) display this help 27 + EOF 28 + } 29 + 30 + function cleanup() { 31 + rm -fr "$tmp_dir" 32 + ktap_finished 33 + } 34 + trap cleanup EXIT 35 + 36 + function skip() { 37 + local msg=${1:-""} 38 + 39 + ktap_test_skip "$msg" 40 + exit "$KSFT_SKIP" 41 + } 42 + 43 + function fail() { 44 + local msg=${1:-""} 45 + 46 + ktap_test_fail "$msg" 47 + exit "$KSFT_FAIL" 48 + } 49 + 50 + function build_kernel() { 51 + local build_dir=$1 52 + local make_cmd=$2 53 + local arch_kconfig=$3 54 + local kimage=$4 55 + 56 + local kho_config="$tmp_dir/kho.config" 57 + local kconfig="$build_dir/.config" 58 + 59 + # enable initrd, KHO and KHO test in kernel configuration 60 + tee "$kconfig" > "$kho_config" <<EOF 61 + CONFIG_BLK_DEV_INITRD=y 62 + CONFIG_KEXEC_HANDOVER=y 63 + CONFIG_TEST_KEXEC_HANDOVER=y 64 + CONFIG_DEBUG_KERNEL=y 65 + CONFIG_DEBUG_VM=y 66 + $arch_kconfig 67 + EOF 68 + 69 + make_cmd="$make_cmd -C $kernel_dir O=$build_dir" 70 + $make_cmd olddefconfig 71 + 72 + # verify that kernel confiration has all necessary options 73 + while read -r opt ; do 74 + grep "$opt" "$kconfig" &>/dev/null || skip "$opt is missing" 75 + done < "$kho_config" 76 + 77 + $make_cmd "$kimage" 78 + $make_cmd headers_install INSTALL_HDR_PATH="$headers_dir" 79 + } 80 + 81 + function mkinitrd() { 82 + local kernel=$1 83 + 84 + mkdir -p "$initrd_dir"/{dev,debugfs,proc} 85 + sudo mknod "$initrd_dir/dev/console" c 5 1 86 + 87 + "$CROSS_COMPILE"gcc -s -static -Os -nostdinc -I"$headers_dir/include" \ 88 + -fno-asynchronous-unwind-tables -fno-ident -nostdlib \ 89 + -include "$test_dir/../../../include/nolibc/nolibc.h" \ 90 + -o "$initrd_dir/init" "$test_dir/init.c" \ 91 + 92 + cp "$kernel" "$initrd_dir/kernel" 93 + 94 + pushd "$initrd_dir" &>/dev/null 95 + find . | cpio -H newc --create > "$initrd" 2>/dev/null 96 + popd &>/dev/null 97 + } 98 + 99 + function run_qemu() { 100 + local qemu_cmd=$1 101 + local cmdline=$2 102 + local kernel=$3 103 + local serial="$tmp_dir/qemu.serial" 104 + 105 + cmdline="$cmdline kho=on panic=-1" 106 + 107 + $qemu_cmd -m 1G -smp 2 -no-reboot -nographic -nodefaults \ 108 + -accel kvm -accel hvf -accel tcg \ 109 + -serial file:"$serial" \ 110 + -append "$cmdline" \ 111 + -kernel "$kernel" \ 112 + -initrd "$initrd" 113 + 114 + grep "KHO restore succeeded" "$serial" &> /dev/null || fail "KHO failed" 115 + } 116 + 117 + function target_to_arch() { 118 + local target=$1 119 + 120 + case $target in 121 + aarch64) echo "arm64" ;; 122 + x86_64) echo "x86" ;; 123 + *) skip "architecture $target is not supported" 124 + esac 125 + } 126 + 127 + function main() { 128 + local build_dir="$kernel_dir/.kho" 129 + local jobs=$(($(nproc) * 2)) 130 + local target="$(uname -m)" 131 + 132 + # skip the test if any of the preparation steps fails 133 + set -o errtrace 134 + trap skip ERR 135 + 136 + while getopts 'hd:j:t:' opt; do 137 + case $opt in 138 + d) 139 + build_dir="$OPTARG" 140 + ;; 141 + j) 142 + jobs="$OPTARG" 143 + ;; 144 + t) 145 + target="$OPTARG" 146 + ;; 147 + h) 148 + usage 149 + exit 0 150 + ;; 151 + *) 152 + echo Unknown argument "$opt" 153 + usage 154 + exit 1 155 + ;; 156 + esac 157 + done 158 + 159 + ktap_print_header 160 + ktap_set_plan 1 161 + 162 + if [[ "$target" != "$(uname -m)" ]] && [[ -z "$CROSS_COMPILE" ]]; then 163 + skip "Cross-platform testing needs to specify CROSS_COMPILE" 164 + fi 165 + 166 + mkdir -p "$build_dir" 167 + local arch=$(target_to_arch "$target") 168 + source "$test_dir/$arch.conf" 169 + 170 + # build the kernel and create initrd 171 + # initrd includes the kernel image that will be kexec'ed 172 + local make_cmd="make ARCH=$arch CROSS_COMPILE=$CROSS_COMPILE -j$jobs" 173 + build_kernel "$build_dir" "$make_cmd" "$QEMU_KCONFIG" "$KERNEL_IMAGE" 174 + 175 + local kernel="$build_dir/arch/$arch/boot/$KERNEL_IMAGE" 176 + mkinitrd "$kernel" 177 + 178 + run_qemu "$QEMU_CMD" "$KERNEL_CMDLINE" "$kernel" 179 + 180 + ktap_test_pass "KHO succeeded" 181 + } 182 + 183 + main "$@"
+7
tools/testing/selftests/kho/x86.conf
··· 1 + QEMU_CMD=qemu-system-x86_64 2 + QEMU_KCONFIG=" 3 + CONFIG_SERIAL_8250=y 4 + CONFIG_SERIAL_8250_CONSOLE=y 5 + " 6 + KERNEL_IMAGE="bzImage" 7 + KERNEL_CMDLINE="console=ttyS0"