1c1b24f0fSThomas Huth# Functional test that boots known good tuxboot images the same way 2c1b24f0fSThomas Huth# that tuxrun (www.tuxrun.org) does. This tool is used by things like 3c1b24f0fSThomas Huth# the LKFT project to run regression tests on kernels. 4c1b24f0fSThomas Huth# 5c1b24f0fSThomas Huth# Copyright (c) 2023 Linaro Ltd. 6c1b24f0fSThomas Huth# 7c1b24f0fSThomas Huth# Author: 8c1b24f0fSThomas Huth# Alex Bennée <alex.bennee@linaro.org> 9c1b24f0fSThomas Huth# 10c1b24f0fSThomas Huth# SPDX-License-Identifier: GPL-2.0-or-later 11c1b24f0fSThomas Huth 12c1b24f0fSThomas Huthimport os 13c9daf680SDaniel P. Berrangéimport stat 14*37e9b19cSDaniel P. Berrangéfrom subprocess import check_call, DEVNULL 15c1b24f0fSThomas Huth 16c1b24f0fSThomas Huthfrom qemu_test import QemuSystemTest 178a6253a4SDaniel P. Berrangéfrom qemu_test import exec_command_and_wait_for_pattern 18c1b24f0fSThomas Huthfrom qemu_test import wait_for_console_pattern 19*37e9b19cSDaniel P. Berrangéfrom qemu_test import which, get_qemu_img 20c1b24f0fSThomas Huth 21c1b24f0fSThomas Huthclass TuxRunBaselineTest(QemuSystemTest): 22c1b24f0fSThomas Huth 23c1b24f0fSThomas Huth KERNEL_COMMON_COMMAND_LINE = 'printk.time=0' 24c1b24f0fSThomas Huth # Tests are ~10-40s, allow for --debug/--enable-gcov overhead 25c1b24f0fSThomas Huth timeout = 100 26c1b24f0fSThomas Huth 27c1b24f0fSThomas Huth def get_tag(self, tagname, default=None): 28c1b24f0fSThomas Huth """ 29c1b24f0fSThomas Huth Get the metadata tag or return the default. 30c1b24f0fSThomas Huth """ 31c1b24f0fSThomas Huth utag = self._get_unique_tag_val(tagname) 32c1b24f0fSThomas Huth print(f"{tagname}/{default} -> {utag}") 33c1b24f0fSThomas Huth if utag: 34c1b24f0fSThomas Huth return utag 35c1b24f0fSThomas Huth 36c1b24f0fSThomas Huth return default 37c1b24f0fSThomas Huth 38c1b24f0fSThomas Huth def setUp(self): 39c1b24f0fSThomas Huth super().setUp() 40c1b24f0fSThomas Huth 41c1b24f0fSThomas Huth # We need zstd for all the tuxrun tests 429132fff8SDaniel P. Berrangé if which('zstd') is None: 439132fff8SDaniel P. Berrangé self.skipTest("zstd not found in $PATH") 44c1b24f0fSThomas Huth 45c1b24f0fSThomas Huth # Pre-init TuxRun specific settings: Most machines work with 46c1b24f0fSThomas Huth # reasonable defaults but we sometimes need to tweak the 47c1b24f0fSThomas Huth # config. To avoid open coding everything we store all these 48c1b24f0fSThomas Huth # details in the metadata for each test. 49c1b24f0fSThomas Huth 50c1b24f0fSThomas Huth # The tuxboot tag matches the root directory 51c1b24f0fSThomas Huth self.tuxboot = self.arch 52c1b24f0fSThomas Huth 53c1b24f0fSThomas Huth # Most Linux's use ttyS0 for their serial port 54c1b24f0fSThomas Huth self.console = "ttyS0" 55c1b24f0fSThomas Huth 56c1b24f0fSThomas Huth # Does the machine shutdown QEMU nicely on "halt" 57c1b24f0fSThomas Huth self.wait_for_shutdown = True 58c1b24f0fSThomas Huth 59c1b24f0fSThomas Huth self.root = "vda" 60c1b24f0fSThomas Huth 61c1b24f0fSThomas Huth # Occasionally we need extra devices to hook things up 62c1b24f0fSThomas Huth self.extradev = None 63c1b24f0fSThomas Huth 64c1b24f0fSThomas Huth self.qemu_img = get_qemu_img(self) 65c1b24f0fSThomas Huth 66c1b24f0fSThomas Huth def wait_for_console_pattern(self, success_message, vm=None): 67c1b24f0fSThomas Huth wait_for_console_pattern(self, success_message, 68c1b24f0fSThomas Huth failure_message='Kernel panic - not syncing', 69c1b24f0fSThomas Huth vm=vm) 70c1b24f0fSThomas Huth 71c1b24f0fSThomas Huth def fetch_tuxrun_assets(self, kernel_asset, rootfs_asset, dtb_asset=None): 72c1b24f0fSThomas Huth """ 73c1b24f0fSThomas Huth Fetch the TuxBoot assets. 74c1b24f0fSThomas Huth """ 75c1b24f0fSThomas Huth kernel_image = kernel_asset.fetch() 76c1b24f0fSThomas Huth disk_image_zst = rootfs_asset.fetch() 77c1b24f0fSThomas Huth 78beaf88c8SDaniel P. Berrangé disk_image = self.scratch_file("rootfs.ext4") 79c9daf680SDaniel P. Berrangé 80*37e9b19cSDaniel P. Berrangé check_call(['zstd', "-f", "-d", disk_image_zst, 81*37e9b19cSDaniel P. Berrangé "-o", disk_image], 82*37e9b19cSDaniel P. Berrangé stdout=DEVNULL, stderr=DEVNULL) 83c9daf680SDaniel P. Berrangé # zstd copies source archive permissions for the output 84c9daf680SDaniel P. Berrangé # file, so must make this writable for QEMU 85c9daf680SDaniel P. Berrangé os.chmod(disk_image, stat.S_IRUSR | stat.S_IWUSR) 86c1b24f0fSThomas Huth 87c1b24f0fSThomas Huth dtb = dtb_asset.fetch() if dtb_asset is not None else None 88c1b24f0fSThomas Huth 89c9daf680SDaniel P. Berrangé return (kernel_image, disk_image, dtb) 90c1b24f0fSThomas Huth 91c1b24f0fSThomas Huth def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): 92c1b24f0fSThomas Huth """ 93c1b24f0fSThomas Huth Setup to run and add the common parameters to the system 94c1b24f0fSThomas Huth """ 95c1b24f0fSThomas Huth self.vm.set_console(console_index=console_index) 96c1b24f0fSThomas Huth 97c1b24f0fSThomas Huth # all block devices are raw ext4's 98c1b24f0fSThomas Huth blockdev = "driver=raw,file.driver=file," \ 99c1b24f0fSThomas Huth + f"file.filename={disk},node-name=hd0" 100c1b24f0fSThomas Huth 101c1b24f0fSThomas Huth kcmd_line = self.KERNEL_COMMON_COMMAND_LINE 102c1b24f0fSThomas Huth kcmd_line += f" root=/dev/{self.root}" 103c1b24f0fSThomas Huth kcmd_line += f" console={self.console}" 104c1b24f0fSThomas Huth 105c1b24f0fSThomas Huth self.vm.add_args('-kernel', kernel, 106c1b24f0fSThomas Huth '-append', kcmd_line, 107c1b24f0fSThomas Huth '-blockdev', blockdev) 108c1b24f0fSThomas Huth 109c1b24f0fSThomas Huth # Sometimes we need extra devices attached 110c1b24f0fSThomas Huth if self.extradev: 111c1b24f0fSThomas Huth self.vm.add_args('-device', self.extradev) 112c1b24f0fSThomas Huth 113c1b24f0fSThomas Huth self.vm.add_args('-device', 114c1b24f0fSThomas Huth f"{drive},drive=hd0") 115c1b24f0fSThomas Huth 116c1b24f0fSThomas Huth # Some machines need an explicit DTB 117c1b24f0fSThomas Huth if dtb: 118c1b24f0fSThomas Huth self.vm.add_args('-dtb', dtb) 119c1b24f0fSThomas Huth 120c1b24f0fSThomas Huth def run_tuxtest_tests(self, haltmsg): 121c1b24f0fSThomas Huth """ 122c1b24f0fSThomas Huth Wait for the system to boot up, wait for the login prompt and 123c1b24f0fSThomas Huth then do a few things on the console. Trigger a shutdown and 124c1b24f0fSThomas Huth wait to exit cleanly. 125c1b24f0fSThomas Huth """ 12697d79319SDaniel P. Berrangé ps1='root@tuxtest:~#' 12797d79319SDaniel P. Berrangé self.wait_for_console_pattern('tuxtest login:') 12897d79319SDaniel P. Berrangé exec_command_and_wait_for_pattern(self, 'root', ps1) 12997d79319SDaniel P. Berrangé exec_command_and_wait_for_pattern(self, 'cat /proc/interrupts', ps1) 13097d79319SDaniel P. Berrangé exec_command_and_wait_for_pattern(self, 'cat /proc/self/maps', ps1) 13197d79319SDaniel P. Berrangé exec_command_and_wait_for_pattern(self, 'uname -a', ps1) 132c1b24f0fSThomas Huth exec_command_and_wait_for_pattern(self, 'halt', haltmsg) 133c1b24f0fSThomas Huth 134c1b24f0fSThomas Huth # Wait for VM to shut down gracefully if it can 135c1b24f0fSThomas Huth if self.wait_for_shutdown: 136c1b24f0fSThomas Huth self.vm.wait() 137c1b24f0fSThomas Huth else: 138c1b24f0fSThomas Huth self.vm.shutdown() 139c1b24f0fSThomas Huth 140c1b24f0fSThomas Huth def common_tuxrun(self, 141c1b24f0fSThomas Huth kernel_asset, 142c1b24f0fSThomas Huth rootfs_asset, 143c1b24f0fSThomas Huth dtb_asset=None, 144c1b24f0fSThomas Huth drive="virtio-blk-device", 145c1b24f0fSThomas Huth haltmsg="reboot: System halted", 146c1b24f0fSThomas Huth console_index=0): 147c1b24f0fSThomas Huth """ 148c1b24f0fSThomas Huth Common path for LKFT tests. Unless we need to do something 149c1b24f0fSThomas Huth special with the command line we can process most things using 150c1b24f0fSThomas Huth the tag metadata. 151c1b24f0fSThomas Huth """ 152c1b24f0fSThomas Huth (kernel, disk, dtb) = self.fetch_tuxrun_assets(kernel_asset, rootfs_asset, 153c1b24f0fSThomas Huth dtb_asset) 154c1b24f0fSThomas Huth 155c1b24f0fSThomas Huth self.prepare_run(kernel, disk, drive, dtb, console_index) 156c1b24f0fSThomas Huth self.vm.launch() 157c1b24f0fSThomas Huth self.run_tuxtest_tests(haltmsg) 158c1b24f0fSThomas Huth os.remove(disk) 159