[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [RFC v2 2/2] hw/riscv: Add server platform reference machine
From: |
Alistair Francis |
Subject: |
Re: [RFC v2 2/2] hw/riscv: Add server platform reference machine |
Date: |
Fri, 22 Mar 2024 14:55:56 +1000 |
On Tue, Mar 12, 2024 at 11:55 PM Fei Wu <fei2.wu@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.wu@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
Re: [RFC v2 2/2] hw/riscv: Add server platform reference machine, Atish Kumar Patra, 2024/03/22