Keep the reusable RPMI protocol tests in riscv-rpmi-test.c and expose
them through a common registration helper. The machine-specific test
files now only describe board policy and select the common coverage they
can run.

Move virt-only policy checks into riscv-rpmi-virt-test.c, including the
rpmi=on/off coverage and the virt max-hart rejection case. The virt test
binary continues to register the full RPMI protocol coverage.

Add a riscv-server-ref test binary that rejects the virt-only rpmi
property and then registers the meaningful shared protocol subset for the
board: Base, System Reset, reset cleanup, negative doorbell/queue
handling, migration blocking, HSM, and System Suspend. Server-ref
intentionally skips only the virt-specific optional-machine-property
cases and the extra virt-only topology/reset stress cases.

Keeping the common protocol body shared avoids duplicating the virt test
implementation as more RISC-V machines add RPMI coverage. Update
MAINTAINERS so the split RPMI qtest files continue to reach the RISC-V
maintainers.

Signed-off-by: Subrahmanya Lingappa <[email protected]>
---
 MAINTAINERS                              |   2 +-
 tests/qtest/meson.build                  |   4 +-
 tests/qtest/riscv-rpmi-server-ref-test.c |  36 ++
 tests/qtest/riscv-rpmi-test.c            | 476 ++++++++++++-----------
 tests/qtest/riscv-rpmi-test.h            | 131 +++++++
 tests/qtest/riscv-rpmi-virt-test.c       |  45 +++
 6 files changed, 469 insertions(+), 225 deletions(-)
 create mode 100644 tests/qtest/riscv-rpmi-server-ref-test.c
 create mode 100644 tests/qtest/riscv-rpmi-test.h
 create mode 100644 tests/qtest/riscv-rpmi-virt-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d2577f66b6..d85385213f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -374,7 +374,7 @@ F: hw/riscv/
 F: hw/intc/riscv*
 F: hw/misc/riscv_rpmi*
 F: include/hw/misc/riscv_rpmi.h
-F: tests/qtest/riscv-rpmi-test.c
+F: tests/qtest/riscv-rpmi*
 F: include/hw/char/riscv_htif.h
 F: include/hw/riscv/
 F: common-user/host/riscv*
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 96f9ba9d4b..5b1cb4b9a3 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -297,7 +297,7 @@ qtests_riscv32 = \
 qtests_riscv64 = ['riscv-csr-test'] + \
   (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
   (config_all_devices.has_key('CONFIG_RISCV_RPMI') ?
-   ['riscv-rpmi-test'] : []) + \
+   ['riscv-rpmi-virt-test', 'riscv-rpmi-server-ref-test'] : []) + \
   (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
    config_all_devices.has_key('CONFIG_RISCV_IOMMU') ?
    ['iommu-riscv-test'] : []) + \
@@ -412,6 +412,8 @@ qtests = {
   'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
   'migration-test': test_migration_files + migration_tls_files + 
migration_colo_files,
   'pxe-test': files('boot-sector.c'),
+  'riscv-rpmi-server-ref-test': files('riscv-rpmi-test.c'),
+  'riscv-rpmi-virt-test': files('riscv-rpmi-test.c'),
   'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c',
                           'pnv-xive2-nvpg_bar.c'),
   'qos-test': [chardev, io, qos_test_ss.apply({}).sources()],
diff --git a/tests/qtest/riscv-rpmi-server-ref-test.c 
b/tests/qtest/riscv-rpmi-server-ref-test.c
new file mode 100644
index 0000000000..bed6f586bc
--- /dev/null
+++ b/tests/qtest/riscv-rpmi-server-ref-test.c
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QTests for riscv-server-ref RPMI support.
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ * Author:
+ *  Subrahmanya Lingappa <[email protected]>
+ */
+
+#include "qemu/osdep.h"
+#include "riscv-rpmi-test.h"
+
+static void test_rpmi_rvserver_ref_rejects_rpmi_property(void)
+{
+    rpmi_expect_qemu_failure(
+        "-machine riscv-server-ref,rpmi=off -display none -S",
+        "Property 'riscv-server-ref-machine.rpmi' not found");
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    if (!qtest_has_machine("riscv-server-ref")) {
+        return g_test_run();
+    }
+
+    qtest_add_func("/riscv/rpmi/rvserver-ref/machine/rejects-rpmi-property",
+                   test_rpmi_rvserver_ref_rejects_rpmi_property);
+    rpmi_register_common_tests("/riscv/rpmi/rvserver-ref",
+                               &rvserver_ref_rpmi_machine,
+                               RPMI_QTEST_SERVER_REF);
+
+    return g_test_run();
+}
diff --git a/tests/qtest/riscv-rpmi-test.c b/tests/qtest/riscv-rpmi-test.c
index fa23ab73db..e0557ccffa 100644
--- a/tests/qtest/riscv-rpmi-test.c
+++ b/tests/qtest/riscv-rpmi-test.c
@@ -1,7 +1,7 @@
 /*
  * SPDX-License-Identifier: GPL-2.0-or-later
  *
- * QTests for RISC-V RPMI devices.
+ * Common helpers for RISC-V RPMI qtests.
  *
  * Copyright (c) 2026 Qualcomm Technologies, Inc.
  * Author:
@@ -9,71 +9,49 @@
  */
 
 #include "qemu/osdep.h"
-#include <glib/gstdio.h>
-#include "libqtest.h"
+#include "riscv-rpmi-test.h"
 #include "qobject/qdict.h"
 
