Re: [PATCH v3 06/10] riscv: add RPMI HSM 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 the RPMI Hart State Management service group for RISC-V virt. > > The service exposes the configured virt hart IDs to librpmi and keeps > per-hart RPMI hardware state in the RPMI device. Start, stop, and > suspend requests update QEMU CPU halted state and resume/kick vCPUs as > needed so firmware can exercise RPMI HSM operations against the emulated > topology. > > Signed-off-by: Subrahmanya Lingappa <[email protected]> > --- Reviewed-by: Daniel Henrique Barboza <[email protected]> > hw/misc/meson.build | 1 + > hw/misc/riscv_rpmi.c | 46 ++++++ > hw/misc/riscv_rpmi_hsm.c | 280 ++++++++++++++++++++++++++++++++++ > hw/misc/riscv_rpmi_internal.h | 4 + > hw/riscv/virt.c | 6 + > include/hw/misc/riscv_rpmi.h | 6 + > 6 files changed, 343 insertions(+) > create mode 100644 hw/misc/riscv_rpmi_hsm.c > > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index bca7819ee0..fc5b11ed57 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -171,4 +171,5 @@ 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', > + 'riscv_rpmi_hsm.c', > ), librpmi]) > diff --git a/hw/misc/riscv_rpmi.c b/hw/misc/riscv_rpmi.c > index 7a3f570ee7..820b1d5d63 100644 > --- a/hw/misc/riscv_rpmi.c > +++ b/hw/misc/riscv_rpmi.c > @@ -171,6 +171,49 @@ static bool riscv_rpmi_transport_indices_valid(RiscvRpmiState *s) > s->a2p_req_size); > } > > +typedef struct RiscvRpmiServiceOps { > + RiscvRpmiServiceKind kind; > + void (*configure)(RiscvRpmiState *s, const RiscvRpmiConfig *cfg); > + bool (*add)(RiscvRpmiState *s, Error **errp); > + void (*remove)(RiscvRpmiState *s); > +} RiscvRpmiServiceOps; > + > +static const RiscvRpmiServiceOps riscv_rpmi_service_ops[] = { > + { > + .kind = RISCV_RPMI_SERVICE_SYSRESET, > + .add = riscv_rpmi_sysreset_add, > + .remove = riscv_rpmi_sysreset_remove, > + }, { > + .kind = RISCV_RPMI_SERVICE_HSM, > + .add = riscv_rpmi_hsm_add, > + .remove = riscv_rpmi_hsm_remove, > + }, > +}; > + > +static const RiscvRpmiServiceOps *riscv_rpmi_service_ops_by_kind( > + RiscvRpmiServiceKind kind) > +{ > + for (uint32_t i = 0; i < ARRAY_SIZE(riscv_rpmi_service_ops); i++) { > + if (riscv_rpmi_service_ops[i].kind == kind) { > + return &riscv_rpmi_service_ops[i]; > + } > + } > + > + return NULL; > +} > + > +static void riscv_rpmi_configure_services(RiscvRpmiState *s, > + const RiscvRpmiConfig *cfg) > +{ > + for (uint32_t i = 0; i < ARRAY_SIZE(riscv_rpmi_service_ops); i++) { > + const RiscvRpmiServiceOps *ops = &riscv_rpmi_service_ops[i]; > + > + if (ops->configure && riscv_rpmi_service_enabled(s, ops->kind)) { > + ops->configure(s, cfg); > + } > + } > +} > + > static void riscv_rpmi_configure_base(RiscvRpmiState *s, > const RiscvRpmiConfig *cfg) > { > @@ -187,6 +230,8 @@ static void riscv_rpmi_configure_base(RiscvRpmiState *s, > cfg->hart_count * sizeof(*cfg->hart_ids)); > } > } > + > + riscv_rpmi_configure_services(s, cfg); > } > > static void riscv_rpmi_init(Object *obj) > @@ -209,6 +254,7 @@ static void riscv_rpmi_reset(DeviceState *dev) > memory_region_set_dirty(&s->shmem, 0, s->shmem_size); > } > > + riscv_rpmi_hsm_reset(s); > } > > static void riscv_rpmi_cleanup(RiscvRpmiState *s) > diff --git a/hw/misc/riscv_rpmi_hsm.c b/hw/misc/riscv_rpmi_hsm.c > new file mode 100644 > index 0000000000..9a9859b10c > --- /dev/null > +++ b/hw/misc/riscv_rpmi_hsm.c > @@ -0,0 +1,280 @@ > +/* > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * RISC-V RPMI HSM service. > + * > + * Copyright (c) 2026 Qualcomm Technologies, Inc. > + * Author: > + * Subrahmanya Lingappa <[email protected]> > + */ > + > +#include "qemu/osdep.h" > +#include "riscv_rpmi_internal.h" > +#include "hw/core/cpu.h" > +#include "librpmi_env.h" > + > +static const struct rpmi_hsm_suspend_type riscv_rpmi_hsm_suspend_types[] = { > + { > + .type = 0, > + .info = { > + .flags = 0, > + .entry_latency_us = 0, > + .exit_latency_us = 0, > + .wakeup_latency_us = 0, > + .min_residency_us = 0, > + }, > + }, > +}; > + > +typedef struct RiscvRpmiHsmStateTransition { > + enum rpmi_hart_hw_state state; > + bool halted; > + bool resume; > +} RiscvRpmiHsmStateTransition; > + > +static const RiscvRpmiHsmStateTransition riscv_rpmi_hsm_state_transitions[] = { > + { > + .state = RPMI_HART_HW_STATE_STARTED, > + .halted = false, > + .resume = true, > + }, { > + .state = RPMI_HART_HW_STATE_STOPPED, > + .halted = true, > + .resume = false, > + }, { > + .state = RPMI_HART_HW_STATE_SUSPENDED, > + .halted = true, > + .resume = false, > + }, > +}; > + > +static const RiscvRpmiHsmStateTransition *riscv_rpmi_hsm_state_transition( > + enum rpmi_hart_hw_state state) > +{ > + for (uint32_t index = 0; > + index < ARRAY_SIZE(riscv_rpmi_hsm_state_transitions); index++) { > + if (riscv_rpmi_hsm_state_transitions[index].state == state) { > + return &riscv_rpmi_hsm_state_transitions[index]; > + } > + } > + > + return NULL; > +} > + > +static CPUState *riscv_rpmi_hart_cpu(RiscvRpmiState *s, uint32_t hart_index) > +{ > + if (hart_index >= s->hart_count || !s->hart_ids) { > + return NULL; > + } > + > + return cpu_by_arch_id(s->hart_ids[hart_index]); > +} > + > +static void riscv_rpmi_hsm_set_hw_state(RiscvRpmiState *s, > + uint32_t hart_index, > + enum rpmi_hart_hw_state state) > +{ > + CPUState *cpu = riscv_rpmi_hart_cpu(s, hart_index); > + const RiscvRpmiHsmStateTransition *transition; > + > + if (hart_index >= s->hart_count || !s->hsm_hw_states) { > + return; > + } > + > + s->hsm_hw_states[hart_index] = state; > + if (!cpu) { > + return; > + } > + > + transition = riscv_rpmi_hsm_state_transition(state); > + if (!transition) { > + return; > + } > + > + cpu->halted = transition->halted; > + if (transition->resume) { > + cpu_resume(cpu); > + } else { > + qemu_cpu_kick(cpu); > + } > +} > + > +static enum rpmi_error riscv_rpmi_hsm_start_prepare( > + void *priv, rpmi_uint32_t hart_index, rpmi_uint64_t start_addr) > +{ > + RiscvRpmiState *s = priv; > + CPUState *cpu = riscv_rpmi_hart_cpu(s, hart_index); > + > + if (!cpu) { > + return RPMI_ERR_INVALID_PARAM; > + } > + > + riscv_rpmi_hsm_set_hw_state(s, hart_index, RPMI_HART_HW_STATE_STARTED); > + return RPMI_SUCCESS; > +} > + > +static void riscv_rpmi_hsm_start_finalize(void *priv, > + rpmi_uint32_t hart_index, > + rpmi_uint64_t start_addr) > +{ > +} > + > +static enum rpmi_error riscv_rpmi_hsm_stop_prepare(void *priv, > + rpmi_uint32_t hart_index) > +{ > + RiscvRpmiState *s = priv; > + > + if (!riscv_rpmi_hart_cpu(s, hart_index)) { > + return RPMI_ERR_INVALID_PARAM; > + } > + > + riscv_rpmi_hsm_set_hw_state(s, hart_index, RPMI_HART_HW_STATE_STOPPED); > + return RPMI_SUCCESS; > +} > + > +static void riscv_rpmi_hsm_stop_finalize(void *priv, rpmi_uint32_t hart_index) > +{ > +} > + > +static enum rpmi_error riscv_rpmi_hsm_suspend_prepare( > + void *priv, rpmi_uint32_t hart_index, > + const struct rpmi_hsm_suspend_type *suspend_type, > + rpmi_uint64_t resume_addr) > +{ > + RiscvRpmiState *s = priv; > + > + if (!suspend_type || !riscv_rpmi_hart_cpu(s, hart_index)) { > + return RPMI_ERR_INVALID_PARAM; > + } > + > + riscv_rpmi_hsm_set_hw_state(s, hart_index, RPMI_HART_HW_STATE_SUSPENDED); > + return RPMI_SUCCESS; > +} > + > +static void riscv_rpmi_hsm_suspend_finalize( > + void *priv, rpmi_uint32_t hart_index, > + const struct rpmi_hsm_suspend_type *suspend_type, > + rpmi_uint64_t resume_addr) > +{ > +} > + > +static enum rpmi_hart_hw_state riscv_rpmi_hsm_get_hw_state( > + void *priv, rpmi_uint32_t hart_index) > +{ > + RiscvRpmiState *s = priv; > + > + if (hart_index >= s->hart_count || !s->hsm_hw_states) { > + return RPMI_HART_HW_STATE_STOPPED; > + } > + > + return s->hsm_hw_states[hart_index]; > +} > + > +static const struct rpmi_hsm_platform_ops riscv_rpmi_hsm_ops = { > + .hart_get_hw_state = riscv_rpmi_hsm_get_hw_state, > + .hart_start_prepare = riscv_rpmi_hsm_start_prepare, > + .hart_start_finalize = riscv_rpmi_hsm_start_finalize, > + .hart_stop_prepare = riscv_rpmi_hsm_stop_prepare, > + .hart_stop_finalize = riscv_rpmi_hsm_stop_finalize, > + .hart_suspend_prepare = riscv_rpmi_hsm_suspend_prepare, > + .hart_suspend_finalize = riscv_rpmi_hsm_suspend_finalize, > +}; > + > +static bool riscv_rpmi_hsm_create(RiscvRpmiState *s, > + struct rpmi_service_group **group, > + Error **errp) > +{ > + struct rpmi_hsm *hsm; > + > + if (!s->hart_count || !s->hart_ids) { > + error_setg(errp, "RPMI HSM service requires hart IDs"); > + return false; > + } > + > + s->hsm_hw_states = g_new0(uint32_t, s->hart_count); > + for (uint32_t i = 0; i < s->hart_count; i++) { > + s->hsm_hw_states[i] = RPMI_HART_HW_STATE_STARTED; > + } > + > + hsm = rpmi_hsm_create(s->hart_count, s->hart_ids, > + ARRAY_SIZE(riscv_rpmi_hsm_suspend_types), > + riscv_rpmi_hsm_suspend_types, > + &riscv_rpmi_hsm_ops, s); > + if (!hsm) { > + g_clear_pointer(&s->hsm_hw_states, g_free); > + error_setg(errp, "failed to create RPMI HSM context"); > + return false; > + } > + > + *group = rpmi_service_group_hsm_create(hsm); > + if (!*group) { > + rpmi_hsm_destroy(hsm); > + g_clear_pointer(&s->hsm_hw_states, g_free); > + error_setg(errp, "failed to create RPMI HSM service group"); > + return false; > + } > + > + s->hsm = hsm; > + return true; > +} > + > +static void riscv_rpmi_hsm_destroy(RiscvRpmiState *s) > +{ > + if (s->hsm_group) { > + rpmi_env_free_lock(s->hsm_group->lock); > + s->hsm_group->lock = NULL; > + rpmi_service_group_hsm_destroy(s->hsm_group); > + s->hsm_group = NULL; > + } > + > + if (s->hsm) { > + rpmi_hsm_destroy(s->hsm); > + s->hsm = NULL; > + } > + > + g_clear_pointer(&s->hsm_hw_states, g_free); > +} > + > + > +bool riscv_rpmi_hsm_add(RiscvRpmiState *s, Error **errp) > +{ > + struct rpmi_service_group *group; > + > + if (s->hsm_group) { > + error_setg(errp, "duplicate RPMI HSM service descriptor"); > + return false; > + } > + > + if (!riscv_rpmi_hsm_create(s, &group, errp)) { > + return false; > + } > + > + if (!riscv_rpmi_context_add_group(s, group, "HSM", errp)) { > + s->hsm_group = group; > + riscv_rpmi_hsm_destroy(s); > + return false; > + } > + > + s->hsm_group = group; > + return true; > +} > + > +void riscv_rpmi_hsm_remove(RiscvRpmiState *s) > +{ > + riscv_rpmi_context_remove_group(s, s->hsm_group); > + riscv_rpmi_hsm_destroy(s); > +} > + > +void riscv_rpmi_hsm_reset(RiscvRpmiState *s) > +{ > + if (!s->hsm_hw_states) { > + return; > + } > + > + for (uint32_t i = 0; i < s->hart_count; i++) { > + s->hsm_hw_states[i] = RPMI_HART_HW_STATE_STARTED; > + } > + if (s->hsm) { > + rpmi_hsm_process_state_changes(s->hsm); > + } > +} > diff --git a/hw/misc/riscv_rpmi_internal.h b/hw/misc/riscv_rpmi_internal.h > index fe09804e69..42a6708907 100644 > --- a/hw/misc/riscv_rpmi_internal.h > +++ b/hw/misc/riscv_rpmi_internal.h > @@ -30,4 +30,8 @@ void riscv_rpmi_context_remove_group(RiscvRpmiState *s, > bool riscv_rpmi_sysreset_add(RiscvRpmiState *s, Error **errp); > void riscv_rpmi_sysreset_remove(RiscvRpmiState *s); > > +bool riscv_rpmi_hsm_add(RiscvRpmiState *s, Error **errp); > +void riscv_rpmi_hsm_remove(RiscvRpmiState *s); > +void riscv_rpmi_hsm_reset(RiscvRpmiState *s); > + > #endif > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > index 273a9aa04f..2b5f367bdb 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -1014,6 +1014,12 @@ static const RiscvRpmiServiceConfig virt_rpmi_services[] = { > .compatible = "riscv,rpmi-system-reset", > .service_group = RISCV_RPMI_SRVGRP_SYSTEM_RESET, > }, > + { > + .kind = RISCV_RPMI_SERVICE_HSM, > + .node_name = "hsm", > + .compatible = "riscv,rpmi-hsm", > + .service_group = RISCV_RPMI_SRVGRP_HSM, > + }, > }; > > static uint32_t virt_rpmi_service_count(RISCVVirtState *s) > diff --git a/include/hw/misc/riscv_rpmi.h b/include/hw/misc/riscv_rpmi.h > index d11db87731..37c56ea2e8 100644 > --- a/include/hw/misc/riscv_rpmi.h > +++ b/include/hw/misc/riscv_rpmi.h > @@ -28,6 +28,7 @@ > #define VIRT_RPMI_P2A_REQ_SIZE 0 > > #define RISCV_RPMI_SRVGRP_SYSTEM_RESET 3 > +#define RISCV_RPMI_SRVGRP_HSM 5 > > #define TYPE_RISCV_RPMI "riscv-rpmi" > OBJECT_DECLARE_SIMPLE_TYPE(RiscvRpmiState, RISCV_RPMI) > @@ -36,10 +37,12 @@ struct rpmi_context; > struct rpmi_service_group; > struct rpmi_shmem; > struct rpmi_transport; > +struct rpmi_hsm; > > typedef enum RiscvRpmiServiceKind { > RISCV_RPMI_SERVICE_INVALID = 0, > RISCV_RPMI_SERVICE_SYSRESET, > + RISCV_RPMI_SERVICE_HSM, > } RiscvRpmiServiceKind; > > typedef struct RiscvRpmiMachineOps { > @@ -83,6 +86,9 @@ struct RiscvRpmiState { > const RiscvRpmiMachineOps *machine_ops; > void *machine_opaque; > struct rpmi_service_group *sysreset_group; > + struct rpmi_hsm *hsm; > + struct rpmi_service_group *hsm_group; > + uint32_t *hsm_hw_states; > uint32_t *hart_ids; > uint32_t hart_count; > const RiscvRpmiServiceConfig *services;