1# Functional test that boots known good tuxboot images the same way 2# that tuxrun (www.tuxrun.org) does. This tool is used by things like 3# the LKFT project to run regression tests on kernels. 4# 5# Copyright (c) 2023 Linaro Ltd. 6# 7# Author: 8# Alex Bennée <alex.bennee@linaro.org> 9# 10# SPDX-License-Identifier: GPL-2.0-or-later 11 12import os 13import time 14 15from avocado import skip, skipIf 16from avocado_qemu import QemuSystemTest 17from avocado_qemu import exec_command, exec_command_and_wait_for_pattern 18from avocado_qemu import wait_for_console_pattern 19from avocado.utils import process 20from avocado.utils.path import find_command 21 22class TuxRunBaselineTest(QemuSystemTest): 23 """ 24 :avocado: tags=accel:tcg 25 """ 26 27 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0' 28 # Tests are ~10-40s, allow for --debug/--enable-gcov overhead 29 timeout = 100 30 31 def get_tag(self, tagname, default=None): 32 """ 33 Get the metadata tag or return the default. 34 """ 35 utag = self._get_unique_tag_val(tagname) 36 print(f"{tagname}/{default} -> {utag}") 37 if utag: 38 return utag 39 40 return default 41 42 def setUp(self): 43 super().setUp() 44 45 # We need zstd for all the tuxrun tests 46 # See https://github.com/avocado-framework/avocado/issues/5609 47 zstd = find_command('zstd', False) 48 if zstd is False: 49 self.cancel('Could not find "zstd", which is required to ' 50 'decompress rootfs') 51 self.zstd = zstd 52 53 # Process the TuxRun specific tags, most machines work with 54 # reasonable defaults but we sometimes need to tweak the 55 # config. To avoid open coding everything we store all these 56 # details in the metadata for each test. 57 58 # The tuxboot tag matches the root directory 59 self.tuxboot = self.get_tag('tuxboot') 60 61 # Most Linux's use ttyS0 for their serial port 62 self.console = self.get_tag('console', "ttyS0") 63 64 # Does the machine shutdown QEMU nicely on "halt" 65 self.shutdown = self.get_tag('shutdown') 66 67 # The name of the kernel Image file 68 self.image = self.get_tag('image', "Image") 69 70 # The block device drive type 71 self.drive = self.get_tag('drive', "virtio-blk-device") 72 73 self.root = self.get_tag('root', "vda") 74 75 # Occasionally we need extra devices to hook things up 76 self.extradev = self.get_tag('extradev') 77 78 def wait_for_console_pattern(self, success_message, vm=None): 79 wait_for_console_pattern(self, success_message, 80 failure_message='Kernel panic - not syncing', 81 vm=vm) 82 83 def fetch_tuxrun_assets(self, dt=None): 84 """ 85 Fetch the TuxBoot assets. They are stored in a standard way so we 86 use the per-test tags to fetch details. 87 """ 88 base_url = f"https://storage.tuxboot.com/{self.tuxboot}/" 89 kernel_image = self.fetch_asset(base_url + self.image) 90 disk_image_zst = self.fetch_asset(base_url + "rootfs.ext4.zst") 91 92 cmd = f"{self.zstd} -d {disk_image_zst} -o {self.workdir}/rootfs.ext4" 93 process.run(cmd) 94 95 if dt: 96 dtb = self.fetch_asset(base_url + dt) 97 else: 98 dtb = None 99 100 return (kernel_image, self.workdir + "/rootfs.ext4", dtb) 101 102 def prepare_run(self, kernel, disk, dtb=None, console_index=0): 103 """ 104 Setup to run and add the common parameters to the system 105 """ 106 self.vm.set_console(console_index=console_index) 107 108 # all block devices are raw ext4's 109 blockdev = "driver=raw,file.driver=file," \ 110 + f"file.filename={disk},node-name=hd0" 111 112 kcmd_line = self.KERNEL_COMMON_COMMAND_LINE 113 kcmd_line += f" root=/dev/{self.root}" 114 kcmd_line += f" console={self.console}" 115 116 self.vm.add_args('-kernel', kernel, 117 '-append', kcmd_line, 118 '-blockdev', blockdev) 119 120 # Sometimes we need extra devices attached 121 if self.extradev: 122 self.vm.add_args('-device', self.extradev) 123 124 # Some machines already define a drive device 125 if self.drive != "none": 126 self.vm.add_args('-device', 127 f"{self.drive},drive=hd0") 128 129 # Some machines need an explicit DTB 130 if dtb: 131 self.vm.add_args('-dtb', dtb) 132 133 def run_tuxtest_tests(self, haltmsg): 134 """ 135 Wait for the system to boot up, wait for the login prompt and 136 then do a few things on the console. Trigger a shutdown and 137 wait to exit cleanly. 138 """ 139 self.wait_for_console_pattern("Welcome to TuxTest") 140 time.sleep(0.2) 141 exec_command(self, 'root') 142 time.sleep(0.2) 143 exec_command(self, 'cat /proc/interrupts') 144 time.sleep(0.1) 145 exec_command(self, 'cat /proc/self/maps') 146 time.sleep(0.1) 147 exec_command(self, 'uname -a') 148 time.sleep(0.1) 149 exec_command_and_wait_for_pattern(self, 'halt', haltmsg) 150 151 # Wait for VM to shut down gracefully if it can 152 if self.shutdown == "nowait": 153 self.vm.shutdown() 154 else: 155 self.vm.wait() 156 157 def common_tuxrun(self, dt=None, haltmsg="reboot: System halted", 158 console_index=0): 159 """ 160 Common path for LKFT tests. Unless we need to do something 161 special with the command line we can process most things using 162 the tag metadata. 163 """ 164 (kernel, disk, dtb) = self.fetch_tuxrun_assets(dt) 165 166 self.prepare_run(kernel, disk, dtb, console_index) 167 self.vm.launch() 168 self.run_tuxtest_tests(haltmsg) 169 170 # 171 # The tests themselves. The configuration is derived from how 172 # tuxrun invokes qemu (with minor tweaks like using -blockdev 173 # consistently). The tuxrun equivalent is something like: 174 # 175 # tuxrun --device qemu-{ARCH} \ 176 # --kernel https://storage.tuxboot.com/{TUXBOOT}/{IMAGE} 177 # 178 179 def test_arm64(self): 180 """ 181 :avocado: tags=arch:aarch64 182 :avocado: tags=cpu:cortex-a57 183 :avocado: tags=machine:virt 184 :avocado: tags=tuxboot:arm64 185 :avocado: tags=console:ttyAMA0 186 :avocado: tags=shutdown:nowait 187 """ 188 self.common_tuxrun() 189 190 def test_arm64be(self): 191 """ 192 :avocado: tags=arch:aarch64 193 :avocado: tags=cpu:cortex-a57 194 :avocado: tags=endian:big 195 :avocado: tags=machine:virt 196 :avocado: tags=tuxboot:arm64be 197 :avocado: tags=console:ttyAMA0 198 :avocado: tags=shutdown:nowait 199 """ 200 self.common_tuxrun() 201 202 def test_armv5(self): 203 """ 204 :avocado: tags=arch:arm 205 :avocado: tags=cpu:arm926 206 :avocado: tags=machine:versatilepb 207 :avocado: tags=tuxboot:armv5 208 :avocado: tags=image:zImage 209 :avocado: tags=drive:virtio-blk-pci 210 :avocado: tags=console:ttyAMA0 211 :avocado: tags=shutdown:nowait 212 """ 213 self.common_tuxrun(dt="versatile-pb.dtb") 214 215 def test_armv7(self): 216 """ 217 :avocado: tags=arch:arm 218 :avocado: tags=cpu:cortex-a15 219 :avocado: tags=machine:virt 220 :avocado: tags=tuxboot:armv7 221 :avocado: tags=image:zImage 222 :avocado: tags=console:ttyAMA0 223 :avocado: tags=shutdown:nowait 224 """ 225 self.common_tuxrun() 226 227 def test_armv7be(self): 228 """ 229 :avocado: tags=arch:arm 230 :avocado: tags=cpu:cortex-a15 231 :avocado: tags=endian:big 232 :avocado: tags=machine:virt 233 :avocado: tags=tuxboot:armv7be 234 :avocado: tags=image:zImage 235 :avocado: tags=console:ttyAMA0 236 :avocado: tags=shutdown:nowait 237 """ 238 self.common_tuxrun() 239 240 def test_i386(self): 241 """ 242 :avocado: tags=arch:i386 243 :avocado: tags=cpu:coreduo 244 :avocado: tags=machine:q35 245 :avocado: tags=tuxboot:i386 246 :avocado: tags=image:bzImage 247 :avocado: tags=drive:virtio-blk-pci 248 :avocado: tags=shutdown:nowait 249 """ 250 self.common_tuxrun() 251 252 def test_mips32(self): 253 """ 254 :avocado: tags=arch:mips 255 :avocado: tags=machine:malta 256 :avocado: tags=cpu:mips32r6-generic 257 :avocado: tags=endian:big 258 :avocado: tags=tuxboot:mips32 259 :avocado: tags=image:vmlinux 260 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 261 :avocado: tags=root:sda 262 :avocado: tags=shutdown:nowait 263 """ 264 self.common_tuxrun() 265 266 def test_mips32el(self): 267 """ 268 :avocado: tags=arch:mipsel 269 :avocado: tags=machine:malta 270 :avocado: tags=cpu:mips32r6-generic 271 :avocado: tags=tuxboot:mips32el 272 :avocado: tags=image:vmlinux 273 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 274 :avocado: tags=root:sda 275 :avocado: tags=shutdown:nowait 276 """ 277 self.common_tuxrun() 278 279 @skip("QEMU currently broken") # regression against stable QEMU 280 def test_mips64(self): 281 """ 282 :avocado: tags=arch:mips64 283 :avocado: tags=machine:malta 284 :avocado: tags=tuxboot:mips64 285 :avocado: tags=endian:big 286 :avocado: tags=image:vmlinux 287 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 288 :avocado: tags=root:sda 289 :avocado: tags=shutdown:nowait 290 """ 291 self.common_tuxrun() 292 293 def test_mips64el(self): 294 """ 295 :avocado: tags=arch:mips64el 296 :avocado: tags=machine:malta 297 :avocado: tags=tuxboot:mips64el 298 :avocado: tags=image:vmlinux 299 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 300 :avocado: tags=root:sda 301 :avocado: tags=shutdown:nowait 302 """ 303 self.common_tuxrun() 304 305 def test_ppc32(self): 306 """ 307 :avocado: tags=arch:ppc 308 :avocado: tags=machine:ppce500 309 :avocado: tags=cpu:e500mc 310 :avocado: tags=tuxboot:ppc32 311 :avocado: tags=image:uImage 312 :avocado: tags=drive:virtio-blk-pci 313 :avocado: tags=shutdown:nowait 314 """ 315 self.common_tuxrun() 316 317 def test_ppc64(self): 318 """ 319 :avocado: tags=arch:ppc64 320 :avocado: tags=machine:pseries 321 :avocado: tags=cpu:POWER8 322 :avocado: tags=endian:big 323 :avocado: tags=console:hvc0 324 :avocado: tags=tuxboot:ppc64 325 :avocado: tags=image:vmlinux 326 :avocado: tags=extradev:driver=spapr-vscsi 327 :avocado: tags=drive:scsi-hd 328 :avocado: tags=root:sda 329 """ 330 self.common_tuxrun() 331 332 def test_ppc64le(self): 333 """ 334 :avocado: tags=arch:ppc64 335 :avocado: tags=machine:pseries 336 :avocado: tags=cpu:POWER8 337 :avocado: tags=console:hvc0 338 :avocado: tags=tuxboot:ppc64le 339 :avocado: tags=image:vmlinux 340 :avocado: tags=extradev:driver=spapr-vscsi 341 :avocado: tags=drive:scsi-hd 342 :avocado: tags=root:sda 343 """ 344 self.common_tuxrun() 345 346 def test_riscv32(self): 347 """ 348 :avocado: tags=arch:riscv32 349 :avocado: tags=machine:virt 350 :avocado: tags=tuxboot:riscv32 351 """ 352 self.common_tuxrun() 353 354 def test_riscv64(self): 355 """ 356 :avocado: tags=arch:riscv64 357 :avocado: tags=machine:virt 358 :avocado: tags=tuxboot:riscv64 359 """ 360 self.common_tuxrun() 361 362 def test_s390(self): 363 """ 364 :avocado: tags=arch:s390x 365 :avocado: tags=endian:big 366 :avocado: tags=tuxboot:s390 367 :avocado: tags=image:bzImage 368 :avocado: tags=drive:virtio-blk-ccw 369 :avocado: tags=shutdown:nowait 370 """ 371 self.common_tuxrun(haltmsg="Requesting system halt") 372 373 # Note: some segfaults caused by unaligned userspace access 374 @skipIf(os.getenv('GITLAB_CI'), 'Skipping unstable test on GitLab') 375 def test_sh4(self): 376 """ 377 :avocado: tags=arch:sh4 378 :avocado: tags=machine:r2d 379 :avocado: tags=cpu:sh7785 380 :avocado: tags=tuxboot:sh4 381 :avocado: tags=image:zImage 382 :avocado: tags=root:sda 383 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 384 :avocado: tags=console:ttySC1 385 """ 386 # The test is currently too unstable to do much in userspace 387 # so we skip common_tuxrun and do a minimal boot and shutdown. 388 (kernel, disk, dtb) = self.fetch_tuxrun_assets() 389 390 # the console comes on the second serial port 391 self.prepare_run(kernel, disk, console_index=1) 392 self.vm.launch() 393 394 self.wait_for_console_pattern("Welcome to TuxTest") 395 time.sleep(0.1) 396 exec_command(self, 'root') 397 time.sleep(0.1) 398 exec_command_and_wait_for_pattern(self, 'halt', 399 "reboot: System halted") 400 401 def test_sparc64(self): 402 """ 403 :avocado: tags=arch:sparc64 404 :avocado: tags=tuxboot:sparc64 405 :avocado: tags=image:vmlinux 406 :avocado: tags=root:sda 407 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 408 :avocado: tags=shutdown:nowait 409 """ 410 self.common_tuxrun() 411 412 def test_x86_64(self): 413 """ 414 :avocado: tags=arch:x86_64 415 :avocado: tags=machine:q35 416 :avocado: tags=cpu:Nehalem 417 :avocado: tags=tuxboot:x86_64 418 :avocado: tags=image:bzImage 419 :avocado: tags=root:sda 420 :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 421 :avocado: tags=shutdown:nowait 422 """ 423 self.common_tuxrun() 424