-#define RPMI_SHMEM_BASE 0x10200000ULL
-#define RPMI_DOORBELL_BASE 0x10230000ULL
-#define RPMI_SLOT_SIZE 64
-
-#define RPMI_A2P_HEAD RPMI_SHMEM_BASE
-#define RPMI_A2P_TAIL (RPMI_SHMEM_BASE + RPMI_SLOT_SIZE)
-#define RPMI_A2P_SLOT0 (RPMI_SHMEM_BASE + 2 * RPMI_SLOT_SIZE)
-
-#define RPMI_SRVGRP_BASE 0x0001
-#define RPMI_SRVGRP_SYSTEM_RESET 0x0003
-#define RPMI_SRVGRP_SYSTEM_SUSPEND 0x0004
-#define RPMI_SRVGRP_HSM 0x0005
-#define RPMI_BASE_SRV_GET_PLATFORM_INFO 0x05
-#define RPMI_BASE_SRV_PROBE_SERVICE_GROUP 0x06
-#define RPMI_SYSRST_SRV_GET_ATTRIBUTES 0x02
-#define RPMI_SYSRST_SRV_SYSTEM_RESET 0x03
-#define RPMI_HSM_SRV_GET_HART_STATUS 0x02
-#define RPMI_HSM_SRV_GET_HART_LIST 0x03
-#define RPMI_HSM_SRV_GET_SUSPEND_TYPES 0x04
-#define RPMI_HSM_SRV_GET_SUSPEND_INFO 0x05
-#define RPMI_HSM_SRV_HART_START 0x06
-#define RPMI_HSM_SRV_HART_STOP 0x07
-#define RPMI_HSM_SRV_HART_SUSPEND 0x08
-#define RPMI_SYSSUSP_SRV_GET_ATTRIBUTES 0x02
-#define RPMI_SYSSUSP_SRV_SYSTEM_SUSPEND 0x03
-#define RPMI_MSG_NORMAL_REQUEST 0x00
-#define RPMI_MSG_POSTED_REQUEST 0x01
-#define RPMI_MSG_ACKNOWLEDGEMENT 0x02
-#define RPMI_SYSRST_TYPE_SHUTDOWN 0x00
-#define RPMI_SYSRST_TYPE_COLD_REBOOT 0x01
-#define RPMI_SYSRST_TYPE_INVALID 0x03
-#define RPMI_SYSRST_ATTRS_FLAGS_RESETTYPE 1
-#define RPMI_TOKEN 0x55aa
-#define RPMI_ERR_NOTSUPP 0xfffffffeU
-#define RPMI_ERR_INVALID_PARAM 0xfffffffdU
-#define RPMI_ERR_INVALID_ADDR 0xfffffffbU
-#define RPMI_ERR_DENIED 0xfffffffcU
-#define RPMI_HSM_HART_STATE_STARTED 0x00
-#define RPMI_HSM_HART_STATE_STOPPED 0x01
-#define RPMI_HSM_HART_STATE_SUSPENDED 0x04
-#define RPMI_HSM_TEST_START_ADDR 0x80000000ULL
-#define RPMI_HSM_TEST_RESUME_ADDR 0x80001000ULL
-
-#define RPMI_P2A_ACK_BASE (RPMI_SHMEM_BASE + 16 * RPMI_SLOT_SIZE)
-#define RPMI_P2A_ACK_HEAD RPMI_P2A_ACK_BASE
-#define RPMI_P2A_ACK_TAIL (RPMI_P2A_ACK_BASE + RPMI_SLOT_SIZE)
-#define RPMI_P2A_ACK_SLOT0 (RPMI_P2A_ACK_BASE + 2 * RPMI_SLOT_SIZE)
-
+const RiscvRpmiQTestMachine virt_rpmi_machine = {
+    .machine_opts = "virt,rpmi=on",
+    .service_group_extra_args = ",aia=aplic-imsic",
+    .shmem_base = RPMI_SHMEM_BASE,
+    .doorbell_base = RPMI_DOORBELL_BASE,
+    .platform_info = "QEMU RISC-V virt RPMI",
+};
+
+const RiscvRpmiQTestMachine rvserver_ref_rpmi_machine = {
+    .machine_opts = "riscv-server-ref",
+    .service_group_extra_args = "",
+    .shmem_base = RVSERVER_RPMI_SHMEM_BASE,
+    .doorbell_base = RVSERVER_RPMI_DOORBELL_BASE,
+    .platform_info = "QEMU RISC-V server-ref RPMI",
+};
+
+uint64_t rpmi_shmem_base = RPMI_SHMEM_BASE;
+uint64_t rpmi_doorbell_base = RPMI_DOORBELL_BASE;
 static uint64_t rpmi_response_base;
 
+QTestState *rpmi_qtest_init(const RiscvRpmiQTestMachine *machine,
+                            const char *extra_args)
+{
+    rpmi_shmem_base = machine->shmem_base;
+    rpmi_doorbell_base = machine->doorbell_base;
+
+    return qtest_initf("-machine %s%s", machine->machine_opts, extra_args);
+}
+
 static uint64_t rpmi_queue_slot(uint64_t queue_base, uint32_t index)
 {
     return queue_base + (index + 2) * RPMI_SLOT_SIZE;
 }
 
-static void rpmi_send_request(QTestState *qts, uint16_t service_group,
-                              uint8_t service_id, uint8_t request_type,
-                              const uint32_t *data, size_t data_words)
+void rpmi_send_request(QTestState *qts, uint16_t service_group,
+                       uint8_t service_id, uint8_t request_type,
+                       const uint32_t *data, size_t data_words)
 {
     uint32_t tail = qtest_readl(qts, RPMI_A2P_TAIL);
-    uint64_t slot = rpmi_queue_slot(RPMI_SHMEM_BASE, tail);
+    uint64_t slot = rpmi_queue_slot(rpmi_shmem_base, tail);
     size_t i;
 
     qtest_writew(qts, slot, service_group);
@@ -90,21 +68,21 @@ static void rpmi_send_request(QTestState *qts, uint16_t 
service_group,
         "RPMI_A2P_REQ shmem=0x%016" PRIx64 " doorbell=0x%016" PRIx64
         " group=0x%04x service=0x%02x type=0x%02x data_len=%zu"
         " token=0x%04x a2p_tail=%u slot=0x%016" PRIx64,
-        (uint64_t)RPMI_SHMEM_BASE, (uint64_t)RPMI_DOORBELL_BASE,
+        (uint64_t)rpmi_shmem_base, (uint64_t)rpmi_doorbell_base,
         service_group, service_id, request_type, data_words * sizeof(*data),
         RPMI_TOKEN, tail, slot);
 
     qtest_writel(qts, RPMI_A2P_TAIL, (tail + 1) % 16);
-    qtest_writel(qts, RPMI_DOORBELL_BASE, 1);
+    qtest_writel(qts, rpmi_doorbell_base, 1);
 }
 
