Re: [PATCH v3 05/10] riscv: add RPMI system reset service
Daniel Henrique Barboza <[email protected]>
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
On 7/14/2026 4:56 AM, Subrahmanya Lingappa wrote: > 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 | 38 ++++++- > hw/misc/riscv_rpmi_internal.h | 9 ++ > hw/misc/riscv_rpmi_sysreset.c | 206 ++++++++++++++++++++++++++++++++++ > hw/riscv/virt.c | 40 +++++++ > include/hw/misc/riscv_rpmi.h | 13 ++- > include/hw/riscv/rpmi-fdt.h | 11 ++ > 7 files changed, 313 insertions(+), 5 deletions(-) > create mode 100644 hw/misc/riscv_rpmi_sysreset.c > > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index e7c2ebe28f..bca7819ee0 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -170,4 +170,5 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) > system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) > system_ss.add(when: 'CONFIG_RISCV_RPMI', if_true: [files( > 'riscv_rpmi.c', > + 'riscv_rpmi_sysreset.c', > ), librpmi]) > diff --git a/hw/misc/riscv_rpmi.c b/hw/misc/riscv_rpmi.c > index c4b115b659..7a3f570ee7 100644 > --- a/hw/misc/riscv_rpmi.c > +++ b/hw/misc/riscv_rpmi.c > @@ -175,6 +175,8 @@ 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->machine_opaque = cfg->machine_opaque; > s->services = cfg->services; > s->service_count = cfg->service_count; > > @@ -211,7 +213,9 @@ static void riscv_rpmi_reset(DeviceState *dev) > > 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); > @@ -247,6 +251,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 +346,15 @@ static bool riscv_rpmi_add_service_group(RiscvRpmiState *s, > const RiscvRpmiServiceConfig *service, > Error **errp) > { > - switch (service->kind) { > - default: > + const RiscvRpmiServiceOps *ops; > + > + ops = riscv_rpmi_service_ops_by_kind(service->kind); > + if (!ops) { > error_setg(errp, "unsupported RPMI service kind %d", service->kind); > 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 447c0acb55..fe09804e69 100644 > --- a/hw/misc/riscv_rpmi_internal.h > +++ b/hw/misc/riscv_rpmi_internal.h > @@ -20,5 +20,14 @@ > extern const struct rpmi_shmem_platform_ops rpmi_shmem_qemu_ops; > bool riscv_rpmi_service_enabled(RiscvRpmiState *s, > RiscvRpmiServiceKind kind); > +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..da6ef4c566 > --- /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(s->machine_opaque); > + } > +} > + > +static void riscv_rpmi_sysreset_shutdown(RiscvRpmiState *s) > +{ > + const RiscvRpmiMachineOps *ops = s->machine_ops; > + > + if (ops && ops->system_shutdown) { > + ops->system_shutdown(s->machine_opaque); > + } > +} > + > +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 e52ccfae1f..273a9aa04f 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -1007,6 +1007,35 @@ static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) > } > > > +static const RiscvRpmiServiceConfig virt_rpmi_services[] = { > + { > + .kind = RISCV_RPMI_SERVICE_SYSRESET, > + .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 *opaque) > +{ > + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); > +} > + > +static void virt_rpmi_system_shutdown(void *opaque) > +{ > + 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) > @@ -1018,8 +1047,12 @@ 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, > + .machine_opaque = s, > .hart_ids = hart_ids, > .hart_count = hart_count, > + .services = virt_rpmi_services, > + .service_count = virt_rpmi_service_count(s), > }; > } > > @@ -1028,6 +1061,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, > @@ -1038,6 +1072,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) > diff --git a/include/hw/misc/riscv_rpmi.h b/include/hw/misc/riscv_rpmi.h > index b5d8a32f9b..d11db87731 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 > > #define TYPE_RISCV_RPMI "riscv-rpmi" > OBJECT_DECLARE_SIMPLE_TYPE(RiscvRpmiState, RISCV_RPMI) > @@ -38,8 +39,13 @@ struct rpmi_transport; > > typedef enum RiscvRpmiServiceKind { > RISCV_RPMI_SERVICE_INVALID = 0, > + RISCV_RPMI_SERVICE_SYSRESET, > } RiscvRpmiServiceKind; > > +typedef struct RiscvRpmiMachineOps { > + void (*system_reset)(void *opaque); > + void (*system_shutdown)(void *opaque); > +} RiscvRpmiMachineOps; > typedef struct RiscvRpmiServiceConfig { > RiscvRpmiServiceKind kind; > const char *node_name; > @@ -56,7 +62,8 @@ typedef struct RiscvRpmiConfig { > uint32_t a2p_req_size; > uint32_t p2a_req_size; > const char *platform_info; > - > + const RiscvRpmiMachineOps *machine_ops; > + void *machine_opaque; > const uint32_t *hart_ids; > uint32_t hart_count; > const RiscvRpmiServiceConfig *services; > @@ -73,7 +80,9 @@ struct RiscvRpmiState { > uint32_t a2p_req_size; > uint32_t p2a_req_size; > char *platform_info; > - > + const RiscvRpmiMachineOps *machine_ops; > + void *machine_opaque; > + 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 f053ae5d57..af0e8a65ab 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