From: Subrahmanya Lingappa <[email protected]> Add qtests for the RPMI transport plus Base, system reset, HSM, and system suspend service groups.
Signed-off-by: Subrahmanya Lingappa <[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..ecd5b910c7 --- /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 +#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); + 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); + + qtest_quit(qts); +} + +static void test_rpmi_sysreset_attrs(void) +{ + QTestState *qts; + uint32_t reset_type = RPMI_SYSRST_TYPE_SHUTDOWN; + + qts = qtest_init("-machine virt,rpmi=on"); + rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, + RPMI_MSG_NORMAL_REQUEST, &reset_type, 1); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, 2 * sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + g_assert_cmphex(rpmi_response_word(qts, 1), ==, + RPMI_SYSRST_ATTRS_FLAGS_RESETTYPE); + + qtest_system_reset(qts); + reset_type = RPMI_SYSRST_TYPE_COLD_REBOOT; + rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, + RPMI_MSG_NORMAL_REQUEST, &reset_type, 1); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, 2 * sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + g_assert_cmphex(rpmi_response_word(qts, 1), ==, + RPMI_SYSRST_ATTRS_FLAGS_RESETTYPE); + + qtest_system_reset(qts); + reset_type = RPMI_SYSRST_TYPE_INVALID; + rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, + RPMI_MSG_NORMAL_REQUEST, &reset_type, 1); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, 2 * sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + g_assert_cmphex(rpmi_response_word(qts, 1), ==, 0); + + qtest_quit(qts); +} + +static void test_rpmi_sysreset_shutdown(void) +{ + QTestState *qts; + + qts = qtest_init("-machine virt,rpmi=on"); + 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) +{ + QTestState *qts; + + qts = qtest_init("-machine virt,rpmi=on -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) +{ + QTestState *qts; + + qts = qtest_init("-machine virt,rpmi=on"); + rpmi_send_sysreset(qts, RPMI_SYSRST_TYPE_INVALID, + RPMI_MSG_NORMAL_REQUEST); + + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_SYSTEM_RESET, sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, + RPMI_ERR_INVALID_PARAM); + + qtest_quit(qts); +} + +static void test_rpmi_repeated_reset_after_traffic(void) +{ + QTestState *qts; + uint32_t reset_type = RPMI_SYSRST_TYPE_SHUTDOWN; + + qts = qtest_init("-machine virt,rpmi=on,aia=aplic-imsic"); + for (uint32_t i = 0; i < 5; i++) { + rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, + RPMI_MSG_NORMAL_REQUEST, + &reset_type, 1); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_GET_ATTRIBUTES, + 2 * sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + qtest_system_reset(qts); + } + qtest_quit(qts); +} + +static void test_rpmi_reset_clears_transport(void) +{ + QTestState *qts; + + qts = qtest_init("-machine virt,rpmi=on"); + rpmi_send_sysreset(qts, RPMI_SYSRST_TYPE_INVALID, + RPMI_MSG_NORMAL_REQUEST); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_RESET, + RPMI_SYSRST_SRV_SYSTEM_RESET, sizeof(uint32_t)); + + qtest_system_reset(qts); + + 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); + + qtest_quit(qts); +} + +static void test_rpmi_doorbell_invalid_access(void) +{ + 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); + + qtest_quit(qts); +} + +static void test_rpmi_queue_bounds(void) +{ + QTestState *qts; + + qts = qtest_init("-machine virt,rpmi=on"); + qtest_writel(qts, RPMI_A2P_TAIL, 0x1000); + 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) +{ + QTestState *qts; + QDict *error; + const char *desc; + + qts = qtest_init("-machine virt,rpmi=on -S"); + error = qtest_qmp_assert_failure_ref(qts, + "{ 'execute': 'migrate'," + " 'arguments': { 'uri': 'exec:cat > /dev/null' } }"); + desc = qdict_get_try_str(error, "desc"); + + g_assert_nonnull(desc); + g_assert_nonnull(strstr(desc, "non-migratable device")); + g_assert_nonnull(strstr(desc, "riscv-rpmi")); + + qobject_unref(error); + qtest_quit(qts); +} + +static void test_rpmi_hsm_hart_list(void) +{ + 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); + + qtest_quit(qts); +} + +static void test_rpmi_hsm_multi_socket_hart_list(void) +{ + 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); + + qtest_quit(qts); +} + +static void test_rpmi_hsm_hart_status(void) +{ + QTestState *qts; + uint32_t hart_id = 3; + + qts = qtest_init("-machine virt,rpmi=on -smp 4"); + 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), ==, + RPMI_HSM_HART_STATE_STARTED); + + 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 void test_rpmi_hsm_hart_control(void) +{ + QTestState *qts; + uint32_t hart_id = 1; + uint32_t stop_request[] = { hart_id }; + uint32_t start_request[] = { hart_id, 0x80000000, 0 }; + uint32_t suspend_request[] = { hart_id, 0, 0x80001000, 0 }; + uint32_t start_index = 0; + uint32_t suspend_type = 0; + + qts = qtest_init("-machine virt,rpmi=on -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, + 4 * 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), ==, 1); + g_assert_cmphex(rpmi_response_word(qts, 3), ==, 0); + + qtest_system_reset(qts); + rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_SUSPEND_INFO, + RPMI_MSG_NORMAL_REQUEST, &suspend_type, 1); + rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_GET_SUSPEND_INFO, + 6 * sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + + qtest_system_reset(qts); + rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_HART_STOP, + RPMI_MSG_NORMAL_REQUEST, stop_request, + ARRAY_SIZE(stop_request)); + rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_HART_STOP, + sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + rpmi_expect_hsm_status(qts, hart_id, RPMI_HSM_HART_STATE_STOPPED); + + rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_HART_START, + RPMI_MSG_NORMAL_REQUEST, start_request, + ARRAY_SIZE(start_request)); + rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_HART_START, + sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + rpmi_expect_hsm_status(qts, hart_id, RPMI_HSM_HART_STATE_STARTED); + + rpmi_send_request(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_HART_SUSPEND, + RPMI_MSG_NORMAL_REQUEST, suspend_request, + ARRAY_SIZE(suspend_request)); + rpmi_expect_ack(qts, RPMI_SRVGRP_HSM, RPMI_HSM_SRV_HART_SUSPEND, + sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + rpmi_expect_hsm_status(qts, hart_id, RPMI_HSM_HART_STATE_SUSPENDED); + + qtest_quit(qts); +} + +static void test_rpmi_syssusp_attrs_and_suspend(void) +{ + QTestState *qts; + uint32_t suspend_type = 0; + uint32_t suspend_request[] = { 0, 0, 0x80000000, 0 }; + + qts = qtest_init("-machine virt,rpmi=on -smp 1"); + rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_SUSPEND, + RPMI_SYSSUSP_SRV_GET_ATTRIBUTES, + RPMI_MSG_NORMAL_REQUEST, &suspend_type, 1); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_SUSPEND, + RPMI_SYSSUSP_SRV_GET_ATTRIBUTES, + 2 * sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + g_assert_cmphex(rpmi_response_word(qts, 1), ==, 3); + + qtest_system_reset(qts); + rpmi_send_request(qts, RPMI_SRVGRP_SYSTEM_SUSPEND, + RPMI_SYSSUSP_SRV_SYSTEM_SUSPEND, + RPMI_MSG_NORMAL_REQUEST, suspend_request, + ARRAY_SIZE(suspend_request)); + rpmi_expect_ack(qts, RPMI_SRVGRP_SYSTEM_SUSPEND, + RPMI_SYSSUSP_SRV_SYSTEM_SUSPEND, sizeof(uint32_t)); + g_assert_cmphex(rpmi_response_word(qts, 0), ==, 0); + qtest_qmp_eventwait(qts, "SUSPEND"); + qtest_qmp_assert_success(qts, "{ 'execute': 'system_wakeup' }"); + qtest_qmp_eventwait(qts, "WAKEUP"); + + 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); + } + + return g_test_run(); +} -- 2.43.0
