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