On Mon, 2026-10-26 at 18:54 +0530, Subrahmanya Lingappa wrote: > Wire the common RISC-V RPMI device into the riscv-server-ref machine > so > firmware can discover platform management services through the > generated > device tree. The board exposes RPMI unconditionally when QEMU is > built with > librpmi support and does not provide an rpmi=on|off machine option. > > Advertise Base, System Reset, HSM, and System Suspend service groups > for the > board. Reset, shutdown, hart management, and suspend requests are > routed > through the shared RPMI implementation and the server-ref machine > callbacks. > When RPMI is available, omit the legacy syscon reboot and poweroff > nodes so > firmware uses RPMI for those operations. > > Keep riscv-server-ref buildable in configurations where librpmi is > not > available. In that case the board boots without RPMI support, > configure > reports a warning, and the machine emits a runtime warning.
My review bot points out that we should allow users to enable/disable RPMI, not just use build time options. Full output below: - [HIGH] hw/riscv/server_platform_ref.c wires an unmigratable device into riscv-server-ref unconditionally. rvserver_ref_machine_init() realizes the RPMI device whenever CONFIG_LIBRPMI is set, and hw/misc/riscv_rpmi.c declares riscv_rpmi_vmstate with .unmigratable = 1, so every librpmi build of this board loses live migration, including guests that never use RPMI. Verified at the series end commit: the failure is deterministic (run the machine and issue QMP migrate; it fails with an unmigratable-device error), yet neither the commit message nor docs/system/riscv/riscv-server-ref.rst mentions it. hw/riscv/virt.c gets this right by confining the same device behind an explicit rpmi=on opt-in (creation guarded on s->have_rpmi), and the series' own qtest test_rpmi_migration_blocked proves the failure mode for that machine. The follow-up tests/qtest patch does not fix this; it codifies unconditional RPMI via test_rpmi_rvserver_ref_rejects_rpmi_property. Add an on/auto/off rpmi machine property matching virt and default it so migratability is preserved unless the user opts in; document the restriction. - [MEDIUM] hw/riscv/server_platform_ref.c selects the guest-visible device tree from build configuration, not machine configuration. Under CONFIG_LIBRPMI, finalize_fdt() emits the RPMI mailbox and service nodes and calls riscv_create_fdt_syscon with create_reset_nodes=false, which per hw/riscv/fdt-common.c (if (!create_reset_nodes) return;) drops the /reboot (syscon-reboot) and /poweroff (syscon-poweroff) nodes; without librpmi those legacy nodes remain. meson auto-detects librpmi, so the same guest image boots on different hardware depending on the build host, and firmware that previously relied on the syscon-reboot/syscon-poweroff nodes loses its reset/shutdown path with no command-line way to recover the legacy DT shape. riscv-server-ref is a user-facing machine, and per the RISC-V subsystem rules silently reshaping its guest-visible DT by build config is a finding. The follow-up qtest asserting rpmi=off is rejected with Property not found codifies the defect rather than resolving it. Gate this on an explicit rpmi machine property (on/auto/off) like hw/riscv/virt.c does with s->have_rpmi, defaulting to off. Alistair > > Signed-off-by: Subrahmanya Lingappa > <[email protected]> > --- > docs/system/riscv/riscv-server-ref.rst | 41 +++++++ > hw/riscv/Kconfig | 1 + > hw/riscv/server_platform_ref.c | 144 > ++++++++++++++++++++++++- > meson.build | 4 + > 4 files changed, 189 insertions(+), 1 deletion(-) > > diff --git a/docs/system/riscv/riscv-server-ref.rst > b/docs/system/riscv/riscv-server-ref.rst > index da043ed096..b09d747aaf 100644 > --- a/docs/system/riscv/riscv-server-ref.rst > +++ b/docs/system/riscv/riscv-server-ref.rst > @@ -28,6 +28,7 @@ The main features included in the riscv-server-ref > board are: > * No fw_cfg device > * No ACPI table > * Minimal device tree nodes > +* RISC-V RPMI platform management > > There are multiple ways of using this reference board. The spec > compliant way > is using an EDK2 image and a TPM device. The board was tested with > the TPM > @@ -60,6 +61,46 @@ And then start QEMU with: > -netdev user,id=net0 -device e1000e,netdev=net0 \ > -nographic > > +RPMI support > +------------ > + > +The ``riscv-server-ref`` machine always exposes RISC-V RPMI platform > management > +when QEMU is built with librpmi support. There is no > ``rpmi=on|off`` machine > +option for this board. If QEMU is built without librpmi, the board > still boots > +without RPMI and reports a warning. > + > +The initial ``riscv-server-ref`` RPMI implementation exposes Base, > System > +Reset, HSM, and System Suspend service groups. System Reset handles > guest > +shutdown and cold reboot requests through RPMI. > +HSM uses the machine hart IDs for hart discovery, status, start, > stop, and > +suspend commands. System Suspend uses QEMU's suspend and wakeup > path. > + > +When RPMI is compiled in, the generated device tree advertises > reset, shutdown, > +HSM, and suspend through RPMI service nodes. The legacy syscon > reboot and > +poweroff nodes are omitted so firmware routes reset and shutdown > through the > +RPMI System Reset service. Without RPMI support, the generated > device tree uses > +the legacy syscon reboot and poweroff nodes. RPMI is currently > described > +through FDT only; this machine does not emit ACPI tables for the > RPMI transport > +or service groups. > + > +RPMI support targets the RISC-V Platform Management Interface (RPMI) > +Specification v1.0 Ratified (`server-ref RPMI v1.0 > specification`_). The > +build dependency is provided by `server-ref librpmi`_. After > installing > +librpmi, configure QEMU with ``--enable-librpmi`` or ``- > Dlibrpmi=enabled`` to > +make the build fail if librpmi is unavailable. > + > +.. code-block:: bash > + > + qemu-system-riscv64 -M riscv-server-ref \ > + -bios fw_dynamic.bin \ > + -kernel EDK2.fd \ > + -drive file=nvme_disk.ext2,format=raw,id=hd0,if=none \ > + -device ahci,id=ahci \ > + -device ide-hd,drive=hd0,bus=ahci.0 \ > + -nographic > + > > .. _spec: https://github.com/riscv-non-isa/riscv-server-platform > .. _tpm: https://qemu-project.gitlab.io/qemu/specs/tpm.html > +.. _server-ref RPMI v1.0 specification: > https://github.com/riscv-non-isa/riscv-rpmi/releases/download/v1.0/riscv-rpmi.pdf > +.. _server-ref librpmi: > https://github.com/riscv-software-src/librpmi > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > index dfdbc6dfe1..b5f0346962 100644 > --- a/hw/riscv/Kconfig > +++ b/hw/riscv/Kconfig > @@ -86,6 +86,7 @@ config RISCV_SERVER_PLATFORM_REF > select RISCV_IOMMU > select RISCV_NUMA > select SERIAL_MM > + select RISCV_RPMI if LIBRPMI > > config SHAKTI_C > bool > diff --git a/hw/riscv/server_platform_ref.c > b/hw/riscv/server_platform_ref.c > index d18550b48d..3e4ee0c831 100644 > --- a/hw/riscv/server_platform_ref.c > +++ b/hw/riscv/server_platform_ref.c > @@ -29,18 +29,20 @@ > #include "hw/riscv/device-common.h" > #include "hw/riscv/fdt-common.h" > #include "hw/riscv/numa.h" > +#include "hw/riscv/rpmi-fdt.h" > #include "hw/riscv/iommu.h" > #include "hw/riscv/riscv-iommu.h" > #include "hw/riscv/riscv-iommu-bits.h" > +#include "hw/misc/riscv_rpmi.h" > #include "hw/intc/riscv_aclint.h" > #include "hw/intc/riscv_aplic.h" > #include "hw/intc/riscv_imsic.h" > #include "chardev/char.h" > #include "hw/char/serial-mm.h" > #include "system/device_tree.h" > +#include "system/kvm.h" > #include "system/runstate.h" > #include "system/system.h" > -#include "system/kvm.h" > #include "system/qtest.h" > #include "system/tcg.h" > #include "kvm/kvm_riscv.h" > @@ -68,6 +70,9 @@ > > #define RVSERVER_PLATFORM_BUS_NUM_IRQS 8 > > +#define RVSERVER_RPMI_A2P_REQ_SIZE (16 * RPMI_QUEUE_SLOT_SIZE) > +#define RVSERVER_RPMI_P2A_REQ_SIZE 0 > + > #define TYPE_RISCV_SERVER_REF_MACHINE MACHINE_TYPE_NAME("riscv- > server-ref") > OBJECT_DECLARE_SIMPLE_TYPE(RISCVServerRefMachineState, > RISCV_SERVER_REF_MACHINE) > > @@ -93,6 +98,8 @@ enum { > RVSERVER_RESET_SYSCON, > RVSERVER_RTC, > RVSERVER_IOMMU_SYS, > + RVSERVER_RPMI_SHMEM, > + RVSERVER_RPMI_DOORBELL, > RVSERVER_ACLINT, > RVSERVER_APLIC_M, > RVSERVER_APLIC_S, > @@ -143,6 +150,8 @@ static const MemMapEntry rvserver_ref_memmap[] = > { > [RVSERVER_RESET_SYSCON] = { 0x100000, 0x1000 }, > [RVSERVER_RTC] = { 0x101000, 0x1000 }, > [RVSERVER_IOMMU_SYS] = { 0x102000, 0x1000 }, > + [RVSERVER_RPMI_SHMEM] = { 0x110000, 0x20000 }, > + [RVSERVER_RPMI_DOORBELL] = { 0x140000, 0x1000 }, > [RVSERVER_ACLINT] = { 0x2000000, 0x10000 }, > [RVSERVER_PCIE_PIO] = { 0x3000000, 0x10000 }, > [RVSERVER_PLATFORM_BUS] = { 0x4000000, 0x2000000 }, > @@ -283,6 +292,100 @@ static void > create_fdt_sockets(RISCVServerRefMachineState *s, > riscv_socket_fdt_write_distance_matrix(ms); > } > > +#ifdef CONFIG_LIBRPMI > +static const RiscvRpmiServiceConfig rvserver_ref_rpmi_services[] = { > + { > + .node_name = "sysreset", > + .compatible = "riscv,rpmi-system-reset", > + .service_group = RPMI_SRVGRP_SYSTEM_RESET, > + }, > + { > + .node_name = "hsm", > + .compatible = "riscv,rpmi-hsm", > + .service_group = RPMI_SRVGRP_HSM, > + }, > + { > + .node_name = "suspend", > + .compatible = "riscv,rpmi-system-suspend", > + .service_group = RPMI_SRVGRP_SYSTEM_SUSPEND, > + }, > +}; > + > +static void rvserver_ref_rpmi_system_reset(void) > +{ > + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); > +} > + > +static void rvserver_ref_rpmi_system_shutdown(void) > +{ > + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); > +} > + > +static void rvserver_ref_rpmi_system_suspend(void) > +{ > + qemu_system_suspend_request(); > +} > + > +static void rvserver_ref_rpmi_register_wakeup_support(void) > +{ > + qemu_register_wakeup_support(); > +} > + > +static bool rvserver_ref_rpmi_system_can_resume(void) > +{ > + return runstate_check(RUN_STATE_RUNNING); > +} > + > +static const RiscvRpmiMachineOps rvserver_ref_rpmi_machine_ops = { > + .system_reset = rvserver_ref_rpmi_system_reset, > + .system_shutdown = rvserver_ref_rpmi_system_shutdown, > + .system_suspend = rvserver_ref_rpmi_system_suspend, > + .register_wakeup_support = > rvserver_ref_rpmi_register_wakeup_support, > + .system_can_resume = rvserver_ref_rpmi_system_can_resume, > +}; > + > +static RiscvRpmiConfig rvserver_ref_rpmi_config( > + RISCVServerRefMachineState *s, const uint32_t *hart_ids, > + uint32_t hart_count) > +{ > + return (RiscvRpmiConfig) { > + .doorbell_base = s->memmap[RVSERVER_RPMI_DOORBELL].base, > + .shmem_base = s->memmap[RVSERVER_RPMI_SHMEM].base, > + .shmem_size = s->memmap[RVSERVER_RPMI_SHMEM].size, > + .a2p_req_size = RVSERVER_RPMI_A2P_REQ_SIZE, > + .p2a_req_size = RVSERVER_RPMI_P2A_REQ_SIZE, > + .platform_info = "QEMU RISC-V server-ref RPMI", > + .machine_ops = &rvserver_ref_rpmi_machine_ops, > + .hart_ids = hart_ids, > + .hart_count = hart_count, > + .services = rvserver_ref_rpmi_services, > + .service_count = ARRAY_SIZE(rvserver_ref_rpmi_services), > + }; > +} > + > +static void create_fdt_rpmi(RISCVServerRefMachineState *s, uint32_t > *phandle) > +{ > + RiscvRpmiConfig rpmi_cfg = rvserver_ref_rpmi_config(s, NULL, 0); > + uint32_t rpmi_mbox_handle; > + RiscvRpmiFdtMboxConfig cfg = { > + .shmem_base = rpmi_cfg.shmem_base, > + .doorbell_base = rpmi_cfg.doorbell_base, > + .a2p_req_size = rpmi_cfg.a2p_req_size, > + .p2a_req_size = rpmi_cfg.p2a_req_size, > + .doorbell_size = s->memmap[RVSERVER_RPMI_DOORBELL].size, > + }; > + > + riscv_rpmi_fdt_add_mbox(MACHINE(s)->fdt, &cfg, phandle, > + &rpmi_mbox_handle); > + > + for (uint32_t i = 0; i < rpmi_cfg.service_count; i++) { > + riscv_rpmi_fdt_add_service_node(MACHINE(s)->fdt, > + &rpmi_cfg.services[i], > + rpmi_mbox_handle); > + } > +} > +#endif > + > static void finalize_fdt(RISCVServerRefMachineState *s) > { > uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = > 1; > @@ -307,10 +410,18 @@ static void > finalize_fdt(RISCVServerRefMachineState *s) > irq_pcie_phandle, msi_pcie_phandle, > iommu_sys_phandle, RVSERVER_PCIE_IRQ); > > +#ifdef CONFIG_LIBRPMI > + create_fdt_rpmi(s, &phandle); > + riscv_create_fdt_syscon(ms->fdt, &phandle, > + > rvserver_ref_memmap[RVSERVER_RESET_SYSCON].base, > + > rvserver_ref_memmap[RVSERVER_RESET_SYSCON].size, > + SYSCON_RESET, SYSCON_POWEROFF, false, > false); > +#else > riscv_create_fdt_syscon(ms->fdt, &phandle, > > rvserver_ref_memmap[RVSERVER_RESET_SYSCON].base, > > rvserver_ref_memmap[RVSERVER_RESET_SYSCON].size, > SYSCON_RESET, SYSCON_POWEROFF, false, > true); > +#endif > > riscv_create_fdt_uart(ms->fdt, > &rvserver_ref_memmap[RVSERVER_UART0], > RVSERVER_UART0_IRQ, AIA_TYPE_APLIC_IMSIC, > @@ -516,6 +627,12 @@ static void > rvserver_ref_machine_init(MachineState *machine) > } > > imsic_bits = imsic_num_bits(s->aia_guests + 1); > +#ifdef CONFIG_LIBRPMI > + if (kvm_enabled()) { > + error_report("RISC-V RPMI support is not available with > KVM"); > + exit(1); > + } > +#endif > > /* Initialize sockets */ > mmio_irqchip = pcie_irqchip = NULL; > @@ -598,6 +715,31 @@ static void > rvserver_ref_machine_init(MachineState *machine) > memmap[RVSERVER_RESET_SYSCON].base, > reset_syscon_io); > > +#ifdef CONFIG_LIBRPMI > + { > + MachineClass *mc = MACHINE_GET_CLASS(machine); > + const CPUArchIdList *possible_cpus = mc- > >possible_cpu_arch_ids(machine); > + g_autofree uint32_t *rpmi_hart_ids = > + g_new0(uint32_t, machine->smp.cpus); > + RiscvRpmiConfig rpmi_cfg; > + Error *local_err = NULL; > + > + for (i = 0; i < machine->smp.cpus; i++) { > + rpmi_hart_ids[i] = possible_cpus->cpus[i].arch_id; > + } > + > + rpmi_cfg = rvserver_ref_rpmi_config(s, rpmi_hart_ids, > + machine->smp.cpus); > + if (!riscv_rpmi_create(&rpmi_cfg, &local_err)) { > + error_report_err(local_err); > + exit(1); > + } > + } > +#else > + warn_report("riscv-server-ref: RPMI support is not compiled in; > " > + "booting without RPMI"); > +#endif > + > riscv_gpex_pcie_init(system_memory, pcie_irqchip, > &rvserver_ref_memmap[RVSERVER_PCIE_ECAM], > &rvserver_ref_memmap[RVSERVER_PCIE_MMIO], > diff --git a/meson.build b/meson.build > index 9c4d501687..5b8ea24714 100644 > --- a/meson.build > +++ b/meson.build > @@ -3438,6 +3438,10 @@ foreach target : target_dirs > foreach k, v: config_devices > config_devices_data.set(k, 1) > endforeach > + if target == 'riscv64-softmmu' and get_option('default_devices') > and \ > + not librpmi.found() > + warning('riscv-server-ref machine will be built without RPMI > support; install librpmi and configure with --enable-librpmi to > enable RPMI') > + endif > config_devices_mak_list += config_devices_mak > config_devices_h += {target: configure_file(output: target + '- > config-devices.h', > configuration: > config_devices_data)}