-static uint32_t rpmi_response_word(QTestState *qts, unsigned int word)
+uint32_t rpmi_response_word(QTestState *qts, unsigned int word)
 {
     return qtest_readl(qts, rpmi_response_base + 8 + word * sizeof(uint32_t));
 }
 
-static void rpmi_expect_ack(QTestState *qts, uint16_t service_group,
-                            uint8_t service_id, uint16_t data_len)
+void rpmi_expect_ack(QTestState *qts, uint16_t service_group,
+                     uint8_t service_id, uint16_t data_len)
 {
     uint32_t head = qtest_readl(qts, RPMI_P2A_ACK_HEAD);
     uint32_t tail = qtest_readl(qts, RPMI_P2A_ACK_TAIL);
@@ -124,23 +102,23 @@ static void rpmi_expect_ack(QTestState *qts, uint16_t 
service_group,
         " group=0x%04x service=0x%02x type=0x%02x data_len=%u"
         " token=0x%04x p2a_head=%u slot=0x%016" PRIx64
         " status=0x%08x",
-        (uint64_t)RPMI_SHMEM_BASE, service_group, service_id,
+        (uint64_t)rpmi_shmem_base, service_group, service_id,
         RPMI_MSG_ACKNOWLEDGEMENT, data_len, RPMI_TOKEN, head,
         rpmi_response_base,
         data_len >= sizeof(uint32_t) ? rpmi_response_word(qts, 0) : 0);
     qtest_writel(qts, RPMI_P2A_ACK_HEAD, (head + 1) % 16);
 }
 
-static void rpmi_send_sysreset(QTestState *qts, uint32_t reset_type,
-                               uint8_t request_type)
+void rpmi_send_sysreset(QTestState *qts, uint32_t reset_type,
+                        uint8_t request_type)
 {
     rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET,
                       RPMI_SYSRST_SRV_SYSTEM_RESET, request_type,
                       &reset_type, 1);
 }
 
-static void rpmi_expect_qemu_failure(const char *extra_args,
-                                     const char *stderr_needle)
+void rpmi_expect_qemu_failure(const char *extra_args,
+                              const char *stderr_needle)
 {
     g_autoptr(GError) error = NULL;
     g_auto(GStrv) split_args = NULL;
@@ -169,47 +147,37 @@ static void rpmi_expect_qemu_failure(const char 
*extra_args,
     g_assert_nonnull(strstr(stderr_data, stderr_needle));
 }
 
-static void test_rpmi_machine_realize_off(void)
-{
-    QTestState *qts;
-
-    qts = qtest_init("-machine virt,rpmi=off");
-    qtest_quit(qts);
-}
-
-static void test_rpmi_machine_rejects_too_many_harts(void)
-{
-    rpmi_expect_qemu_failure(
-        "-machine virt,rpmi=on -smp 513 -display none -S",
-        "max CPUs supported by machine 'virt' is 512");
-}
-
-static void test_rpmi_base_platform_info(void)
+void rpmi_check_platform_info(QTestState *qts, const char *expected)
 {
-    static const char expected[] = "QEMU RISC-V virt RPMI";
-    QTestState *qts;
+    size_t expected_len = strlen(expected) + 1;
     size_t i;
 
-    qts = qtest_init("-machine virt,rpmi=on");
     rpmi_send_request(qts, RPMI_SRVGRP_BASE,
                       RPMI_BASE_SRV_GET_PLATFORM_INFO,
                       RPMI_MSG_NORMAL_REQUEST, NULL, 0);
 
     rpmi_expect_ack(qts, RPMI_SRVGRP_BASE,
                     RPMI_BASE_SRV_GET_PLATFORM_INFO,
-                    2 * sizeof(uint32_t) + sizeof(expected));
+                    2 * sizeof(uint32_t) + expected_len);
     g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 1), ==, sizeof(expected));
-    for (i = 0; i < sizeof(expected); i++) {
+    g_assert_cmphex(rpmi_response_word(qts, 1), ==, expected_len);
+    for (i = 0; i < expected_len; i++) {
         g_assert_cmphex(qtest_readb(qts, RPMI_P2A_ACK_SLOT0 + 16 + i), ==,
                         expected[i]);
     }
+}
+
+void rpmi_test_platform_info(const RiscvRpmiQTestMachine *machine)
+{
+    QTestState *qts;
+
+    qts = rpmi_qtest_init(machine, "");
+    rpmi_check_platform_info(qts, machine->platform_info);
 
     qtest_quit(qts);
 }
 
