On Fri, Aug 14, 2026 at 10:24 AM Subrahmanya Lingappa
<[email protected]> wrote:
>
> Add qtests for the RPMI transport plus Base, system reset, HSM, and
> system suspend service groups.
>
> Signed-off-by: Subrahmanya Lingappa <[email protected]>
> Reviewed-by: Daniel Henrique Barboza <[email protected]>
> ---
>  MAINTAINERS                   |   1 +
>  tests/qtest/meson.build       |   2 +
>  tests/qtest/riscv-rpmi-test.c | 684 ++++++++++++++++++++++++++++++++++
>  3 files changed, 687 insertions(+)
>  create mode 100644 tests/qtest/riscv-rpmi-test.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8e91a31757..6dd71c5a82 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -361,6 +361,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: 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 4897325d84..d54b79cbcf 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -290,6 +290,8 @@ qtests_riscv32 = \
>
>  qtests_riscv64 = ['riscv-csr-test'] + \
>    (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
> +  (config_all_devices.has_key('CONFIG_RISCV_RPMI') ?
> +   ['riscv-rpmi-test'] : []) + \
>    (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
>     config_all_devices.has_key('CONFIG_RISCV_IOMMU') ?
>     ['iommu-riscv-test'] : []) + \
> diff --git a/tests/qtest/riscv-rpmi-test.c b/tests/qtest/riscv-rpmi-test.c
> new file mode 100644
> index 0000000000..dab723b999
> --- /dev/null
> +++ b/tests/qtest/riscv-rpmi-test.c
> @@ -0,0 +1,684 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * QTests for RISC-V RPMI devices.
> + *
> + * Copyright (c) 2026 Qualcomm Technologies, Inc.
> + * Author:
> + *  Subrahmanya Lingappa <[email protected]>
> + */
> +
> +#include "qemu/osdep.h"
> +#include <glib/gstdio.h>
> +#include "libqtest.h"
> +#include "qobject/qdict.h"
> +
> +#define RPMI_SHMEM_BASE 0x10200000ULL
> +#define RPMI_DOORBELL_BASE 0x10230000ULL
> +#define RPMI_IMSIC_S_BASE 0x28000000ULL
> +#define RPMI_CPPC_FASTCHAN_BASE 0x10240000ULL
> +#define RPMI_CPPC_FASTCHAN_SIZE 0x4000
> +#define RPMI_CPPC_FASTCHAN_FEEDBACK_OFFSET 0x2000
> +#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_MSI 0x0002
> +#define RPMI_SRVGRP_SYSTEM_RESET 0x0003
> +#define RPMI_SRVGRP_SYSTEM_SUSPEND 0x0004
> +#define RPMI_SRVGRP_HSM 0x0005
> +#define RPMI_SRVGRP_CPPC 0x0006
> +#define RPMI_SRVGRP_CLOCK 0x0008
> +#define RPMI_SRVGRP_MANAGEMENT_MODE 0x000b
> +#define RPMI_SRVGRP_LOGGING 0x000e
> +#define RPMI_BASE_SRV_GET_PLATFORM_INFO 0x05
> +#define RPMI_BASE_SRV_PROBE_SERVICE_GROUP 0x06
> +#define RPMI_SYSMSI_SRV_GET_ATTRIBUTES 0x02
> +#define RPMI_SYSMSI_SRV_GET_MSI_ATTRIBUTES 0x03
> +#define RPMI_SYSMSI_SRV_SET_MSI_STATE 0x04
> +#define RPMI_SYSMSI_SRV_GET_MSI_STATE 0x05
> +#define RPMI_SYSMSI_SRV_SET_MSI_TARGET 0x06
> +#define RPMI_SYSMSI_SRV_GET_MSI_TARGET 0x07
> +#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_CPPC_SRV_PROBE_REG 0x02
>
Please add only the required definitions. Not the future ones.

> +#define RPMI_CPPC_SRV_READ_REG 0x03
> +#define RPMI_CPPC_SRV_WRITE_REG 0x04
> +#define RPMI_CPPC_SRV_GET_FAST_CHANNEL_REGION 0x05
> +#define RPMI_CPPC_SRV_GET_FAST_CHANNEL_OFFSET 0x06
> +#define RPMI_CPPC_SRV_GET_HART_LIST 0x07
> +#define RPMI_CPPC_NOMINAL_PERF 0x01
> +#define RPMI_CPPC_DESIRED_PERF 0x05
> +#define RPMI_CPPC_COUNTER_WRAPAROUND_TIME 0x0a
> +#define RPMI_CPPC_REFERENCE_PERF_COUNTER 0x0b
> +#define RPMI_CPPC_DELIVERED_PERF_COUNTER 0x0c
> +#define RPMI_CPPC_REG_LEN_64 64
> +#define RPMI_CLK_SRV_GET_NUM_CLOCKS 0x02
> +#define RPMI_CLK_SRV_GET_ATTRIBUTES 0x03
> +#define RPMI_CLK_SRV_GET_SUPPORTED_RATES 0x04
> +#define RPMI_CLK_SRV_SET_CONFIG 0x05
> +#define RPMI_CLK_SRV_GET_CONFIG 0x06
> +#define RPMI_CLK_SRV_SET_RATE 0x07
> +#define RPMI_CLK_SRV_GET_RATE 0x08
> +#define RPMI_MM_SRV_GET_ATTRIBUTES 0x02
> +#define RPMI_MM_SRV_COMMUNICATE 0x03
> +#define RPMI_LOGGING_SRV_SET_CONFIG 0x02
> +#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_SYS_MSI_SHUTDOWN_INDEX 0
> +#define RPMI_SYS_MSI_REBOOT_INDEX 1
> +#define RPMI_SYS_MSI_SUSPEND_INDEX 2
> +#define RPMI_SYS_NUM_MSI 4
> +#define RPMI_SYSMSI_MSI_STATE_ENABLE 1
> +#define RPMI_SYSMSI_MSI_STATE_PENDING 2
> +#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 VIRT_RPMI_CPPC_NOMINAL_PERF 30
> +#define VIRT_RPMI_CLOCK_COUNT 6
> +#define VIRT_RPMI_MM_VERSION 0x10000
> +#define VIRT_RPMI_SHMEM_SIZE 0x20000
> +#define RPMI_MM_INPUT_OFFSET 0x3000
> +#define RPMI_MM_OUTPUT_OFFSET 0x3800
> +#define RPMI_MM_BUFFER_SIZE 0x400
> +#define RPMI_MM_INPUT_BASE (RPMI_SHMEM_BASE + RPMI_MM_INPUT_OFFSET)
> +#define RPMI_MM_OUTPUT_BASE (RPMI_SHMEM_BASE + RPMI_MM_OUTPUT_OFFSET)
> +#define MM_EFI_COMM_HEADER_SIZE 24
> +#define EFI_VAR_COMM_HEADER_SIZE 16
> +#define EFI_VAR_ACCESS_NAME_OFFSET 36
> +#define EFI_VAR_NEXT_NAME_OFFSET 24
> +#define EFI_VAR_FN_GET_VARIABLE 1
> +#define EFI_VAR_FN_GET_NEXT_VARIABLE_NAME 2
> +#define EFI_VAR_FN_SET_VARIABLE 3
> +#define EFI_SUCCESS 0ULL
> +#define EFI_INVALID_PARAMETER 0x8000000000000002ULL
> +#define EFI_BUFFER_TOO_SMALL 0x8000000000000005ULL
> +#define EFI_NOT_FOUND 0x800000000000000eULL
> +#define EFI_VARIABLE_NON_VOLATILE 0x00000001
> +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
> +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
> +
> +#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)
> +
> +static uint64_t rpmi_response_base;
> +
> +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)
> +{
> +    uint32_t tail = qtest_readl(qts, RPMI_A2P_TAIL);
> +    uint64_t slot = rpmi_queue_slot(RPMI_SHMEM_BASE, tail);
> +    size_t i;
> +
> +    qtest_writew(qts, slot, service_group);
> +    qtest_writeb(qts, slot + 2, service_id);
> +    qtest_writeb(qts, slot + 3, request_type);
> +    qtest_writew(qts, slot + 4, data_words * sizeof(*data));
> +    qtest_writew(qts, slot + 6, RPMI_TOKEN);
> +
> +    for (i = 0; i < data_words; i++) {
> +        qtest_writel(qts, slot + 8 + i * sizeof(*data), data[i]);
> +    }
> +
> +    g_test_message(
> +        "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,
> +        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);
> +}
> +
> +static 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)
> +{
> +    uint32_t head = qtest_readl(qts, RPMI_P2A_ACK_HEAD);
> +    uint32_t tail = qtest_readl(qts, RPMI_P2A_ACK_TAIL);
> +
> +    g_assert_cmphex(tail, !=, head);
> +    rpmi_response_base = rpmi_queue_slot(RPMI_P2A_ACK_BASE, head);
> +    g_assert_cmphex(qtest_readw(qts, rpmi_response_base), ==, service_group);
> +    g_assert_cmphex(qtest_readb(qts, rpmi_response_base + 2), ==,
> +                    service_id);
> +    g_assert_cmphex(qtest_readb(qts, rpmi_response_base + 3), ==,
> +                    RPMI_MSG_ACKNOWLEDGEMENT);
> +    g_assert_cmphex(qtest_readw(qts, rpmi_response_base + 4), ==, data_len);
> +    g_assert_cmphex(qtest_readw(qts, rpmi_response_base + 6), ==,
> +                    RPMI_TOKEN);
> +    g_test_message(
> +        "RPMI_P2A_ACK shmem=0x%016" PRIx64
> +        " 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,
> +        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)
> +{
> +    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)
> +{
> +    g_autoptr(GError) error = NULL;
> +    g_auto(GStrv) split_args = NULL;
> +    g_autoptr(GPtrArray) argv = g_ptr_array_new();
> +    g_autofree char *stderr_data = NULL;
> +    gint wait_status;
> +    gboolean spawned;
> +
> +    g_assert_true(g_shell_parse_argv(extra_args, NULL, &split_args, &error));
> +    g_assert_no_error(error);
> +
> +    g_ptr_array_add(argv, (gpointer)qtest_qemu_binary(NULL));
> +    for (char **arg = split_args; *arg; arg++) {
> +        g_ptr_array_add(argv, *arg);
> +    }
> +    g_ptr_array_add(argv, NULL);
> +
> +    spawned = g_spawn_sync(NULL, (char **)argv->pdata, NULL,
> +                           G_SPAWN_STDOUT_TO_DEV_NULL,
> +                           NULL, NULL, NULL, &stderr_data,
> +                           &wait_status, &error);
> +    g_assert_true(spawned);
> +    g_assert_no_error(error);
> +    g_assert_false(g_spawn_check_exit_status(wait_status, NULL));
> +    g_assert_nonnull(stderr_data);
> +    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)
> +{
> +    static const char expected[] = "QEMU RISC-V virt RPMI";
> +    QTestState *qts;
> +    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));
> +    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(qtest_readb(qts, RPMI_P2A_ACK_SLOT0 + 16 + i), ==,
> +                        expected[i]);
> +    }
> +
> +    qtest_quit(qts);
> +}
> +
> +static 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,
> +                      RPMI_MSG_NORMAL_REQUEST, &service_group, 1);
> +
> +    rpmi_expect_ack(qts, RPMI_SRVGRP_BASE,
> +                    RPMI_BASE_SRV_PROBE_SERVICE_GROUP,
> +                    2 * sizeof(uint32_t));
> +    g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0);
> +    if (present) {
> +        g_assert_cmphex(rpmi_response_word(qts, 1), !=, 0);
> +    } else {
> +        g_assert_cmphex(rpmi_response_word(qts, 1), ==, 0);
> +    }
> +}
> +
> +static void test_rpmi_base_probe_service_groups(void)
> +{
> +    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);
> +    qtest_system_reset(qts);
> +    rpmi_probe_group(qts, RPMI_SRVGRP_HSM, true);
> +    qtest_system_reset(qts);
> +    rpmi_probe_group(qts, RPMI_SRVGRP_SYSTEM_SUSPEND, true);
> +    qtest_system_reset(qts);
>
IMO, it is better to add these tests along with the commit which
enabled the feature.

> +    rpmi_probe_group(qts, RPMI_SRVGRP_CPPC, false);
> +    qtest_system_reset(qts);
> +    rpmi_probe_group(qts, RPMI_SRVGRP_SYSTEM_MSI, false);
> +    qtest_system_reset(qts);
> +    rpmi_probe_group(qts, RPMI_SRVGRP_CLOCK, false);
> +    qtest_system_reset(qts);
> +    rpmi_probe_group(qts, RPMI_SRVGRP_MANAGEMENT_MODE, false);
> +    qtest_system_reset(qts);
> +    rpmi_probe_group(qts, RPMI_SRVGRP_LOGGING, false);
> +
>
Please remove these unimplemented service groups from this commit.

Thanks,
Sunil

Reply via email to