Re: [PATCH v9 05/10] riscv: add RPMI system reset service
Subrahmanya Lingappa <[email protected]>
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <CAPxK-6fkm-yKJM+9GhVeDRBnCxmTfA8bFNw63zghd4NJTqbraA@mail.gmail.com> |
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 > >