-static void rpmi_probe_group(QTestState *qts, uint32_t service_group,
-                             bool present)
+void rpmi_probe_group(QTestState *qts, uint32_t service_group, bool present)
 {
     rpmi_send_request(qts, RPMI_SRVGRP_BASE,
                       RPMI_BASE_SRV_PROBE_SERVICE_GROUP,
@@ -226,11 +194,8 @@ static void rpmi_probe_group(QTestState *qts, uint32_t 
service_group,
     }
 }
 
-static void test_rpmi_base_probe_service_groups(void)
+void rpmi_check_service_groups(QTestState *qts)
 {
-    QTestState *qts;
-
-    qts = qtest_init("-machine virt,rpmi=on,aia=aplic-imsic");
     rpmi_probe_group(qts, RPMI_SRVGRP_BASE, true);
     qtest_system_reset(qts);
     rpmi_probe_group(qts, RPMI_SRVGRP_SYSTEM_RESET, true);
@@ -238,16 +203,102 @@ static void test_rpmi_base_probe_service_groups(void)
     rpmi_probe_group(qts, RPMI_SRVGRP_HSM, true);
     qtest_system_reset(qts);
     rpmi_probe_group(qts, RPMI_SRVGRP_SYSTEM_SUSPEND, true);
+}
+
+void rpmi_test_service_groups(const RiscvRpmiQTestMachine *machine,
+                              const char *extra_args)
+{
+    QTestState *qts;
+
+    qts = rpmi_qtest_init(machine, extra_args);
+    rpmi_check_service_groups(qts);
 
     qtest_quit(qts);
 }
 
-static void test_rpmi_sysreset_attrs(void)
+void rpmi_check_hsm_hart_list(QTestState *qts, uint32_t hart_count)
+{
+    uint32_t start_index = 0;
+
+    rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_LIST,
+                      RPMI_MSG_NORMAL_REQUEST, &start_index, 1);
+
+    rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_LIST,
+                    (3 + hart_count) * sizeof(uint32_t));
+    g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
+    g_assert_cmphex(rpmi_response_word(qts, 1), ==, 0);
+    g_assert_cmphex(rpmi_response_word(qts, 2), ==, hart_count);
+    for (uint32_t i = 0; i < hart_count; i++) {
+        g_assert_cmphex(rpmi_response_word(qts, i + 3), ==, i);
+    }
+}
+
+void rpmi_test_hsm_hart_list(const RiscvRpmiQTestMachine *machine,
+                             const char *extra_args)
 {
     QTestState *qts;
+
+    qts = rpmi_qtest_init(machine, extra_args);
+    rpmi_check_hsm_hart_list(qts, 4);
+
+    qtest_quit(qts);
+}
+
+void rpmi_expect_hsm_status(QTestState *qts, uint32_t hart_id,
+                            uint32_t expected_state)
+{
+    rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_STATUS,
+                      RPMI_MSG_NORMAL_REQUEST, &hart_id, 1);
+    rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_STATUS,
+                    2 * sizeof(uint32_t));
+    g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
+    g_assert_cmphex(rpmi_response_word(qts, 1), ==, expected_state);
+}
+
+uint64_t rpmi_hart_pc(QTestState *qts, uint32_t cpu_index)
+{
+    g_autofree char *registers = qtest_hmp(qts, "info registers %u",
+                                           cpu_index);
+    const char *pc_line;
+    uint64_t pc;
+
+    pc_line = strstr(registers, "\n pc");
+    g_assert_nonnull(pc_line);
+    g_assert_cmpint(sscanf(pc_line, "\n pc %" SCNx64, &pc), ==, 1);
+
+    return pc;
+}
+
+static void rpmi_add_common_test(const char *prefix, const char *name,
+                                 const RiscvRpmiQTestMachine *machine,
+                                 void (*fn)(const void *))
+{
+    g_autofree char *path = g_strdup_printf("%s/%s", prefix, name);
+
+    qtest_add_data_func(path, machine, fn);
+}
+
+static void test_rpmi_common_base_platform_info(const void *opaque)
+{
+    const RiscvRpmiQTestMachine *machine = opaque;
+
+    rpmi_test_platform_info(machine);
+}
+
+static void test_rpmi_common_base_probe_service_groups(const void *opaque)
+{
+    const RiscvRpmiQTestMachine *machine = opaque;
+
+    rpmi_test_service_groups(machine, machine->service_group_extra_args);
+}
+
+static void test_rpmi_common_sysreset_attrs(const void *opaque)
+{
+    const RiscvRpmiQTestMachine *machine = opaque;
+    QTestState *qts;
     uint32_t reset_type = RPMI_SYSRST_TYPE_SHUTDOWN;
 
-    qts = qtest_init("-machine virt,rpmi=on");
+    qts = rpmi_qtest_init(machine, "");
     rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET,
                       RPMI_SYSRST_SRV_GET_ATTRIBUTES,
                       RPMI_MSG_NORMAL_REQUEST, &reset_type, 1);
@@ -281,33 +332,36 @@ static void test_rpmi_sysreset_attrs(void)
     qtest_quit(qts);
 }
 
-static void test_rpmi_sysreset_shutdown(void)
+static void test_rpmi_common_sysreset_shutdown(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
 
-    qts = qtest_init("-machine virt,rpmi=on");
+    qts = rpmi_qtest_init(machine, "");
     rpmi_send_sysreset(qts, RPMI_SYSRST_TYPE_SHUTDOWN,
                        RPMI_MSG_POSTED_REQUEST);
     qtest_qmp_eventwait(qts, "SHUTDOWN");
     qtest_quit(qts);
 }
 
-static void test_rpmi_sysreset_cold_reboot(void)
+static void test_rpmi_common_sysreset_cold_reboot(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
 
-    qts = qtest_init("-machine virt,rpmi=on -no-reboot");
+    qts = rpmi_qtest_init(machine, " -no-reboot");
     rpmi_send_sysreset(qts, RPMI_SYSRST_TYPE_COLD_REBOOT,
                        RPMI_MSG_POSTED_REQUEST);
     qtest_qmp_eventwait(qts, "SHUTDOWN");
     qtest_quit(qts);
 }
 
-static void test_rpmi_sysreset_invalid_type(void)
+static void test_rpmi_common_sysreset_invalid_type(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
 
-    qts = qtest_init("-machine virt,rpmi=on");
+    qts = rpmi_qtest_init(machine, "");
     rpmi_send_sysreset(qts, RPMI_SYSRST_TYPE_INVALID,
                        RPMI_MSG_NORMAL_REQUEST);
 
@@ -319,12 +373,13 @@ static void test_rpmi_sysreset_invalid_type(void)
     qtest_quit(qts);
 }
 
