Alistair, On Tue, Oct 27, 2026 at 5:41 AM Alistair Francis <[email protected]> wrote: > > 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 > Fixed in v3. Added rpmi=on|off, defaulting to off, so building with librpmi does not unconditionally disable migration.
The property now controls device realization and DT layout. Default/off keeps the legacy reset/poweroff nodes. RPMI-on emits the RPMI nodes and has the documented migration restriction. Added property, DT and migration-policy qtests. Normal and TPM guests reached login with RPMI both off and on. Thanks, Subrahmanya > > > > 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)}
