|
@@ -33,6 +33,7 @@
|
|
|
#include "target/riscv/pmu.h"
|
|
|
#include "hw/riscv/riscv_hart.h"
|
|
|
#include "hw/riscv/iommu.h"
|
|
|
+#include "hw/riscv/riscv-iommu-bits.h"
|
|
|
#include "hw/riscv/virt.h"
|
|
|
#include "hw/riscv/boot.h"
|
|
|
#include "hw/riscv/numa.h"
|
|
@@ -58,9 +59,18 @@
|
|
|
#include "hw/virtio/virtio-iommu.h"
|
|
|
|
|
|
/* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */
|
|
|
-static bool virt_use_kvm_aia(RISCVVirtState *s)
|
|
|
+static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type)
|
|
|
{
|
|
|
- return kvm_irqchip_in_kernel() && s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
|
|
|
+ bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
|
|
|
+
|
|
|
+ return riscv_is_kvm_aia_aplic_imsic(msimode);
|
|
|
+}
|
|
|
+
|
|
|
+static bool virt_use_emulated_aplic(RISCVVirtAIAType aia_type)
|
|
|
+{
|
|
|
+ bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
|
|
|
+
|
|
|
+ return riscv_use_emulated_aplic(msimode);
|
|
|
}
|
|
|
|
|
|
static bool virt_aclint_allowed(void)
|
|
@@ -76,6 +86,7 @@ static const MemMapEntry virt_memmap[] = {
|
|
|
[VIRT_CLINT] = { 0x2000000, 0x10000 },
|
|
|
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
|
|
|
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
|
|
|
+ [VIRT_IOMMU_SYS] = { 0x3010000, 0x1000 },
|
|
|
[VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 },
|
|
|
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
|
|
|
[VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) },
|
|
@@ -775,12 +786,19 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
|
|
|
*msi_pcie_phandle = msi_s_phandle;
|
|
|
}
|
|
|
|
|
|
- /* KVM AIA only has one APLIC instance */
|
|
|
- if (kvm_enabled() && virt_use_kvm_aia(s)) {
|
|
|
+ /*
|
|
|
+ * With KVM AIA aplic-imsic, using an irqchip without split
|
|
|
+ * mode, we'll use only one APLIC instance.
|
|
|
+ */
|
|
|
+ if (!virt_use_emulated_aplic(s->aia_type)) {
|
|
|
create_fdt_socket_aplic(s, memmap, 0,
|
|
|
msi_m_phandle, msi_s_phandle, phandle,
|
|
|
&intc_phandles[0], xplic_phandles,
|
|
|
ms->smp.cpus);
|
|
|
+
|
|
|
+ *irq_mmio_phandle = xplic_phandles[0];
|
|
|
+ *irq_virtio_phandle = xplic_phandles[0];
|
|
|
+ *irq_pcie_phandle = xplic_phandles[0];
|
|
|
} else {
|
|
|
phandle_pos = ms->smp.cpus;
|
|
|
for (socket = (socket_count - 1); socket >= 0; socket--) {
|
|
@@ -798,13 +816,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
|
|
|
s->soc[socket].num_harts);
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (kvm_enabled() && virt_use_kvm_aia(s)) {
|
|
|
- *irq_mmio_phandle = xplic_phandles[0];
|
|
|
- *irq_virtio_phandle = xplic_phandles[0];
|
|
|
- *irq_pcie_phandle = xplic_phandles[0];
|
|
|
- } else {
|
|
|
for (socket = 0; socket < socket_count; socket++) {
|
|
|
if (socket == 0) {
|
|
|
*irq_mmio_phandle = xplic_phandles[socket];
|
|
@@ -853,7 +865,8 @@ static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap,
|
|
|
|
|
|
static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap,
|
|
|
uint32_t irq_pcie_phandle,
|
|
|
- uint32_t msi_pcie_phandle)
|
|
|
+ uint32_t msi_pcie_phandle,
|
|
|
+ uint32_t iommu_sys_phandle)
|
|
|
{
|
|
|
g_autofree char *name = NULL;
|
|
|
MachineState *ms = MACHINE(s);
|
|
@@ -887,6 +900,12 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap,
|
|
|
2, virt_high_pcie_memmap.base,
|
|
|
2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size);
|
|
|
|
|
|
+ if (virt_is_iommu_sys_enabled(s)) {
|
|
|
+ qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map",
|
|
|
+ 0, iommu_sys_phandle, 0, 0, 0,
|
|
|
+ iommu_sys_phandle, 0, 0xffff);
|
|
|
+ }
|
|
|
+
|
|
|
create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle);
|
|
|
}
|
|
|
|
|
@@ -1033,6 +1052,47 @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf)
|
|
|
bdf + 1, iommu_phandle, bdf + 1, 0xffff - bdf);
|
|
|
}
|
|
|
|
|
|
+static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip,
|
|
|
+ uint32_t msi_phandle,
|
|
|
+ uint32_t *iommu_sys_phandle)
|
|
|
+{
|
|
|
+ const char comp[] = "riscv,iommu";
|
|
|
+ void *fdt = MACHINE(s)->fdt;
|
|
|
+ uint32_t iommu_phandle;
|
|
|
+ g_autofree char *iommu_node = NULL;
|
|
|
+ hwaddr addr = s->memmap[VIRT_IOMMU_SYS].base;
|
|
|
+ hwaddr size = s->memmap[VIRT_IOMMU_SYS].size;
|
|
|
+ uint32_t iommu_irq_map[RISCV_IOMMU_INTR_COUNT] = {
|
|
|
+ IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_CQ,
|
|
|
+ IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_FQ,
|
|
|
+ IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PM,
|
|
|
+ IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PQ,
|
|
|
+ };
|
|
|
+
|
|
|
+ iommu_node = g_strdup_printf("/soc/iommu@%x",
|
|
|
+ (unsigned int) s->memmap[VIRT_IOMMU_SYS].base);
|
|
|
+ iommu_phandle = qemu_fdt_alloc_phandle(fdt);
|
|
|
+ qemu_fdt_add_subnode(fdt, iommu_node);
|
|
|
+
|
|
|
+ qemu_fdt_setprop(fdt, iommu_node, "compatible", comp, sizeof(comp));
|
|
|
+ qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1);
|
|
|
+ qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle);
|
|
|
+
|
|
|
+ qemu_fdt_setprop_cells(fdt, iommu_node, "reg",
|
|
|
+ addr >> 32, addr, size >> 32, size);
|
|
|
+ qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip);
|
|
|
+
|
|
|
+ qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts",
|
|
|
+ iommu_irq_map[0], FDT_IRQ_TYPE_EDGE_LOW,
|
|
|
+ iommu_irq_map[1], FDT_IRQ_TYPE_EDGE_LOW,
|
|
|
+ iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW,
|
|
|
+ iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW);
|
|
|
+
|
|
|
+ qemu_fdt_setprop_cell(fdt, iommu_node, "msi-parent", msi_phandle);
|
|
|
+
|
|
|
+ *iommu_sys_phandle = iommu_phandle;
|
|
|
+}
|
|
|
+
|
|
|
static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf)
|
|
|
{
|
|
|
const char comp[] = "riscv,pci-iommu";
|
|
@@ -1061,6 +1121,7 @@ static void finalize_fdt(RISCVVirtState *s)
|
|
|
{
|
|
|
uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1;
|
|
|
uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1;
|
|
|
+ uint32_t iommu_sys_phandle = 1;
|
|
|
|
|
|
create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle,
|
|
|
&irq_pcie_phandle, &irq_virtio_phandle,
|
|
@@ -1068,7 +1129,12 @@ static void finalize_fdt(RISCVVirtState *s)
|
|
|
|
|
|
create_fdt_virtio(s, virt_memmap, irq_virtio_phandle);
|
|
|
|
|
|
- create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle);
|
|
|
+ if (virt_is_iommu_sys_enabled(s)) {
|
|
|
+ create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle,
|
|
|
+ &iommu_sys_phandle);
|
|
|
+ }
|
|
|
+ create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle,
|
|
|
+ iommu_sys_phandle);
|
|
|
|
|
|
create_fdt_reset(s, virt_memmap, &phandle);
|
|
|
|
|
@@ -1234,7 +1300,7 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
|
|
|
int base_hartid, int hart_count)
|
|
|
{
|
|
|
int i;
|
|
|
- hwaddr addr;
|
|
|
+ hwaddr addr = 0;
|
|
|
uint32_t guest_bits;
|
|
|
DeviceState *aplic_s = NULL;
|
|
|
DeviceState *aplic_m = NULL;
|
|
@@ -1284,6 +1350,10 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
|
|
|
VIRT_IRQCHIP_NUM_PRIO_BITS,
|
|
|
msimode, false, aplic_m);
|
|
|
|
|
|
+ if (kvm_enabled() && msimode) {
|
|
|
+ riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s), addr);
|
|
|
+ }
|
|
|
+
|
|
|
return kvm_enabled() ? aplic_s : aplic_m;
|
|
|
}
|
|
|
|
|
@@ -1364,6 +1434,7 @@ static void virt_machine_done(Notifier *notifier, void *data)
|
|
|
uint64_t fdt_load_addr;
|
|
|
uint64_t kernel_entry = 0;
|
|
|
BlockBackend *pflash_blk0;
|
|
|
+ RISCVBootInfo boot_info;
|
|
|
|
|
|
/*
|
|
|
* An user provided dtb must include everything, including
|
|
@@ -1412,17 +1483,19 @@ static void virt_machine_done(Notifier *notifier, void *data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ riscv_boot_info_init(&boot_info, &s->soc[0]);
|
|
|
+
|
|
|
if (machine->kernel_filename && !kernel_entry) {
|
|
|
- kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0],
|
|
|
+ kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
|
|
|
firmware_end_addr);
|
|
|
-
|
|
|
- kernel_entry = riscv_load_kernel(machine, &s->soc[0],
|
|
|
- kernel_start_addr, true, NULL);
|
|
|
+ riscv_load_kernel(machine, &boot_info, kernel_start_addr,
|
|
|
+ true, NULL);
|
|
|
+ kernel_entry = boot_info.image_low_addr;
|
|
|
}
|
|
|
|
|
|
fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base,
|
|
|
memmap[VIRT_DRAM].size,
|
|
|
- machine);
|
|
|
+ machine, &boot_info);
|
|
|
riscv_load_fdt(fdt_load_addr, machine->fdt);
|
|
|
|
|
|
/* load the reset vector */
|
|
@@ -1565,7 +1638,7 @@ static void virt_machine_init(MachineState *machine)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (kvm_enabled() && virt_use_kvm_aia(s)) {
|
|
|
+ if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) {
|
|
|
kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT,
|
|
|
VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS,
|
|
|
memmap[VIRT_APLIC_S].base,
|
|
@@ -1648,6 +1721,22 @@ static void virt_machine_init(MachineState *machine)
|
|
|
create_fdt(s, memmap);
|
|
|
}
|
|
|
|
|
|
+ if (virt_is_iommu_sys_enabled(s)) {
|
|
|
+ DeviceState *iommu_sys = qdev_new(TYPE_RISCV_IOMMU_SYS);
|
|
|
+
|
|
|
+ object_property_set_uint(OBJECT(iommu_sys), "addr",
|
|
|
+ s->memmap[VIRT_IOMMU_SYS].base,
|
|
|
+ &error_fatal);
|
|
|
+ object_property_set_uint(OBJECT(iommu_sys), "base-irq",
|
|
|
+ IOMMU_SYS_IRQ,
|
|
|
+ &error_fatal);
|
|
|
+ object_property_set_link(OBJECT(iommu_sys), "irqchip",
|
|
|
+ OBJECT(mmio_irqchip),
|
|
|
+ &error_fatal);
|
|
|
+
|
|
|
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(iommu_sys), &error_fatal);
|
|
|
+ }
|
|
|
+
|
|
|
s->machine_done.notify = virt_machine_done;
|
|
|
qemu_add_machine_init_done_notifier(&s->machine_done);
|
|
|
}
|
|
@@ -1661,6 +1750,7 @@ static void virt_machine_instance_init(Object *obj)
|
|
|
s->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
|
|
|
s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
|
|
|
s->acpi = ON_OFF_AUTO_AUTO;
|
|
|
+ s->iommu_sys = ON_OFF_AUTO_AUTO;
|
|
|
}
|
|
|
|
|
|
static char *virt_get_aia_guests(Object *obj, Error **errp)
|
|
@@ -1733,6 +1823,28 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp)
|
|
|
s->have_aclint = value;
|
|
|
}
|
|
|
|
|
|
+bool virt_is_iommu_sys_enabled(RISCVVirtState *s)
|
|
|
+{
|
|
|
+ return s->iommu_sys == ON_OFF_AUTO_ON;
|
|
|
+}
|
|
|
+
|
|
|
+static void virt_get_iommu_sys(Object *obj, Visitor *v, const char *name,
|
|
|
+ void *opaque, Error **errp)
|
|
|
+{
|
|
|
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
|
|
|
+ OnOffAuto iommu_sys = s->iommu_sys;
|
|
|
+
|
|
|
+ visit_type_OnOffAuto(v, name, &iommu_sys, errp);
|
|
|
+}
|
|
|
+
|
|
|
+static void virt_set_iommu_sys(Object *obj, Visitor *v, const char *name,
|
|
|
+ void *opaque, Error **errp)
|
|
|
+{
|
|
|
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
|
|
|
+
|
|
|
+ visit_type_OnOffAuto(v, name, &s->iommu_sys, errp);
|
|
|
+}
|
|
|
+
|
|
|
bool virt_is_acpi_enabled(RISCVVirtState *s)
|
|
|
{
|
|
|
return s->acpi != ON_OFF_AUTO_OFF;
|
|
@@ -1759,10 +1871,12 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
|
|
|
DeviceState *dev)
|
|
|
{
|
|
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
|
|
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(machine);
|
|
|
|
|
|
if (device_is_dynamic_sysbus(mc, dev) ||
|
|
|
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) ||
|
|
|
object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) {
|
|
|
+ s->iommu_sys = ON_OFF_AUTO_OFF;
|
|
|
return HOTPLUG_HANDLER(machine);
|
|
|
}
|
|
|
|
|
@@ -1789,6 +1903,7 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) {
|
|
|
create_fdt_iommu(s, pci_get_bdf(PCI_DEVICE(dev)));
|
|
|
+ s->iommu_sys = ON_OFF_AUTO_OFF;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1851,6 +1966,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
|
|
|
NULL, NULL);
|
|
|
object_class_property_set_description(oc, "acpi",
|
|
|
"Enable ACPI");
|
|
|
+
|
|
|
+ object_class_property_add(oc, "iommu-sys", "OnOffAuto",
|
|
|
+ virt_get_iommu_sys, virt_set_iommu_sys,
|
|
|
+ NULL, NULL);
|
|
|
+ object_class_property_set_description(oc, "iommu-sys",
|
|
|
+ "Enable IOMMU platform device");
|
|
|
}
|
|
|
|
|
|
static const TypeInfo virt_machine_typeinfo = {
|