Re: [PATCH v3 07/10] riscv: add RPMI system suspend 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 System Suspend service group for RISC-V virt. > > The service advertises suspend-to-RAM support through librpmi, > registers QEMU wakeup support, and routes the suspend finalize callback > to the machine suspend path. On wakeup it processes RPMI group events > and defers the HSM hart resume until vCPUs are ready to run again. > > Signed-off-by: Subrahmanya Lingappa <[email protected]> > --- Reviewed-by: Daniel Henrique Barboza <[email protected]> > hw/misc/meson.build | 1 + > hw/misc/riscv_rpmi.c | 4 + > hw/misc/riscv_rpmi_hsm.c | 9 +- > hw/misc/riscv_rpmi_internal.h | 5 + > hw/misc/riscv_rpmi_syssusp.c | 191 ++++++++++++++++++++++++++++++++++ > hw/riscv/virt.c | 25 +++++ > include/hw/misc/riscv_rpmi.h | 11 ++ > 7 files changed, 245 insertions(+), 1 deletion(-) > create mode 100644 hw/misc/riscv_rpmi_syssusp.c > > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index fc5b11ed57..cb093a8642 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -172,4 +172,5 @@ system_ss.add(when: 'CONFIG_RISCV_RPMI', if_true: [files( > 'riscv_rpmi.c', > 'riscv_rpmi_sysreset.c', > 'riscv_rpmi_hsm.c', > + 'riscv_rpmi_syssusp.c', > ), librpmi]) > diff --git a/hw/misc/riscv_rpmi.c b/hw/misc/riscv_rpmi.c > index 820b1d5d63..08691861b0 100644 > --- a/hw/misc/riscv_rpmi.c > +++ b/hw/misc/riscv_rpmi.c > @@ -187,6 +187,10 @@ static const RiscvRpmiServiceOps riscv_rpmi_service_ops[] = { > .kind = RISCV_RPMI_SERVICE_HSM, > .add = riscv_rpmi_hsm_add, > .remove = riscv_rpmi_hsm_remove, > + }, { > + .kind = RISCV_RPMI_SERVICE_SYSSUSP, > + .add = riscv_rpmi_syssusp_add, > + .remove = riscv_rpmi_syssusp_remove, > }, > }; > > diff --git a/hw/misc/riscv_rpmi_hsm.c b/hw/misc/riscv_rpmi_hsm.c > index 9a9859b10c..bacdabcac6 100644 > --- a/hw/misc/riscv_rpmi_hsm.c > +++ b/hw/misc/riscv_rpmi_hsm.c > @@ -235,7 +235,6 @@ static void riscv_rpmi_hsm_destroy(RiscvRpmiState *s) > g_clear_pointer(&s->hsm_hw_states, g_free); > } > > - > bool riscv_rpmi_hsm_add(RiscvRpmiState *s, Error **errp) > { > struct rpmi_service_group *group; > @@ -278,3 +277,11 @@ void riscv_rpmi_hsm_reset(RiscvRpmiState *s) > rpmi_hsm_process_state_changes(s->hsm); > } > } > + > +void riscv_rpmi_hsm_resume(RiscvRpmiState *s, uint32_t hart_index) > +{ > + riscv_rpmi_hsm_set_hw_state(s, hart_index, 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 42a6708907..34cab35c32 100644 > --- a/hw/misc/riscv_rpmi_internal.h > +++ b/hw/misc/riscv_rpmi_internal.h > @@ -33,5 +33,10 @@ 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); > +void riscv_rpmi_hsm_resume(RiscvRpmiState *s, uint32_t hart_index); > + > +bool riscv_rpmi_syssusp_add(RiscvRpmiState *s, Error **errp); > +void riscv_rpmi_syssusp_remove(RiscvRpmiState *s); > + > > #endif > diff --git a/hw/misc/riscv_rpmi_syssusp.c b/hw/misc/riscv_rpmi_syssusp.c > new file mode 100644 > index 0000000000..d455e08163 > --- /dev/null > +++ b/hw/misc/riscv_rpmi_syssusp.c > @@ -0,0 +1,191 @@ > +/* > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * RISC-V RPMI System Suspend service. > + * > + * Copyright (c) 2026 Qualcomm Technologies, Inc. > + * Author: > + * Subrahmanya Lingappa <[email protected]> > + */ > + > +#include "qemu/osdep.h" > +#include "riscv_rpmi_internal.h" > +#include "qemu/timer.h" > +#include "system/runstate.h" > + > +static const struct rpmi_system_suspend_type riscv_rpmi_syssusp_types[] = { > + { > + .type = RPMI_SYSSUSP_TYPE_SUSPEND_TO_RAM, > + .attr = RPMI_SYSSUSP_ATTRS_FLAGS_RESUMEADDR, > + }, > +}; > + > +static enum rpmi_error riscv_rpmi_syssusp_prepare( > + void *priv, rpmi_uint32_t hart_index, > + const struct rpmi_system_suspend_type *syssusp_type, > + rpmi_uint64_t resume_addr) > +{ > + RiscvRpmiState *s = priv; > + > + if (s->machine_ops && s->machine_ops->register_wakeup_support) { > + s->machine_ops->register_wakeup_support(s->machine_opaque); > + } > + return RPMI_SUCCESS; > +} > + > +static rpmi_bool_t riscv_rpmi_syssusp_ready(void *priv, > + rpmi_uint32_t hart_index) > +{ > + return true; > +} > + > +static void riscv_rpmi_syssusp_finalize( > + void *priv, rpmi_uint32_t hart_index, > + const struct rpmi_system_suspend_type *syssusp_type, > + rpmi_uint64_t resume_addr) > +{ > + RiscvRpmiState *s = priv; > + > + if (s->machine_ops && s->machine_ops->system_suspend) { > + s->machine_ops->system_suspend(s->machine_opaque); > + } > +} > + > +static rpmi_bool_t riscv_rpmi_syssusp_can_resume(void *priv, > + rpmi_uint32_t hart_index) > +{ > + RiscvRpmiState *s = priv; > + > + if (s->machine_ops && s->machine_ops->system_can_resume) { > + return s->machine_ops->system_can_resume(s->machine_opaque); > + } > + > + return true; > +} > + > +static enum rpmi_error riscv_rpmi_syssusp_resume( > + void *priv, rpmi_uint32_t hart_index, > + const struct rpmi_system_suspend_type *syssusp_type, > + rpmi_uint64_t resume_addr) > +{ > + RiscvRpmiState *s = priv; > + > + s->syssusp_resume_hart_index = hart_index; > + s->syssusp_resume_pending = true; > + return RPMI_SUCCESS; > +} > + > +static const struct rpmi_syssusp_platform_ops riscv_rpmi_syssusp_ops = { > + .system_suspend_prepare = riscv_rpmi_syssusp_prepare, > + .system_suspend_ready = riscv_rpmi_syssusp_ready, > + .system_suspend_finalize = riscv_rpmi_syssusp_finalize, > + .system_suspend_can_resume = riscv_rpmi_syssusp_can_resume, > + .system_suspend_resume = riscv_rpmi_syssusp_resume, > +}; > + > +static void riscv_rpmi_wakeup_timer(void *opaque) > +{ > + RiscvRpmiState *s = opaque; > + > + if (s->syssusp_resume_pending) { > + riscv_rpmi_hsm_resume(s, s->syssusp_resume_hart_index); > + s->syssusp_resume_pending = false; > + } > +} > + > +static void riscv_rpmi_wakeup_notify(Notifier *notifier, void *data) > +{ > + RiscvRpmiState *s = container_of(notifier, RiscvRpmiState, > + wakeup_notifier); > + > + if (s->context) { > + rpmi_context_process_group_events(s->context, > + RPMI_SRVGRP_SYSTEM_SUSPEND); > + } > + if (s->wakeup_timer) { > + /* > + * Wakeup notifiers run before vCPUs resume. Defer the HSM > + * kick so OpenSBI can leave WFI after QEMU restarts execution. > + */ > + timer_mod(s->wakeup_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 10); > + } > +} > + > +static bool riscv_rpmi_syssusp_create(RiscvRpmiState *s, > + struct rpmi_service_group **group, > + Error **errp) > +{ > + if (!s->hsm) { > + error_setg(errp, "RPMI system suspend service requires HSM service"); > + return false; > + } > + > + if (s->machine_ops && s->machine_ops->register_wakeup_support) { > + s->machine_ops->register_wakeup_support(s->machine_opaque); > + } > + if (!s->wakeup_timer) { > + s->wakeup_timer = timer_new_ms(QEMU_CLOCK_REALTIME, > + riscv_rpmi_wakeup_timer, s); > + } > + if (!s->wakeup_notifier_registered) { > + s->wakeup_notifier.notify = riscv_rpmi_wakeup_notify; > + qemu_register_wakeup_notifier(&s->wakeup_notifier); > + s->wakeup_notifier_registered = true; > + } > + > + *group = rpmi_service_group_syssusp_create( > + s->hsm, ARRAY_SIZE(riscv_rpmi_syssusp_types), > + riscv_rpmi_syssusp_types, &riscv_rpmi_syssusp_ops, s); > + if (!*group) { > + error_setg(errp, "failed to create RPMI system suspend service group"); > + return false; > + } > + > + return true; > +} > + > +static void riscv_rpmi_syssusp_destroy(RiscvRpmiState *s) > +{ > + if (s->wakeup_notifier_registered) { > + notifier_remove(&s->wakeup_notifier); > + s->wakeup_notifier_registered = false; > + } > + if (s->wakeup_timer) { > + timer_free(s->wakeup_timer); > + s->wakeup_timer = NULL; > + } > + > + if (s->syssusp_group) { > + rpmi_service_group_syssusp_destroy(s->syssusp_group); > + s->syssusp_group = NULL; > + } > +} > + > +bool riscv_rpmi_syssusp_add(RiscvRpmiState *s, Error **errp) > +{ > + struct rpmi_service_group *group; > + > + if (s->syssusp_group) { > + error_setg(errp, "duplicate RPMI system suspend descriptor"); > + return false; > + } > + > + if (!riscv_rpmi_syssusp_create(s, &group, errp)) { > + return false; > + } > + > + if (!riscv_rpmi_context_add_group(s, group, "system suspend", errp)) { > + s->syssusp_group = group; > + riscv_rpmi_syssusp_destroy(s); > + return false; > + } > + > + s->syssusp_group = group; > + return true; > +} > + > +void riscv_rpmi_syssusp_remove(RiscvRpmiState *s) > +{ > + riscv_rpmi_context_remove_group(s, s->syssusp_group); > + riscv_rpmi_syssusp_destroy(s); > +} > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > index 2b5f367bdb..21c218a78d 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -51,6 +51,7 @@ > #include "chardev/char.h" > #include "system/device_tree.h" > #include "system/system.h" > +#include "system/runstate.h" > #include "system/tcg.h" > #include "system/kvm.h" > #include "system/tpm.h" > @@ -1020,6 +1021,12 @@ static const RiscvRpmiServiceConfig virt_rpmi_services[] = { > .compatible = "riscv,rpmi-hsm", > .service_group = RISCV_RPMI_SRVGRP_HSM, > }, > + { > + .kind = RISCV_RPMI_SERVICE_SYSSUSP, > + .node_name = "suspend", > + .compatible = "riscv,rpmi-system-suspend", > + .service_group = RISCV_RPMI_SRVGRP_SYSTEM_SUSPEND, > + }, > }; > > static uint32_t virt_rpmi_service_count(RISCVVirtState *s) > @@ -1037,9 +1044,27 @@ static void virt_rpmi_system_shutdown(void *opaque) > qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); > } > > +static void virt_rpmi_system_suspend(void *opaque) > +{ > + qemu_system_suspend_request(); > +} > + > +static void virt_rpmi_register_wakeup_support(void *opaque) > +{ > + qemu_register_wakeup_support(); > +} > + > +static bool virt_rpmi_system_can_resume(void *opaque) > +{ > + return runstate_check(RUN_STATE_RUNNING); > +} > + > static const RiscvRpmiMachineOps virt_rpmi_machine_ops = { > .system_reset = virt_rpmi_system_reset, > .system_shutdown = virt_rpmi_system_shutdown, > + .system_suspend = virt_rpmi_system_suspend, > + .register_wakeup_support = virt_rpmi_register_wakeup_support, > + .system_can_resume = virt_rpmi_system_can_resume, > }; > > static RiscvRpmiConfig virt_rpmi_config(RISCVVirtState *s, > diff --git a/include/hw/misc/riscv_rpmi.h b/include/hw/misc/riscv_rpmi.h > index 37c56ea2e8..a4fe2aac63 100644 > --- a/include/hw/misc/riscv_rpmi.h > +++ b/include/hw/misc/riscv_rpmi.h > @@ -29,6 +29,7 @@ > > #define RISCV_RPMI_SRVGRP_SYSTEM_RESET 3 > #define RISCV_RPMI_SRVGRP_HSM 5 > +#define RISCV_RPMI_SRVGRP_SYSTEM_SUSPEND 4 > > #define TYPE_RISCV_RPMI "riscv-rpmi" > OBJECT_DECLARE_SIMPLE_TYPE(RiscvRpmiState, RISCV_RPMI) > @@ -43,11 +44,15 @@ typedef enum RiscvRpmiServiceKind { > RISCV_RPMI_SERVICE_INVALID = 0, > RISCV_RPMI_SERVICE_SYSRESET, > RISCV_RPMI_SERVICE_HSM, > + RISCV_RPMI_SERVICE_SYSSUSP, > } RiscvRpmiServiceKind; > > typedef struct RiscvRpmiMachineOps { > void (*system_reset)(void *opaque); > void (*system_shutdown)(void *opaque); > + void (*system_suspend)(void *opaque); > + void (*register_wakeup_support)(void *opaque); > + bool (*system_can_resume)(void *opaque); > } RiscvRpmiMachineOps; > typedef struct RiscvRpmiServiceConfig { > RiscvRpmiServiceKind kind; > @@ -86,9 +91,15 @@ struct RiscvRpmiState { > const RiscvRpmiMachineOps *machine_ops; > void *machine_opaque; > struct rpmi_service_group *sysreset_group; > + struct rpmi_service_group *syssusp_group; > struct rpmi_hsm *hsm; > struct rpmi_service_group *hsm_group; > uint32_t *hsm_hw_states; > + Notifier wakeup_notifier; > + bool wakeup_notifier_registered; > + QEMUTimer *wakeup_timer; > + bool syssusp_resume_pending; > + uint32_t syssusp_resume_hart_index; > uint32_t *hart_ids; > uint32_t hart_count; > const RiscvRpmiServiceConfig *services;