Re: [PATCH v3 10/10] tests/functional: add initial RPMI virt guest smoke
Daniel Henrique Barboza <[email protected]>
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
On 7/14/2026 4:56 AM, Subrahmanya Lingappa wrote: > Boot an RPMI-enabled RISC-V virt guest and exercise the initial HSM, > system suspend, and system reset paths. > > Signed-off-by: Subrahmanya Lingappa <[email protected]> > --- > tests/functional/riscv64/meson.build | 2 + > tests/functional/riscv64/test_rpmi_virt.py | 172 +++++++++++++++++++++ > 2 files changed, 174 insertions(+) > create mode 100644 tests/functional/riscv64/test_rpmi_virt.py > > diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build > index 5871211e89..619a0735fa 100644 > --- a/tests/functional/riscv64/meson.build > +++ b/tests/functional/riscv64/meson.build > @@ -3,6 +3,7 @@ > test_riscv64_timeouts = { > 'boston' : 120, > 'tuxrun' : 120, > + 'rpmi_virt' : 300, > } > > tests_riscv64_system_quick = [ > @@ -15,4 +16,5 @@ tests_riscv64_system_thorough = [ > 'boston', > 'sifive_u', > 'tuxrun', > + 'rpmi_virt', > ] > diff --git a/tests/functional/riscv64/test_rpmi_virt.py b/tests/functional/riscv64/test_rpmi_virt.py > new file mode 100644 > index 0000000000..c0f96f7d8d > --- /dev/null > +++ b/tests/functional/riscv64/test_rpmi_virt.py > @@ -0,0 +1,172 @@ > +#!/usr/bin/env python3 > +# > +# RISC-V virt RPMI Linux guest integration tests > +# > +# Copyright (c) 2026 Ventana Micro Systems Inc. We can't add a Ventana copyright dated 2026. Ventana as an entity ceased to exist December 2025. Everything else LGTM. Thanks, Daniel > +# Copyright (c) 2026 Qualcomm Technologies, Inc. > +# > +# Author: > +# Subrahmanya Lingappa <[email protected]> > +# > +# SPDX-License-Identifier: GPL-2.0-or-later > + > +import os > +from pathlib import Path > +import subprocess > +import time > + > +from qemu_test import QemuSystemTest > +from qemu_test import exec_command > +from qemu_test import exec_command_and_wait_for_pattern > +from qemu_test import get_qemu_img > +from qemu_test import wait_for_console_pattern > + > + > +class RiscvVirtRpmiGuestTest(QemuSystemTest): > + """Boot Linux and exercise RPMI-backed services through guest APIs. > + > + This test intentionally keeps guest artifacts external. Set: > + > + QEMU_TEST_RPMI_OPENSBI=/path/to/fw_jump.bin > + QEMU_TEST_RPMI_KERNEL=/path/to/Image > + QEMU_TEST_RPMI_ROOTFS=/path/to/rootfs.ext4 > + > + Exact RPMI packet command-ID coverage remains in riscv-rpmi qtests. This > + functional test verifies Linux/OpenSBI integration paths visible from a > + real guest without adding probe hooks to production RPMI sources. > + """ > + > + timeout = 360 > + PROMPT = 'root@tuxtest:~#' > + > + def _require_path(self, env_name): > + value = os.getenv(env_name) > + if not value: > + self.skipTest(f'{env_name} is required') > + path = Path(value) > + if not path.exists(): > + self.skipTest(f'{env_name} does not exist: {path}') > + return path > + > + def setUp(self): > + super().setUp() > + self.opensbi = self._require_path('QEMU_TEST_RPMI_OPENSBI') > + self.kernel = self._require_path('QEMU_TEST_RPMI_KERNEL') > + self.rootfs = self._require_path('QEMU_TEST_RPMI_ROOTFS') > + self.qemu_img = get_qemu_img(self) > + > + def _create_rootfs_overlay(self): > + overlay = Path(self.scratch_file('rootfs.qcow2')) > + subprocess.run([ > + self.qemu_img, 'create', '-q', '-f', 'qcow2', '-F', 'raw', > + '-b', str(self.rootfs), str(overlay), > + ], check=True) > + return overlay > + > + def _boot_guest(self, smp=4): > + self.set_machine('virt') > + self.machine = 'virt,rpmi=on,aia=aplic-imsic' > + rootfs_overlay = self._create_rootfs_overlay() > + > + self.vm.set_console() > + self.vm.add_args( > + '-cpu', 'rv64', > + '-smp', str(smp), > + '-m', '1G', > + '-bios', str(self.opensbi), > + '-kernel', str(self.kernel), > + '-append', 'printk.time=0 root=/dev/vda rw console=ttyS0 ' > + 'earlycon=sbi loglevel=8 no_console_suspend', > + '-blockdev', 'driver=qcow2,file.driver=file,' > + f'file.filename={rootfs_overlay},node-name=hd0', > + '-device', 'virtio-blk-device,drive=hd0', > + ) > + self.vm.launch() > + > + def _expect_rpmi_firmware_and_linux(self): > + patterns = [ > + 'Platform HSM Device : rpmi-hsm', > + 'Platform Suspend Device : rpmi-system-suspend', > + 'Standard SBI Extensions', > + 'susp', > + 'tuxtest login:', > + ] > + for pattern in patterns: > + wait_for_console_pattern(self, pattern) > + exec_command_and_wait_for_pattern(self, 'root', self.PROMPT) > + > + def _run_marker_command(self, command, marker, failure_marker=None): > + out = exec_command_and_wait_for_pattern(self, command, marker, > + failure_message=failure_marker) > + self.assertIn(marker.encode(), out) > + return out > + > + def _validate_hsm_boot_start(self, online_cpus): > + self._run_marker_command( > + 'echo RPMI_LINUX_HSM_START_BEGIN; ' > + 'cat /sys/devices/system/cpu/present; ' > + 'online=$(cat /sys/devices/system/cpu/online); ' > + 'echo "$online"; ' > + f'if [ "$online" = "{online_cpus}" ]; ' > + 'then echo RPMI_LINUX_HSM_START_PASS; ' > + 'else echo RPMI_LINUX_HSM_START_FAIL; fi', > + 'RPMI_LINUX_HSM_START_PASS', 'RPMI_LINUX_HSM_START_FAIL') > + > + def _validate_hsm_stop(self): > + self._run_marker_command( > + 'echo RPMI_LINUX_HSM_STOP_BEGIN; ' > + 'if [ -w /sys/devices/system/cpu/cpu1/online ]; then ' > + 'echo 0 > /sys/devices/system/cpu/cpu1/online && ' > + '[ "$(cat /sys/devices/system/cpu/online)" = 0 ] && ' > + 'echo RPMI_LINUX_HSM_STOP_PASS || echo RPMI_LINUX_HSM_STOP_FAIL; ' > + 'else echo RPMI_LINUX_HSM_STOP_FAIL; fi', > + 'RPMI_LINUX_HSM_STOP_PASS', 'RPMI_LINUX_HSM_STOP_FAIL') > + > + def _validate_system_suspend(self): > + exec_command( > + self, > + 'echo RPMI_LINUX_SUSPEND_BEGIN; ' > + 'if grep -qw mem /sys/power/state; then ' > + 'echo mem > /sys/power/state; ' > + 'else echo RPMI_LINUX_SUSPEND_FAIL; fi') > + wait_for_console_pattern(self, 'RPMI_LINUX_SUSPEND_BEGIN') > + wait_for_console_pattern(self, 'PM: suspend entry') > + self.vm.event_wait('SUSPEND', 10) > + self.vm.qmp('system_wakeup') > + self.vm.event_wait('WAKEUP', 10) > + wait_for_console_pattern(self, 'PM: suspend exit') > + wait_for_console_pattern(self, self.PROMPT) > + self._run_marker_command( > + 'marker=RPMI_LINUX_WAKEUP; echo ${marker}_PASS', > + 'RPMI_LINUX_WAKEUP_PASS') > + > + def _validate_reset_poweroff(self): > + exec_command(self, 'echo RPMI_LINUX_RESET_POWEROFF_BEGIN; poweroff -f') > + wait_for_console_pattern(self, 'RPMI_LINUX_RESET_POWEROFF_BEGIN') > + wait_for_console_pattern(self, 'reboot: Power down') > + self.vm.wait() > + > + def test_linux_guest_service_enumeration(self): > + self._boot_guest(smp=4) > + self._expect_rpmi_firmware_and_linux() > + self._validate_hsm_boot_start('0-3') > + > + def test_linux_guest_hsm_stop(self): > + self._boot_guest(smp=2) > + self._expect_rpmi_firmware_and_linux() > + self._validate_hsm_boot_start('0-1') > + self._validate_hsm_stop() > + > + def test_linux_guest_suspend(self): > + self._boot_guest(smp=1) > + self._expect_rpmi_firmware_and_linux() > + self._validate_system_suspend() > + > + def test_linux_guest_reset_poweroff(self): > + self._boot_guest(smp=1) > + self._expect_rpmi_firmware_and_linux() > + self._validate_reset_poweroff() > + > + > +if __name__ == '__main__': > + QemuSystemTest.main()