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)}

Reply via email to