-static void test_rpmi_repeated_reset_after_traffic(void)
+static void test_rpmi_common_repeated_reset_after_traffic(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
     uint32_t reset_type = RPMI_SYSRST_TYPE_SHUTDOWN;
 
-    qts = qtest_init("-machine virt,rpmi=on,aia=aplic-imsic");
+    qts = rpmi_qtest_init(machine, machine->service_group_extra_args);
     for (uint32_t i = 0; i < 5; i++) {
         rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET,
                           RPMI_SYSRST_SRV_GET_ATTRIBUTES,
@@ -339,11 +394,12 @@ static void test_rpmi_repeated_reset_after_traffic(void)
     qtest_quit(qts);
 }
 
-static void test_rpmi_reset_clears_transport(void)
+static void test_rpmi_common_reset_clears_transport(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
 
-    qts = qtest_init("-machine virt,rpmi=on");
+    qts = rpmi_qtest_init(machine, "");
     rpmi_send_sysreset(qts, RPMI_SYSRST_TYPE_INVALID,
                        RPMI_MSG_NORMAL_REQUEST);
     rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET,
@@ -353,43 +409,46 @@ static void test_rpmi_reset_clears_transport(void)
 
     g_assert_cmphex(qtest_readl(qts, RPMI_A2P_TAIL), ==, 0);
     g_assert_cmphex(qtest_readl(qts, RPMI_P2A_ACK_TAIL), ==, 0);
-    g_assert_cmphex(qtest_readl(qts, RPMI_DOORBELL_BASE), ==, 0);
+    g_assert_cmphex(qtest_readl(qts, rpmi_doorbell_base), ==, 0);
 
     qtest_quit(qts);
 }
 
-static void test_rpmi_doorbell_invalid_access(void)
+static void test_rpmi_common_doorbell_invalid_access(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
 
-    qts = qtest_init("-machine virt,rpmi=on");
-    qtest_writeb(qts, RPMI_DOORBELL_BASE, 1);
-    qtest_writel(qts, RPMI_DOORBELL_BASE + 4, 1);
-    g_assert_cmphex(qtest_readl(qts, RPMI_DOORBELL_BASE), ==, 0);
+    qts = rpmi_qtest_init(machine, "");
+    qtest_writeb(qts, rpmi_doorbell_base, 1);
+    qtest_writel(qts, rpmi_doorbell_base + 4, 1);
+    g_assert_cmphex(qtest_readl(qts, rpmi_doorbell_base), ==, 0);
 
     qtest_quit(qts);
 }
 
-static void test_rpmi_queue_bounds(void)
+static void test_rpmi_common_queue_bounds(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
 
-    qts = qtest_init("-machine virt,rpmi=on");
+    qts = rpmi_qtest_init(machine, "");
     qtest_writel(qts, RPMI_A2P_TAIL, 0x1000);
-    qtest_writel(qts, RPMI_DOORBELL_BASE, 1);
+    qtest_writel(qts, rpmi_doorbell_base, 1);
     g_assert_cmphex(qtest_readl(qts, RPMI_A2P_HEAD), ==, 0);
     g_assert_cmphex(qtest_readl(qts, RPMI_P2A_ACK_TAIL), ==, 0);
 
     qtest_quit(qts);
 }
 
-static void test_rpmi_migration_blocked(void)
+static void test_rpmi_common_migration_blocked(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
     QDict *error;
     const char *desc;
 
-    qts = qtest_init("-machine virt,rpmi=on -S");
+    qts = rpmi_qtest_init(machine, " -S");
     error = qtest_qmp_assert_failure_ref(qts,
         "{ 'execute': 'migrate',"
         "  'arguments': { 'uri': 'exec:cat > /dev/null' } }");
@@ -403,57 +462,32 @@ static void test_rpmi_migration_blocked(void)
     qtest_quit(qts);
 }
 
-static void test_rpmi_hsm_hart_list(void)
+static void test_rpmi_common_hsm_hart_list(const void *opaque)
 {
-    QTestState *qts;
-    uint32_t start_index = 0;
-
-    qts = qtest_init("-machine virt,rpmi=on -smp 4");
-    rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_LIST,
-                      RPMI_MSG_NORMAL_REQUEST, &start_index, 1);
-
-    rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_LIST,
-                    7 * sizeof(uint32_t));
-    g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 1), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 2), ==, 4);
-    g_assert_cmphex(rpmi_response_word(qts, 3), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 4), ==, 1);
-    g_assert_cmphex(rpmi_response_word(qts, 5), ==, 2);
-    g_assert_cmphex(rpmi_response_word(qts, 6), ==, 3);
+    const RiscvRpmiQTestMachine *machine = opaque;
 
-    qtest_quit(qts);
+    rpmi_test_hsm_hart_list(machine, " -smp 4");
 }
 
-static void test_rpmi_hsm_multi_socket_hart_list(void)
+static void test_rpmi_common_hsm_multi_socket_hart_list(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
-    uint32_t start_index = 0;
 
-    qts = qtest_init("-machine virt,rpmi=on "
-                     "-smp 4,sockets=2,cores=2,threads=1");
-    rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_LIST,
-                      RPMI_MSG_NORMAL_REQUEST, &start_index, 1);
-
-    rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_LIST,
-                    7 * sizeof(uint32_t));
-    g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 1), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 2), ==, 4);
-    g_assert_cmphex(rpmi_response_word(qts, 3), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 4), ==, 1);
-    g_assert_cmphex(rpmi_response_word(qts, 5), ==, 2);
-    g_assert_cmphex(rpmi_response_word(qts, 6), ==, 3);
+    qts = rpmi_qtest_init(machine,
+                          " -smp 4,sockets=2,cores=2,threads=1");
+    rpmi_check_hsm_hart_list(qts, 4);
 
     qtest_quit(qts);
 }
 
-static void test_rpmi_hsm_hart_status(void)
+static void test_rpmi_common_hsm_hart_status(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
     uint32_t hart_id = 3;
 
-    qts = qtest_init("-machine virt,rpmi=on -smp 4");
+    qts = rpmi_qtest_init(machine, " -smp 4");
     rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_STATUS,
                       RPMI_MSG_NORMAL_REQUEST, &hart_id, 1);
 
@@ -466,33 +500,9 @@ static void test_rpmi_hsm_hart_status(void)
     qtest_quit(qts);
 }
 
