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

Reply via email to