On Tue, Mar 12, 2024 at 11:55 PM Fei Wu <fei2...@intel.com> wrote: > > The RISC-V Server Platform specification[1] defines a standardized set > of hardware and software capabilities, that portable system software, > such as OS and hypervisors can rely on being present in a RISC-V server > platform. > > A corresponding Qemu RISC-V server platform reference (rvsp-ref for > short) machine type is added to provide a environment for firmware/OS > development and testing. The main features included in rvsp-ref are: > > - Based on riscv virt machine type > - A new memory map as close as virt machine as possible > - A new virt CPU type rvsp-ref-cpu for server platform compliance > - AIA > - PCIe AHCI > - PCIe NIC > - No virtio device > - No fw_cfg device > - No ACPI table provided > - Only minimal device tree nodes > > [1] https://github.com/riscv-non-isa/riscv-server-platform > > Signed-off-by: Fei Wu <fei2...@intel.com> > --- > configs/devices/riscv64-softmmu/default.mak | 1 + > hw/riscv/Kconfig | 12 + > hw/riscv/meson.build | 1 + > hw/riscv/server_platform_ref.c | 1276 +++++++++++++++++++ > 4 files changed, 1290 insertions(+) > create mode 100644 hw/riscv/server_platform_ref.c > > diff --git a/configs/devices/riscv64-softmmu/default.mak > b/configs/devices/riscv64-softmmu/default.mak > index 3f68059448..a1d98e49ef 100644 > --- a/configs/devices/riscv64-softmmu/default.mak > +++ b/configs/devices/riscv64-softmmu/default.mak > @@ -10,5 +10,6 @@ CONFIG_SPIKE=y > CONFIG_SIFIVE_E=y > CONFIG_SIFIVE_U=y > CONFIG_RISCV_VIRT=y > +CONFIG_SERVER_PLATFORM_REF=y > CONFIG_MICROCHIP_PFSOC=y > CONFIG_SHAKTI_C=y > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > index 5d644eb7b1..5674589e66 100644 > --- a/hw/riscv/Kconfig > +++ b/hw/riscv/Kconfig > @@ -48,6 +48,18 @@ config RISCV_VIRT > select ACPI > select ACPI_PCI > > +config SERVER_PLATFORM_REF > + bool > + select RISCV_NUMA > + select GOLDFISH_RTC > + select PCI > + select PCI_EXPRESS_GENERIC_BRIDGE > + select PFLASH_CFI01 > + select SERIAL > + select RISCV_ACLINT > + select RISCV_APLIC > + select RISCV_IMSIC > + > config SHAKTI_C > bool > select RISCV_ACLINT > diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build > index 2f7ee81be3..bb3aff91ea 100644 > --- a/hw/riscv/meson.build > +++ b/hw/riscv/meson.build > @@ -4,6 +4,7 @@ riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: > files('numa.c')) > riscv_ss.add(files('riscv_hart.c')) > riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) > riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c')) > +riscv_ss.add(when: 'CONFIG_SERVER_PLATFORM_REF', if_true: > files('server_platform_ref.c')) > riscv_ss.add(when: 'CONFIG_SHAKTI_C', if_true: files('shakti_c.c')) > riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c')) > riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) > diff --git a/hw/riscv/server_platform_ref.c b/hw/riscv/server_platform_ref.c > new file mode 100644 > index 0000000000..b552650265 > --- /dev/null > +++ b/hw/riscv/server_platform_ref.c > @@ -0,0 +1,1276 @@ > +/* > + * QEMU RISC-V Server Platform (RVSP) Reference Board > + * > + * Copyright (c) 2024 Intel, Inc. > + * > + * This board is compliant RISC-V Server platform specification and > leveraging > + * a lot of riscv virt code. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2 or later, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along > with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/units.h" > +#include "qemu/error-report.h" > +#include "qemu/guest-random.h" > +#include "qapi/error.h" > +#include "qapi/qapi-visit-common.h" > +#include "hw/boards.h" > +#include "hw/loader.h" > +#include "hw/sysbus.h" > +#include "hw/qdev-properties.h" > +#include "hw/char/serial.h" > +#include "hw/block/flash.h" > +#include "hw/ide/pci.h" > +#include "hw/ide/ahci-pci.h" > +#include "hw/pci/pci.h" > +#include "hw/pci-host/gpex.h" > +#include "hw/core/sysbus-fdt.h" > +#include "hw/riscv/riscv_hart.h" > +#include "hw/riscv/boot.h" > +#include "hw/riscv/numa.h" > +#include "hw/intc/riscv_aclint.h" > +#include "hw/intc/riscv_aplic.h" > +#include "hw/intc/riscv_imsic.h" > +#include "chardev/char.h" > +#include "sysemu/device_tree.h" > +#include "sysemu/runstate.h" > +#include "sysemu/sysemu.h" > +#include "sysemu/tcg.h" > +#include "target/riscv/cpu.h" > +#include "target/riscv/pmu.h" > +#include "net/net.h" > + > +#define RVSP_CPUS_MAX_BITS 9 > +#define RVSP_CPUS_MAX (1 << RVSP_CPUS_MAX_BITS) > +#define RVSP_SOCKETS_MAX_BITS 2 > +#define RVSP_SOCKETS_MAX (1 << RVSP_SOCKETS_MAX_BITS) > + > +#define RVSP_IRQCHIP_NUM_MSIS 255 > +#define RVSP_IRQCHIP_NUM_SOURCES 96 > +#define RVSP_IRQCHIP_NUM_PRIO_BITS 3 > +#define RVSP_IRQCHIP_MAX_GUESTS_BITS 3 > +#define RVSP_IRQCHIP_MAX_GUESTS ((1U << RVSP_IRQCHIP_MAX_GUESTS_BITS) - 1U) > + > +#define FDT_PCI_ADDR_CELLS 3 > +#define FDT_PCI_INT_CELLS 1 > +#define FDT_APLIC_INT_CELLS 2 > +#define FDT_IMSIC_INT_CELLS 0 > +#define FDT_MAX_INT_CELLS 2 > +#define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ > + 1 + FDT_MAX_INT_CELLS) > +#define FDT_APLIC_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ > + 1 + FDT_APLIC_INT_CELLS) > + > +#define NUM_SATA_PORTS 6 > + > +#define SYSCON_RESET 0x1 > +#define SYSCON_POWEROFF 0x2 > + > +#define TYPE_RVSP_REF_MACHINE MACHINE_TYPE_NAME("rvsp-ref") > +OBJECT_DECLARE_SIMPLE_TYPE(RVSPMachineState, RVSP_REF_MACHINE) > + > +struct RVSPMachineState { > + /*< private >*/ > + MachineState parent; > + > + /*< public >*/ > + Notifier machine_done; > + RISCVHartArrayState soc[RVSP_SOCKETS_MAX]; > + DeviceState *irqchip[RVSP_SOCKETS_MAX]; > + PFlashCFI01 *flash[2]; > + > + int fdt_size; > + int aia_guests; > + const MemMapEntry *memmap; > +}; > + > +enum { > + RVSP_DEBUG, > + RVSP_MROM, > + RVSP_RESET_SYSCON, > + RVSP_RTC, > + RVSP_ACLINT, > + RVSP_APLIC_M, > + RVSP_APLIC_S, > + RVSP_UART0, > + RVSP_IMSIC_M, > + RVSP_IMSIC_S, > + RVSP_FLASH, > + RVSP_DRAM, > + RVSP_PCIE_MMIO, > + RVSP_PCIE_PIO, > + RVSP_PCIE_ECAM, > + RVSP_PCIE_MMIO_HIGH > +}; > + > +enum { > + RVSP_UART0_IRQ = 10, > + RVSP_RTC_IRQ = 11, > + RVSP_PCIE_IRQ = 0x20, /* 32 to 35 */ > +}; > + > +/* > + * The server soc reference machine physical address space used by some of > the > + * devices namely ACLINT, APLIC and IMSIC depend on number of Sockets, number > + * of CPUs, and number of IMSIC guest files. > + * > + * Various limits defined by RVSP_SOCKETS_MAX_BITS, RVSP_CPUS_MAX_BITS, and > + * RVSP_IRQCHIP_MAX_GUESTS_BITS are tuned for maximum utilization of server > soc > + * reference machine physical address space. > + */ > + > +#define RVSP_IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) > +#if RVSP_IMSIC_GROUP_MAX_SIZE < \ > + IMSIC_GROUP_SIZE(RVSP_CPUS_MAX_BITS, RVSP_IRQCHIP_MAX_GUESTS_BITS) > +#error "Can't accomodate single IMSIC group in address space" > +#endif > + > +#define RVSP_IMSIC_MAX_SIZE (RVSP_SOCKETS_MAX * \ > + RVSP_IMSIC_GROUP_MAX_SIZE) > +#if 0x4000000 < RVSP_IMSIC_MAX_SIZE > +#error "Can't accomodate all IMSIC groups in address space" > +#endif > + > +static const MemMapEntry rvsp_ref_memmap[] = { > + [RVSP_DEBUG] = { 0x0, 0x100 }, > + [RVSP_MROM] = { 0x1000, 0xf000 }, > + [RVSP_RESET_SYSCON] = { 0x100000, 0x1000 }, > + [RVSP_RTC] = { 0x101000, 0x1000 }, > + [RVSP_ACLINT] = { 0x2000000, 0x10000 }, > + [RVSP_PCIE_PIO] = { 0x3000000, 0x10000 }, > + [RVSP_APLIC_M] = { 0xc000000, APLIC_SIZE(RVSP_CPUS_MAX) }, > + [RVSP_APLIC_S] = { 0xd000000, APLIC_SIZE(RVSP_CPUS_MAX) }, > + [RVSP_UART0] = { 0x10000000, 0x100 }, > + [RVSP_FLASH] = { 0x20000000, 0x4000000 }, > + [RVSP_IMSIC_M] = { 0x24000000, RVSP_IMSIC_MAX_SIZE }, > + [RVSP_IMSIC_S] = { 0x28000000, RVSP_IMSIC_MAX_SIZE }, > + [RVSP_PCIE_ECAM] = { 0x30000000, 0x10000000 }, > + [RVSP_PCIE_MMIO] = { 0x40000000, 0x40000000 }, > + [RVSP_DRAM] = { 0x80000000, 0xff80000000ull }, > + [RVSP_PCIE_MMIO_HIGH] = { 0x10000000000ull, 0x10000000000ull }, > +}; > + > +#define RVSP_FLASH_SECTOR_SIZE (256 * KiB) > + > +static PFlashCFI01 *rvsp_flash_create(RVSPMachineState *s, > + const char *name, > + const char *alias_prop_name) > +{ > + /* > + * Create a single flash device. We use the same parameters as > + * the flash devices on the ARM virt board. > + */ > + DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); > + > + qdev_prop_set_uint64(dev, "sector-length", RVSP_FLASH_SECTOR_SIZE); > + qdev_prop_set_uint8(dev, "width", 4); > + qdev_prop_set_uint8(dev, "device-width", 2); > + qdev_prop_set_bit(dev, "big-endian", false); > + qdev_prop_set_uint16(dev, "id0", 0x89); > + qdev_prop_set_uint16(dev, "id1", 0x18); > + qdev_prop_set_uint16(dev, "id2", 0x00); > + qdev_prop_set_uint16(dev, "id3", 0x00); > + qdev_prop_set_string(dev, "name", name); > + > + object_property_add_child(OBJECT(s), name, OBJECT(dev)); > + object_property_add_alias(OBJECT(s), alias_prop_name, > + OBJECT(dev), "drive"); > + > + return PFLASH_CFI01(dev); > +} > + > +static void rvsp_flash_map(PFlashCFI01 *flash, > + hwaddr base, hwaddr size, > + MemoryRegion *sysmem) > +{ > + DeviceState *dev = DEVICE(flash); > + > + assert(QEMU_IS_ALIGNED(size, RVSP_FLASH_SECTOR_SIZE)); > + assert(size / RVSP_FLASH_SECTOR_SIZE <= UINT32_MAX); > + qdev_prop_set_uint32(dev, "num-blocks", size / RVSP_FLASH_SECTOR_SIZE); > + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); > + > + memory_region_add_subregion(sysmem, base, > + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), > + 0)); > +} > + > +static void rvsp_flash_maps(RVSPMachineState *s, > + MemoryRegion *sysmem) > +{ > + hwaddr flashsize = rvsp_ref_memmap[RVSP_FLASH].size / 2; > + hwaddr flashbase = rvsp_ref_memmap[RVSP_FLASH].base; > + > + rvsp_flash_map(s->flash[0], flashbase, flashsize, sysmem); > + rvsp_flash_map(s->flash[1], flashbase + flashsize, flashsize, sysmem); > +} > + > +static void create_pcie_irq_map(RVSPMachineState *s, void *fdt, char > *nodename, > + uint32_t irqchip_phandle) > +{ > + int pin, dev; > + uint32_t irq_map_stride = 0; > + uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * > + FDT_MAX_INT_MAP_WIDTH] = {}; > + uint32_t *irq_map = full_irq_map; > + > + /* > + * This code creates a standard swizzle of interrupts such that > + * each device's first interrupt is based on it's PCI_SLOT number. > + * (See pci_swizzle_map_irq_fn()) > + * > + * We only need one entry per interrupt in the table (not one per > + * possible slot) seeing the interrupt-map-mask will allow the table > + * to wrap to any number of devices. > + */ > + for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { > + int devfn = dev * 0x8; > + > + for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { > + int irq_nr = RVSP_PCIE_IRQ + > + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); > + int i = 0; > + > + /* Fill PCI address cells */ > + irq_map[i] = cpu_to_be32(devfn << 8); > + i += FDT_PCI_ADDR_CELLS; > + > + /* Fill PCI Interrupt cells */ > + irq_map[i] = cpu_to_be32(pin + 1); > + i += FDT_PCI_INT_CELLS; > + > + /* Fill interrupt controller phandle and cells */ > + irq_map[i++] = cpu_to_be32(irqchip_phandle); > + irq_map[i++] = cpu_to_be32(irq_nr); > + irq_map[i++] = cpu_to_be32(0x4); > + > + if (!irq_map_stride) { > + irq_map_stride = i; > + } > + irq_map += irq_map_stride; > + } > + } > + > + qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, > + GPEX_NUM_IRQS * GPEX_NUM_IRQS * > + irq_map_stride * sizeof(uint32_t)); > + > + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", > + 0x1800, 0, 0, 0x7); > +} > + > +static void create_fdt_socket_cpus(RVSPMachineState *s, int socket,
I see no mention of device trees in the spec, but I do see ACPI. Do we really expect a server platform to use DTs? These functions should be shared with the virt machine if we really do want DTs, but I'm not convinced we do Alistair