Ranbir,
On Tue, Aug 18, 2026 at 11:36 AM Ranbir Singh <[email protected]> wrote:
>
>
>
>
> ________________________________________
> From: [email protected]
> <[email protected]> on behalf of
> Subrahmanya Lingappa <[email protected]>
> Sent: 17 August 2026 16:24
> To: [email protected]
> Cc: [email protected]; Subrahmanya Lingappa; Daniel Henrique Barboza;
> Palmer Dabbelt; Alistair Francis; Weiwei Li; Liu Zhiwei; Chao Liu
> Subject: [PATCH v9 05/10] riscv: add RPMI system reset service
>
> Add a QEMU implementation of the RPMI System Reset service group.
>
> The service advertises shutdown and cold reboot reset types, validates
> guest requests, and dispatches accepted requests through the machine
> reset and shutdown callbacks. This lets firmware use RPMI reset commands
> while keeping the board-specific reset policy in the RISC-V virt machine.
>
> Signed-off-by: Subrahmanya Lingappa <[email protected]>
> Reviewed-by: Daniel Henrique Barboza <[email protected]>
> ---
> hw/misc/meson.build | 1 +
> hw/misc/riscv_rpmi.c | 70 +++++++++++-
> hw/misc/riscv_rpmi_internal.h | 12 +-
> hw/misc/riscv_rpmi_sysreset.c | 206 ++++++++++++++++++++++++++++++++++
> hw/riscv/virt.c | 51 ++++++++-
> include/hw/misc/riscv_rpmi.h | 15 +--
> include/hw/riscv/rpmi-fdt.h | 11 ++
> 7 files changed, 349 insertions(+), 17 deletions(-)
> create mode 100644 hw/misc/riscv_rpmi_sysreset.c
>
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index fb761f94b3..f8a317b8f6 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -173,4 +173,5 @@ system_ss.add(when: 'CONFIG_LASI', if_true:
> files('lasi.c'))
> system_ss.add(when: 'CONFIG_AXIADO_CLK', if_true: files('axiado_clk.c'))
> system_ss.add(when: 'CONFIG_RISCV_RPMI', if_true: [files(
> 'riscv_rpmi.c',
> + 'riscv_rpmi_sysreset.c',
> ), librpmi], if_false: files('riscv_rpmi-stub.c'))
> diff --git a/hw/misc/riscv_rpmi.c b/hw/misc/riscv_rpmi.c
> index 8598a3f133..97517766e9 100644
> --- a/hw/misc/riscv_rpmi.c
> +++ b/hw/misc/riscv_rpmi.c
> @@ -171,10 +171,37 @@ static bool
> riscv_rpmi_transport_indices_valid(RiscvRpmiState *s)
> s->a2p_req_size);
> }
>
> +typedef struct RiscvRpmiServiceOps {
> + uint32_t service_group;
> + bool (*add)(RiscvRpmiState *s, Error **errp);
> + void (*remove)(RiscvRpmiState *s);
> +} RiscvRpmiServiceOps;
> +
> +static const RiscvRpmiServiceOps riscv_rpmi_service_ops[] = {
> + {
> + .service_group = RISCV_RPMI_SRVGRP_SYSTEM_RESET,
> + .add = riscv_rpmi_sysreset_add,
> + .remove = riscv_rpmi_sysreset_remove,
> + },
> +};
> +
> +static const RiscvRpmiServiceOps *riscv_rpmi_service_ops_by_group(
> + uint32_t service_group)
> +{
> + for (uint32_t i = 0; i < ARRAY_SIZE(riscv_rpmi_service_ops); i++) {
> + if (riscv_rpmi_service_ops[i].service_group == service_group) {
> + return &riscv_rpmi_service_ops[i];
> + }
> + }
> +
> + return NULL;
> +}
> +
> static void riscv_rpmi_configure_base(RiscvRpmiState *s,
> const RiscvRpmiConfig *cfg)
> {
> s->platform_info = g_strdup(cfg->platform_info);
> + s->machine_ops = cfg->machine_ops;
> s->services = cfg->services;
> s->service_count = cfg->service_count;
>
> @@ -211,7 +238,9 @@ static void riscv_rpmi_reset_hold(Object *obj, ResetType
> type)
>
> static void riscv_rpmi_cleanup(RiscvRpmiState *s)
> {
> -
> + for (uint32_t i = ARRAY_SIZE(riscv_rpmi_service_ops); i > 0; i--) {
> + riscv_rpmi_service_ops[i - 1].remove(s);
> + }
>
> if (s->context) {
> rpmi_context_destroy(s->context);
> @@ -234,12 +263,12 @@ static void riscv_rpmi_cleanup(RiscvRpmiState *s)
> }
> }
>
> -bool riscv_rpmi_service_enabled(RiscvRpmiState *s, RiscvRpmiServiceKind kind)
> +bool riscv_rpmi_service_enabled(RiscvRpmiState *s, uint32_t service_group)
> {
> uint32_t i;
>
> for (i = 0; i < s->service_count; i++) {
> - if (s->services[i].kind == kind) {
> + if (s->services[i].service_group == service_group) {
> return true;
> }
> }
> @@ -247,6 +276,30 @@ bool riscv_rpmi_service_enabled(RiscvRpmiState *s,
> RiscvRpmiServiceKind kind)
> return false;
> }
>
> +bool riscv_rpmi_context_add_group(RiscvRpmiState *s,
> + struct rpmi_service_group *group,
> + const char *name,
> + Error **errp)
> +{
> + enum rpmi_error rc;
> +
> + rc = rpmi_context_add_group(s->context, group);
> + if (rc != RPMI_SUCCESS) {
> + error_setg(errp, "failed to add RPMI %s service group: %d", name,
> rc);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +void riscv_rpmi_context_remove_group(RiscvRpmiState *s,
> + struct rpmi_service_group *group)
> +{
> + if (s->context && group) {
> + rpmi_context_remove_group(s->context, group);
> + }
> +}
> +
> static bool riscv_rpmi_validate_config(RiscvRpmiState *s, Error **errp)
> {
> uint64_t queue_bytes;
> @@ -318,11 +371,16 @@ static bool riscv_rpmi_add_service_group(RiscvRpmiState
> *s,
> const RiscvRpmiServiceConfig
> *service,
> Error **errp)
> {
> - switch (service->kind) {
> - default:
> - error_setg(errp, "unsupported RPMI service kind %d", service->kind);
> + const RiscvRpmiServiceOps *ops;
> +
> + ops = riscv_rpmi_service_ops_by_group(service->service_group);
> + if (!ops) {
> + error_setg(errp, "unsupported RPMI service group %u",
> + service->service_group);
> return false;
> }
> +
> + return ops->add(s, errp);
> }
>
> static bool riscv_rpmi_init_services(RiscvRpmiState *s, Error **errp)
> diff --git a/hw/misc/riscv_rpmi_internal.h b/hw/misc/riscv_rpmi_internal.h
> index c263033634..ca9838d256 100644
> --- a/hw/misc/riscv_rpmi_internal.h
> +++ b/hw/misc/riscv_rpmi_internal.h
> @@ -18,7 +18,15 @@
> #define RPMI_PLAT_INFO "QEMU RISC-V RPMI"
>
> extern const struct rpmi_shmem_platform_ops rpmi_shmem_qemu_ops;
> -bool riscv_rpmi_service_enabled(RiscvRpmiState *s,
> - RiscvRpmiServiceKind kind);
> +bool riscv_rpmi_service_enabled(RiscvRpmiState *s, uint32_t service_group);
> +bool riscv_rpmi_context_add_group(RiscvRpmiState *s,
> + struct rpmi_service_group *group,
> + const char *name,
> + Error **errp);
> +void riscv_rpmi_context_remove_group(RiscvRpmiState *s,
> + struct rpmi_service_group *group);
> +
> +bool riscv_rpmi_sysreset_add(RiscvRpmiState *s, Error **errp);
> +void riscv_rpmi_sysreset_remove(RiscvRpmiState *s);
>
> #endif
> diff --git a/hw/misc/riscv_rpmi_sysreset.c b/hw/misc/riscv_rpmi_sysreset.c
> new file mode 100644
> index 0000000000..775eb5483f
> --- /dev/null
> +++ b/hw/misc/riscv_rpmi_sysreset.c
> @@ -0,0 +1,206 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * RISC-V RPMI System Reset service.
> + *
> + * Copyright (c) 2026 Qualcomm Technologies, Inc.
> + * Author:
> + * Subrahmanya Lingappa <[email protected]>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "riscv_rpmi_internal.h"
> +#include "qemu/log.h"
> +#include "librpmi_env.h"
> +
> +typedef struct RiscvRpmiSysresetType {
> + uint32_t type;
> + void (*action)(RiscvRpmiState *s);
> +} RiscvRpmiSysresetType;
> +
> +typedef struct RiscvRpmiSysresetGroup {
> + struct rpmi_service_group group;
> + struct rpmi_service services[RPMI_SYSRST_SRV_ID_MAX];
> + RiscvRpmiState *rpmi;
> +} RiscvRpmiSysresetGroup;
> +
> +static void riscv_rpmi_sysreset_reboot(RiscvRpmiState *s)
> +{
> + const RiscvRpmiMachineOps *ops = s->machine_ops;
> +
> + if (ops && ops->system_reset) {
> + ops->system_reset();
> + }
> +}
> +
> +static void riscv_rpmi_sysreset_shutdown(RiscvRpmiState *s)
> +{
> + const RiscvRpmiMachineOps *ops = s->machine_ops;
> +
> + if (ops && ops->system_shutdown) {
> + ops->system_shutdown();
> + }
> +}
> +
> +static const RiscvRpmiSysresetType riscv_rpmi_sysreset_types[] = {
> + {
> + .type = RPMI_SYSRST_TYPE_SHUTDOWN,
> + .action = riscv_rpmi_sysreset_shutdown,
> + }, {
> + .type = RPMI_SYSRST_TYPE_COLD_REBOOT,
> + .action = riscv_rpmi_sysreset_reboot,
> + },
> +};
> +
> +static const RiscvRpmiSysresetType *riscv_rpmi_sysreset_type_by_id(
> + uint32_t reset_type)
> +{
> + for (uint32_t index = 0; index < ARRAY_SIZE(riscv_rpmi_sysreset_types);
> + index++) {
> + if (riscv_rpmi_sysreset_types[index].type == reset_type) {
> + return &riscv_rpmi_sysreset_types[index];
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static void riscv_rpmi_do_system_reset(RiscvRpmiState *s,
> + rpmi_uint32_t reset_type)
> +{
> + const RiscvRpmiSysresetType *type;
> +
> + type = riscv_rpmi_sysreset_type_by_id(reset_type);
> + if (type) {
> + type->action(s);
> + return;
> + }
> +
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: unsupported reset type %u\n",
> + __func__, reset_type);
> +}
> +
> +static enum rpmi_error riscv_rpmi_sysreset_get_attributes(
> + struct rpmi_service_group *group, struct rpmi_service *service,
> + struct rpmi_transport *trans, rpmi_uint16_t request_data_len,
> + const rpmi_uint8_t *request_data, rpmi_uint16_t *response_data_len,
> + rpmi_uint8_t *response_data)
> +{
> + uint32_t reset_type = ldl_le_p(request_data);
> + uint32_t *resp = (uint32_t *)response_data;
> +
> + *response_data_len = 2 * sizeof(*resp);
> + stl_le_p(&resp[0], RPMI_SUCCESS);
> + stl_le_p(&resp[1], riscv_rpmi_sysreset_type_by_id(reset_type) ?
> + RPMI_SYSRST_ATTRS_FLAGS_RESETTYPE : 0);
> +
> + return RPMI_SUCCESS;
> +}
> +
> +static enum rpmi_error riscv_rpmi_sysreset_do_reset(
> + struct rpmi_service_group *group, struct rpmi_service *service,
> + struct rpmi_transport *trans, rpmi_uint16_t request_data_len,
> + const rpmi_uint8_t *request_data, rpmi_uint16_t *response_data_len,
> + rpmi_uint8_t *response_data)
> +{
> + RiscvRpmiSysresetGroup *sysreset = group->priv;
> + uint32_t reset_type = ldl_le_p(request_data);
> + uint32_t *resp = (uint32_t *)response_data;
> +
> + *response_data_len = sizeof(*resp);
> +
> + if (!riscv_rpmi_sysreset_type_by_id(reset_type)) {
> + stl_le_p(resp, (uint32_t)RPMI_ERR_INVALID_PARAM);
> + return RPMI_SUCCESS;
> + }
> +
> + stl_le_p(resp, RPMI_SUCCESS);
> + riscv_rpmi_do_system_reset(sysreset->rpmi, reset_type);
> + return RPMI_SUCCESS;
> +}
> +
> +static const struct rpmi_service riscv_rpmi_sysreset_services[] = {
> + [RPMI_SYSRST_SRV_ENABLE_NOTIFICATION] = {
> + .service_id = RPMI_SYSRST_SRV_ENABLE_NOTIFICATION,
> + .min_a2p_request_datalen = 8,
> + },
> + [RPMI_SYSRST_SRV_GET_ATTRIBUTES] = {
> + .service_id = RPMI_SYSRST_SRV_GET_ATTRIBUTES,
> + .min_a2p_request_datalen = 4,
> + .process_a2p_request = riscv_rpmi_sysreset_get_attributes,
> + },
> + [RPMI_SYSRST_SRV_SYSTEM_RESET] = {
> + .service_id = RPMI_SYSRST_SRV_SYSTEM_RESET,
> + .min_a2p_request_datalen = 4,
> + .process_a2p_request = riscv_rpmi_sysreset_do_reset,
> + },
> +};
> +
> +static struct rpmi_service_group *riscv_rpmi_sysreset_create(RiscvRpmiState
> *s)
> +{
> + RiscvRpmiSysresetGroup *sysreset;
> + struct rpmi_service_group *group;
> +
> + sysreset = g_new0(RiscvRpmiSysresetGroup, 1);
> + sysreset->rpmi = s;
> + memcpy(sysreset->services, riscv_rpmi_sysreset_services,
> + sizeof(riscv_rpmi_sysreset_services));
> +
> + group = &sysreset->group;
> + group->name = "sysreset";
> + group->servicegroup_id = RPMI_SRVGRP_SYSTEM_RESET;
> + group->max_service_id = RPMI_SYSRST_SRV_ID_MAX;
> + group->servicegroup_version =
> + RPMI_BASE_VERSION(RPMI_SPEC_VERSION_MAJOR, RPMI_SPEC_VERSION_MINOR);
> + group->privilege_level_bitmap = RPMI_PRIVILEGE_M_MODE_MASK;
> + group->services = sysreset->services;
> + group->lock = rpmi_env_alloc_lock();
> + group->priv = sysreset;
> +
> + return group;
> +}
> +
> +static void riscv_rpmi_sysreset_destroy(struct rpmi_service_group *group)
> +{
> + if (!group) {
> + return;
> + }
> +
> + rpmi_env_free_lock(group->lock);
> + g_free(group->priv);
> +}
> +
> +bool riscv_rpmi_sysreset_add(RiscvRpmiState *s, Error **errp)
> +{
> + struct rpmi_service_group *group;
> +
> + if (s->sysreset_group) {
> + error_setg(errp, "duplicate RPMI sysreset service descriptor");
> + return false;
> + }
> +
> + group = riscv_rpmi_sysreset_create(s);
> + if (!group) {
> + error_setg(errp, "failed to create RPMI sysreset service group");
> + return false;
> + }
> +
> + if (!riscv_rpmi_context_add_group(s, group, "sysreset", errp)) {
> + riscv_rpmi_sysreset_destroy(group);
> + return false;
> + }
> +
> + s->sysreset_group = group;
> + return true;
> +}
> +
> +void riscv_rpmi_sysreset_remove(RiscvRpmiState *s)
> +{
> + if (!s->sysreset_group) {
> + return;
> + }
> +
> + riscv_rpmi_context_remove_group(s, s->sysreset_group);
> + riscv_rpmi_sysreset_destroy(s->sysreset_group);
> + s->sysreset_group = NULL;
> +}
> diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> index fa0e0db0c3..1a69fd7cfe 100644
> --- a/hw/riscv/virt.c
> +++ b/hw/riscv/virt.c
> @@ -767,7 +767,8 @@ static void create_fdt_pcie(RISCVVirtState *s,
> create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle);
> }
>
> -static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle)
> +static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle,
> + bool use_syscon_reset)
> {
> char *name;
> uint32_t test_phandle;
> @@ -791,6 +792,14 @@ static void create_fdt_reset(RISCVVirtState *s, uint32_t
> *phandle)
> test_phandle = qemu_fdt_get_phandle(ms->fdt, name);
> g_free(name);
>
> + /*
> + * When RPMI is enabled, advertise reset and shutdown through RPMI so
> + * firmware routes these operations through the RPMI system reset
> service.
> + */
> + if (!use_syscon_reset) {
> + return;
> + }
> +
> name = g_strdup_printf("/reboot");
> qemu_fdt_add_subnode(ms->fdt, name);
> qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot");
> @@ -980,6 +989,34 @@ static void create_fdt_iommu(RISCVVirtState *s, uint16_t
> bdf)
> }
>
>
> +static const RiscvRpmiServiceConfig virt_rpmi_services[] = {
> + {
> + .node_name = "sysreset",
> + .compatible = "riscv,rpmi-system-reset",
> + .service_group = RISCV_RPMI_SRVGRP_SYSTEM_RESET,
> + },
> +};
> +
> +static uint32_t virt_rpmi_service_count(RISCVVirtState *s)
> +{
> + return ARRAY_SIZE(virt_rpmi_services);
> +}
> +
> +static void virt_rpmi_system_reset(void)
> +{
> + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> +}
> +
> +static void virt_rpmi_system_shutdown(void)
> +{
> + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
> +}
> +
> +static const RiscvRpmiMachineOps virt_rpmi_machine_ops = {
> + .system_reset = virt_rpmi_system_reset,
> + .system_shutdown = virt_rpmi_system_shutdown,
> +};
> +
> static RiscvRpmiConfig virt_rpmi_config(RISCVVirtState *s,
> const uint32_t *hart_ids,
> uint32_t hart_count)
> @@ -991,8 +1028,11 @@ static RiscvRpmiConfig virt_rpmi_config(RISCVVirtState
> *s,
> .a2p_req_size = VIRT_RPMI_A2P_REQ_SIZE,
> .p2a_req_size = VIRT_RPMI_P2A_REQ_SIZE,
> .platform_info = "QEMU RISC-V virt RPMI",
> + .machine_ops = &virt_rpmi_machine_ops,
> .hart_ids = hart_ids,
> .hart_count = hart_count,
> + .services = virt_rpmi_services,
> + .service_count = virt_rpmi_service_count(s),
> };
> }
>
> @@ -1001,6 +1041,7 @@ static void create_fdt_rpmi(RISCVVirtState *s, uint32_t
> *phandle,
> {
> RiscvRpmiConfig rpmi_cfg = virt_rpmi_config(s, NULL, 0);
> uint32_t rpmi_mbox_handle;
> + uint32_t i;
> RiscvRpmiFdtMboxConfig cfg = {
> .shmem_base = rpmi_cfg.shmem_base,
> .doorbell_base = rpmi_cfg.doorbell_base,
> @@ -1011,6 +1052,12 @@ static void create_fdt_rpmi(RISCVVirtState *s,
> uint32_t *phandle,
>
> riscv_rpmi_fdt_add_mbox(MACHINE(s)->fdt, &cfg, phandle,
> &rpmi_mbox_handle);
> +
> + for (i = 0; i < rpmi_cfg.service_count; i++) {
> + riscv_rpmi_fdt_add_service_node(MACHINE(s)->fdt, rpmi_cfg.shmem_base,
> + &rpmi_cfg.services[i],
> + rpmi_mbox_handle);
> + }
> }
>
> static void finalize_fdt(RISCVVirtState *s)
> @@ -1036,7 +1083,7 @@ static void finalize_fdt(RISCVVirtState *s)
> create_fdt_rpmi(s, &phandle, msi_pcie_phandle);
> }
>
> - create_fdt_reset(s, &phandle);
> + create_fdt_reset(s, &phandle, !s->have_rpmi);
>
> create_fdt_uart(s, irq_mmio_phandle);
>
> diff --git a/include/hw/misc/riscv_rpmi.h b/include/hw/misc/riscv_rpmi.h
> index f5fd1ee8c7..a9ccc950e9 100644
> --- a/include/hw/misc/riscv_rpmi.h
> +++ b/include/hw/misc/riscv_rpmi.h
> @@ -27,6 +27,7 @@
> #define VIRT_RPMI_A2P_REQ_SIZE (16 * RPMI_QUEUE_SLOT_SIZE)
> #define VIRT_RPMI_P2A_REQ_SIZE 0
>
> +#define RISCV_RPMI_SRVGRP_SYSTEM_RESET 3
>
> Can't we replace the use of these macros with the enum rpmi_servicegroup_id
> for service_group field in struct RiscvRpmiServiceOps so that there is no
> need to keep adding such #define's for various service groups which
> essentially have the same values as that in enum rpmi_servicegroup_id for
> that group?
yes local defines duplicate enum rpmi_servicegroup_id, and the next
version will use librpmi’s enum/values directly.
Thanks
Subbu
>
> #define TYPE_RISCV_RPMI "riscv-rpmi"
> OBJECT_DECLARE_SIMPLE_TYPE(RiscvRpmiState, RISCV_RPMI)
> @@ -36,12 +37,11 @@ struct rpmi_service_group;
> struct rpmi_shmem;
> struct rpmi_transport;
>
> -typedef enum RiscvRpmiServiceKind {
> - RISCV_RPMI_SERVICE_INVALID = 0,
> -} RiscvRpmiServiceKind;
> -
> +typedef struct RiscvRpmiMachineOps {
> + void (*system_reset)(void);
> + void (*system_shutdown)(void);
> +} RiscvRpmiMachineOps;
> typedef struct RiscvRpmiServiceConfig {
> - RiscvRpmiServiceKind kind;
> const char *node_name;
> const char *compatible;
> uint32_t service_group;
> @@ -56,7 +56,7 @@ typedef struct RiscvRpmiConfig {
> uint32_t a2p_req_size;
> uint32_t p2a_req_size;
> const char *platform_info;
> -
> + const RiscvRpmiMachineOps *machine_ops;
> const uint32_t *hart_ids;
> uint32_t hart_count;
> const RiscvRpmiServiceConfig *services;
> @@ -73,7 +73,8 @@ struct RiscvRpmiState {
> uint32_t a2p_req_size;
> uint32_t p2a_req_size;
> char *platform_info;
> -
> + const RiscvRpmiMachineOps *machine_ops;
> + struct rpmi_service_group *sysreset_group;
> uint32_t *hart_ids;
> uint32_t hart_count;
> const RiscvRpmiServiceConfig *services;
> diff --git a/include/hw/riscv/rpmi-fdt.h b/include/hw/riscv/rpmi-fdt.h
> index b157bda1d1..60c5df75d0 100644
> --- a/include/hw/riscv/rpmi-fdt.h
> +++ b/include/hw/riscv/rpmi-fdt.h
> @@ -26,5 +26,16 @@ void riscv_rpmi_fdt_add_mbox(void *fdt,
> const RiscvRpmiFdtMboxConfig *cfg,
> uint32_t *phandle,
> uint32_t *mbox_handle);
> +void riscv_rpmi_fdt_add_service(void *fdt, hwaddr shmem_base,
> + const char *node_name,
> + const char *compatible,
> + uint32_t mbox_handle,
> + uint32_t service_group,
> + bool has_mpxy_channel,
> + uint32_t mpxy_channel);
> +
> +void riscv_rpmi_fdt_add_service_node(void *fdt, hwaddr shmem_base,
> + const RiscvRpmiServiceConfig *service,
> + uint32_t mbox_handle);
>
> #endif
> --
> 2.43.0
>
>