-static void rpmi_expect_hsm_status(QTestState *qts, uint32_t hart_id,
-                                   uint32_t expected_state)
-{
-    rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_STATUS,
-                      RPMI_MSG_NORMAL_REQUEST, &hart_id, 1);
-    rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_HART_STATUS,
-                    2 * sizeof(uint32_t));
-    g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
-    g_assert_cmphex(rpmi_response_word(qts, 1), ==, expected_state);
-}
-
-static uint64_t rpmi_hart_pc(QTestState *qts, uint32_t cpu_index)
-{
-    g_autofree char *registers = qtest_hmp(qts, "info registers %u",
-                                           cpu_index);
-    const char *pc_line;
-    uint64_t pc;
-
-    pc_line = strstr(registers, "\n pc");
-    g_assert_nonnull(pc_line);
-    g_assert_cmpint(sscanf(pc_line, "\n pc %" SCNx64, &pc), ==, 1);
-
-    return pc;
-}
-
-static void test_rpmi_hsm_hart_control(void)
+static void test_rpmi_common_hsm_hart_control(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
     uint32_t hart_id = 1;
     uint32_t stop_request[] = { hart_id };
@@ -501,7 +511,7 @@ static void test_rpmi_hsm_hart_control(void)
     uint32_t start_index = 0;
     uint32_t suspend_type = 0;
 
-    qts = qtest_init("-machine virt,rpmi=on -smp 2");
+    qts = rpmi_qtest_init(machine, " -smp 2");
     rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_SUSPEND_TYPES,
                       RPMI_MSG_NORMAL_REQUEST, &start_index, 1);
     rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_SUSPEND_TYPES,
@@ -547,13 +557,14 @@ static void test_rpmi_hsm_hart_control(void)
     qtest_quit(qts);
 }
 
-static void test_rpmi_syssusp_attrs_and_suspend(void)
+static void test_rpmi_common_syssusp_attrs_and_suspend(const void *opaque)
 {
+    const RiscvRpmiQTestMachine *machine = opaque;
     QTestState *qts;
     uint32_t suspend_type = 0;
     uint32_t suspend_request[] = { 0, 0, 0x80000000, 0 };
 
-    qts = qtest_init("-machine virt,rpmi=on -smp 1");
+    qts = rpmi_qtest_init(machine, " -smp 1");
     rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_SUSPEND,
                       RPMI_SYSSUSP_SRV_GET_ATTRIBUTES,
                       RPMI_MSG_NORMAL_REQUEST, &suspend_type, 1);
@@ -578,48 +589,67 @@ static void test_rpmi_syssusp_attrs_and_suspend(void)
     qtest_quit(qts);
 }
 
