1import os
2
3import infra.basetest
4
5
6class TestKexec(infra.basetest.BRTest):
7
8    # A specific configuration is needed for using kexec:
9    # - We use Aarch64 since it is well supported for kexec,
10    # - A kernel config fragment enables all the kexec parts,
11    # - The kernel Image is installed on target filesystem to be
12    #   reloaded through kexec,
13    # - We use a ext4 rootfs image exposed as a virtio storage (rather
14    #   than cpio initrd). This avoids needing to install the initrd
15    #   inside the rootfs.
16    config = \
17        """
18        BR2_aarch64=y
19        BR2_TOOLCHAIN_EXTERNAL=y
20        BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
21        BR2_LINUX_KERNEL=y
22        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
23        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.15"
24        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
25        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
26        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
27        BR2_LINUX_KERNEL_DTS_SUPPORT=y
28        BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="{}"
29        BR2_LINUX_KERNEL_INSTALL_TARGET=y
30        BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
31        BR2_PACKAGE_KEXEC=y
32        BR2_TARGET_ROOTFS_EXT2=y
33        BR2_TARGET_ROOTFS_EXT2_4=y
34        # BR2_TARGET_ROOTFS_TAR is not set
35        """.format(
36            infra.filepath("tests/package/test_kexec/linux-kexec.fragment"),
37            infra.filepath("tests/package/test_kexec/qemu-aarch64-virt-5.2-machine.dts")
38        )
39
40    def test_run(self):
41        hda = os.path.join(self.builddir, "images", "rootfs.ext4")
42        kern = os.path.join(self.builddir, "images", "Image")
43        dtb = os.path.join(self.builddir, "images", "qemu-aarch64-virt-5.2-machine.dtb")
44        # Notes:
45        # Sufficient memory is needed to load the kernel: having at
46        # least 512MB works. kexec could silently fail if not enough
47        # memory is present. KASLR needs to be disabled for the test:
48        # we pass "nokaslr" to kernel bootargs, and also pass a custom
49        # devicetree to qemu virt machine. This devicetree is based on
50        # qemu aarch64 5.2 dts with kaslr-seed set 0.
51        # With newer qemu >= 7.0 we can disable KASLR from the qemu
52        # command line using "dtb-kaslr-seed=off".
53        bootargs = ["root=/dev/vda console=ttyAMA0 nokaslr"]
54        qemu_opts = ["-M", "virt", "-dtb", dtb, "-cpu", "cortex-a57", "-m", "512M",
55                     "-drive", f"file={hda},if=virtio,format=raw"]
56        self.emulator.boot(arch="aarch64",
57                           kernel=kern,
58                           kernel_cmdline=bootargs,
59                           options=qemu_opts)
60        self.emulator.login()
61
62        # Test the program can execute
63        self.assertRunOk("kexec --version")
64
65        # Check the kexec kernel is NOT loaded:
66        self.assertRunOk("test \"$(cat /sys/kernel/kexec_loaded)\" -eq 0")
67
68        # Load the Kernel:
69        # "--append br-test" adds a dummy kernel args we'll be able to
70        # check in the second executed kernel.
71        # We use the dtb image from /sys/firmware/fdt (since we don't
72        # have the dtb file in the system)
73        self.assertRunOk("kexec -d -l --dtb=/sys/firmware/fdt --reuse-cmdline --serial=ttyAMA0 --append=br-test /boot/Image")
74
75        # Check the kexec kernel IS loaded:
76        self.assertRunOk("test \"$(cat /sys/kernel/kexec_loaded)\" -eq 1")
77
78        # Create a marker file in tmpfs which is supposed to disappear
79        # after kexec kernel restart.
80        self.assertRunOk("touch /dev/shm/br-kexec-marker")
81
82        # Execute the loaded kernel (i.e perform a kexec reboot)
83        # qemu.sendline() is used here because no exit code nor
84        # program return is expected, since kexec is like a
85        # reboot. The login is expected to be reached after the
86        # command is issued.
87        self.emulator.qemu.sendline("kexec -d -e")
88
89        # Wait for the login, and log again
90        self.emulator.login()
91
92        # Check the "br-test" dummy kernel arg is present
93        self.assertRunOk("grep br-test /proc/cmdline")
94
95        # Check the test marker file is no longer here
96        self.assertRunOk("test ! -e /dev/shm/br-kexec-marker")
97
98        # After restart, the kernel is not supposed to have a kexec
99        # loaded image:
100        self.assertRunOk("test \"$(cat /sys/kernel/kexec_loaded)\" -eq 0")
101