-int main(int argc, char **argv)
-{
-    g_test_init(&argc, &argv, NULL);
-
-    if (qtest_has_machine("virt")) {
-        qtest_add_func("/riscv/rpmi/machine/realize-off",
-                       test_rpmi_machine_realize_off);
-        qtest_add_func("/riscv/rpmi/machine/rejects-too-many-harts",
-                       test_rpmi_machine_rejects_too_many_harts);
-        qtest_add_func("/riscv/rpmi/base/platform-info",
-                       test_rpmi_base_platform_info);
-        qtest_add_func("/riscv/rpmi/base/probe-service-groups",
-                       test_rpmi_base_probe_service_groups);
-        qtest_add_func("/riscv/rpmi/sysreset/attrs",
-                       test_rpmi_sysreset_attrs);
-        qtest_add_func("/riscv/rpmi/sysreset/shutdown",
-                       test_rpmi_sysreset_shutdown);
-        qtest_add_func("/riscv/rpmi/sysreset/cold-reboot",
-                       test_rpmi_sysreset_cold_reboot);
-        qtest_add_func("/riscv/rpmi/sysreset/invalid-type",
-                       test_rpmi_sysreset_invalid_type);
-        qtest_add_func("/riscv/rpmi/reset/clears-transport",
-                       test_rpmi_reset_clears_transport);
-        qtest_add_func("/riscv/rpmi/negative/doorbell-invalid-access",
-                       test_rpmi_doorbell_invalid_access);
-        qtest_add_func("/riscv/rpmi/negative/queue-bounds",
-                       test_rpmi_queue_bounds);
-        qtest_add_func("/riscv/rpmi/reset/repeated-after-traffic",
-                       test_rpmi_repeated_reset_after_traffic);
-        qtest_add_func("/riscv/rpmi/migration/blocked",
-                       test_rpmi_migration_blocked);
-        qtest_add_func("/riscv/rpmi/hsm/hart-list",
-                       test_rpmi_hsm_hart_list);
-        qtest_add_func("/riscv/rpmi/hsm/multi-socket-hart-list",
-                       test_rpmi_hsm_multi_socket_hart_list);
-        qtest_add_func("/riscv/rpmi/hsm/hart-status",
-                       test_rpmi_hsm_hart_status);
-        qtest_add_func("/riscv/rpmi/hsm/hart-control",
-                       test_rpmi_hsm_hart_control);
-        qtest_add_func("/riscv/rpmi/syssusp/attrs-and-suspend",
-                       test_rpmi_syssusp_attrs_and_suspend);
+void rpmi_register_common_tests(const char *prefix,
+                                const RiscvRpmiQTestMachine *machine,
+                                unsigned int coverage)
+{
+    if (coverage & RPMI_QTEST_BASE) {
+        rpmi_add_common_test(prefix, "base/platform-info", machine,
+                             test_rpmi_common_base_platform_info);
+        rpmi_add_common_test(prefix, "base/probe-service-groups", machine,
+                             test_rpmi_common_base_probe_service_groups);
     }
 
-    return g_test_run();
+    if (coverage & RPMI_QTEST_SYSRESET) {
+        rpmi_add_common_test(prefix, "sysreset/attrs", machine,
+                             test_rpmi_common_sysreset_attrs);
+        rpmi_add_common_test(prefix, "sysreset/shutdown", machine,
+                             test_rpmi_common_sysreset_shutdown);
+        rpmi_add_common_test(prefix, "sysreset/cold-reboot", machine,
+                             test_rpmi_common_sysreset_cold_reboot);
+        rpmi_add_common_test(prefix, "sysreset/invalid-type", machine,
+                             test_rpmi_common_sysreset_invalid_type);
+    }
+
+    if (coverage & RPMI_QTEST_RESET) {
+        rpmi_add_common_test(prefix, "reset/clears-transport", machine,
+                             test_rpmi_common_reset_clears_transport);
+    }
+
+    if (coverage & RPMI_QTEST_NEGATIVE) {
+        rpmi_add_common_test(prefix, "negative/doorbell-invalid-access",
+                             machine,
+                             test_rpmi_common_doorbell_invalid_access);
+        rpmi_add_common_test(prefix, "negative/queue-bounds", machine,
+                             test_rpmi_common_queue_bounds);
+    }
+
+    if (coverage & RPMI_QTEST_REPEATED_RESET) {
+        rpmi_add_common_test(prefix, "reset/repeated-after-traffic", machine,
+                             test_rpmi_common_repeated_reset_after_traffic);
+    }
+
+    if (coverage & RPMI_QTEST_MIGRATION) {
+        rpmi_add_common_test(prefix, "migration/blocked", machine,
+                             test_rpmi_common_migration_blocked);
+    }
+
+    if (coverage & RPMI_QTEST_HSM) {
+        rpmi_add_common_test(prefix, "hsm/hart-list", machine,
+                             test_rpmi_common_hsm_hart_list);
+        rpmi_add_common_test(prefix, "hsm/hart-status", machine,
+                             test_rpmi_common_hsm_hart_status);
+        rpmi_add_common_test(prefix, "hsm/hart-control", machine,
+                             test_rpmi_common_hsm_hart_control);
+    }
+
+    if (coverage & RPMI_QTEST_HSM_MULTI_SOCKET) {
+        rpmi_add_common_test(prefix, "hsm/multi-socket-hart-list", machine,
+                             test_rpmi_common_hsm_multi_socket_hart_list);
+    }
+
+    if (coverage & RPMI_QTEST_SYSSUSP) {
+        rpmi_add_common_test(prefix, "syssusp/attrs-and-suspend", machine,
+                             test_rpmi_common_syssusp_attrs_and_suspend);
+    }
 }
diff --git a/tests/qtest/riscv-rpmi-test.h b/tests/qtest/riscv-rpmi-test.h
new file mode 100644
index 0000000000..2fce0bb293
--- /dev/null
+++ b/tests/qtest/riscv-rpmi-test.h
@@ -0,0 +1,131 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Common helpers for RISC-V RPMI qtests.
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ * Author:
+ *  Subrahmanya Lingappa <[email protected]>
+ */
+
+#ifndef QTEST_RISCV_RPMI_TEST_H
+#define QTEST_RISCV_RPMI_TEST_H
+
+#include "libqtest.h"
+
+#define RPMI_SHMEM_BASE 0x10200000ULL
+#define RPMI_DOORBELL_BASE 0x10230000ULL
+#define RVSERVER_RPMI_SHMEM_BASE 0x110000ULL
+#define RVSERVER_RPMI_DOORBELL_BASE 0x140000ULL
+#define RPMI_SLOT_SIZE 64
+
+#define RPMI_A2P_HEAD rpmi_shmem_base
+#define RPMI_A2P_TAIL (rpmi_shmem_base + RPMI_SLOT_SIZE)
+#define RPMI_A2P_SLOT0 (rpmi_shmem_base + 2 * RPMI_SLOT_SIZE)
+
+#define RPMI_SRVGRP_BASE 0x0001
+#define RPMI_SRVGRP_SYSTEM_RESET 0x0003
+#define RPMI_SRVGRP_SYSTEM_SUSPEND 0x0004
+#define RPMI_SRVGRP_HSM 0x0005
+#define RPMI_BASE_SRV_GET_PLATFORM_INFO 0x05
+#define RPMI_BASE_SRV_PROBE_SERVICE_GROUP 0x06
+#define RPMI_SYSRST_SRV_GET_ATTRIBUTES 0x02
+#define RPMI_SYSRST_SRV_SYSTEM_RESET 0x03
+#define RPMI_HSM_SRV_GET_HART_STATUS 0x02
+#define RPMI_HSM_SRV_GET_HART_LIST 0x03
+#define RPMI_HSM_SRV_GET_SUSPEND_TYPES 0x04
+#define RPMI_HSM_SRV_GET_SUSPEND_INFO 0x05
+#define RPMI_HSM_SRV_HART_START 0x06
+#define RPMI_HSM_SRV_HART_STOP 0x07
+#define RPMI_HSM_SRV_HART_SUSPEND 0x08
+#define RPMI_SYSSUSP_SRV_GET_ATTRIBUTES 0x02
+#define RPMI_SYSSUSP_SRV_SYSTEM_SUSPEND 0x03
+#define RPMI_MSG_NORMAL_REQUEST 0x00
+#define RPMI_MSG_POSTED_REQUEST 0x01
+#define RPMI_MSG_ACKNOWLEDGEMENT 0x02
+#define RPMI_SYSRST_TYPE_SHUTDOWN 0x00
+#define RPMI_SYSRST_TYPE_COLD_REBOOT 0x01
+#define RPMI_SYSRST_TYPE_INVALID 0x03
+#define RPMI_SYSRST_ATTRS_FLAGS_RESETTYPE 1
+#define RPMI_TOKEN 0x55aa
+#define RPMI_ERR_NOTSUPP 0xfffffffeU
+#define RPMI_ERR_INVALID_PARAM 0xfffffffdU
+#define RPMI_ERR_INVALID_ADDR 0xfffffffbU
+#define RPMI_ERR_DENIED 0xfffffffcU
+#define RPMI_HSM_HART_STATE_STARTED 0x00
+#define RPMI_HSM_HART_STATE_STOPPED 0x01
+#define RPMI_HSM_HART_STATE_SUSPENDED 0x04
+#define RPMI_HSM_TEST_START_ADDR 0x80000000ULL
+#define RPMI_HSM_TEST_RESUME_ADDR 0x80001000ULL
+
+#define RPMI_P2A_ACK_BASE (rpmi_shmem_base + 16 * RPMI_SLOT_SIZE)
+#define RPMI_P2A_ACK_HEAD RPMI_P2A_ACK_BASE
+#define RPMI_P2A_ACK_TAIL (RPMI_P2A_ACK_BASE + RPMI_SLOT_SIZE)
+#define RPMI_P2A_ACK_SLOT0 (RPMI_P2A_ACK_BASE + 2 * RPMI_SLOT_SIZE)
+
+typedef struct RiscvRpmiQTestMachine {
+    const char *machine_opts;
+    const char *service_group_extra_args;
+    uint64_t shmem_base;
+    uint64_t doorbell_base;
+    const char *platform_info;
+} RiscvRpmiQTestMachine;
+
+typedef enum RiscvRpmiQTestCoverage {
+    RPMI_QTEST_BASE = 1 << 0,
+    RPMI_QTEST_SYSRESET = 1 << 1,
+    RPMI_QTEST_RESET = 1 << 2,
+    RPMI_QTEST_NEGATIVE = 1 << 3,
+    RPMI_QTEST_MIGRATION = 1 << 4,
+    RPMI_QTEST_HSM = 1 << 5,
+    RPMI_QTEST_HSM_MULTI_SOCKET = 1 << 6,
+    RPMI_QTEST_SYSSUSP = 1 << 7,
+    RPMI_QTEST_REPEATED_RESET = 1 << 8,
+} RiscvRpmiQTestCoverage;
+
+#define RPMI_QTEST_ALL \
+    (RPMI_QTEST_BASE | RPMI_QTEST_SYSRESET | RPMI_QTEST_RESET | \
+     RPMI_QTEST_NEGATIVE | RPMI_QTEST_MIGRATION | RPMI_QTEST_HSM | \
+     RPMI_QTEST_HSM_MULTI_SOCKET | RPMI_QTEST_SYSSUSP | \
+     RPMI_QTEST_REPEATED_RESET)
+
+#define RPMI_QTEST_SERVER_REF \
+    (RPMI_QTEST_BASE | RPMI_QTEST_SYSRESET | RPMI_QTEST_RESET | \
+     RPMI_QTEST_NEGATIVE | RPMI_QTEST_MIGRATION | RPMI_QTEST_HSM | \
+     RPMI_QTEST_SYSSUSP)
+
+extern const RiscvRpmiQTestMachine virt_rpmi_machine;
+extern const RiscvRpmiQTestMachine rvserver_ref_rpmi_machine;
+
+extern uint64_t rpmi_shmem_base;
+extern uint64_t rpmi_doorbell_base;
+
+QTestState *rpmi_qtest_init(const RiscvRpmiQTestMachine *machine,
+                            const char *extra_args);
+void rpmi_send_request(QTestState *qts, uint16_t service_group,
+                       uint8_t service_id, uint8_t request_type,
+                       const uint32_t *data, size_t data_words);
+uint32_t rpmi_response_word(QTestState *qts, unsigned int word);
+void rpmi_expect_ack(QTestState *qts, uint16_t service_group,
+                     uint8_t service_id, uint16_t data_len);
+void rpmi_send_sysreset(QTestState *qts, uint32_t reset_type,
+                        uint8_t request_type);
+void rpmi_expect_qemu_failure(const char *extra_args,
+                              const char *stderr_needle);
+void rpmi_check_platform_info(QTestState *qts, const char *expected);
+void rpmi_test_platform_info(const RiscvRpmiQTestMachine *machine);
+void rpmi_probe_group(QTestState *qts, uint32_t service_group, bool present);
+void rpmi_check_service_groups(QTestState *qts);
+void rpmi_test_service_groups(const RiscvRpmiQTestMachine *machine,
+                              const char *extra_args);
+void rpmi_check_hsm_hart_list(QTestState *qts, uint32_t hart_count);
+void rpmi_test_hsm_hart_list(const RiscvRpmiQTestMachine *machine,
+                             const char *extra_args);
+void rpmi_expect_hsm_status(QTestState *qts, uint32_t hart_id,
+                            uint32_t expected_state);
+uint64_t rpmi_hart_pc(QTestState *qts, uint32_t cpu_index);
+void rpmi_register_common_tests(const char *prefix,
+                                const RiscvRpmiQTestMachine *machine,
+                                unsigned int coverage);
+
+#endif
diff --git a/tests/qtest/riscv-rpmi-virt-test.c 
b/tests/qtest/riscv-rpmi-virt-test.c
new file mode 100644
index 0000000000..3aa73d3286
--- /dev/null
+++ b/tests/qtest/riscv-rpmi-virt-test.c
@@ -0,0 +1,45 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QTests for virt machine RPMI support.
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ * Author:
+ *  Subrahmanya Lingappa <[email protected]>
+ */
+
+#include "qemu/osdep.h"
+#include "riscv-rpmi-test.h"
+
+static void test_rpmi_machine_realize_off(void)
+{
+    QTestState *qts;
+
+    qts = qtest_init("-machine virt,rpmi=off");
+    qtest_quit(qts);
+}
+
+static void test_rpmi_machine_rejects_too_many_harts(void)
+{
+    rpmi_expect_qemu_failure(
+        "-machine virt,rpmi=on -smp 513 -display none -S",
+        "max CPUs supported by machine 'virt' is 512");
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    if (!qtest_has_machine("virt")) {
+        return g_test_run();
+    }
+
+    qtest_add_func("/riscv/rpmi/virt/machine/realize-off",
+                   test_rpmi_machine_realize_off);
+    qtest_add_func("/riscv/rpmi/virt/machine/rejects-too-many-harts",
+                   test_rpmi_machine_rejects_too_many_harts);
+    rpmi_register_common_tests("/riscv/rpmi/virt", &virt_rpmi_machine,
+                               RPMI_QTEST_ALL);
+
+    return g_test_run();
+}
-- 
2.43.0


Reply via email to