#include "hw/vfio/vfio-amd-xgbe.h"
#include "hw/devices.h"
#include "net/net.h"
-#include "sysemu/block-backend.h"
#include "sysemu/device_tree.h"
#include "sysemu/numa.h"
#include "sysemu/sysemu.h"
#include "hw/smbios/smbios.h"
#include "qapi/visitor.h"
#include "standard-headers/linux/input.h"
+#include "hw/arm/smmuv3.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
#define PLATFORM_BUS_NUM_IRQS 64
-static ARMPlatformBusSystemParams platform_bus_params;
-
/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means
* RAM can go up to the 256GB mark, leaving 256GB of the physical
* address space unallocated and free for future use between 256G and 512G.
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
[VIRT_SECURE_UART] = { 0x09040000, 0x00001000 },
+ [VIRT_SMMU] = { 0x09050000, 0x00020000 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
[VIRT_SECURE_UART] = 8,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
+ [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
};
ARM_CPU_TYPE_NAME("cortex-a53"),
ARM_CPU_TYPE_NAME("cortex-a57"),
ARM_CPU_TYPE_NAME("host"),
+ ARM_CPU_TYPE_NAME("max"),
};
static bool cpu_type_valid(const char *cpu)
}
}
-static void fdt_add_psci_node(const VirtMachineState *vms)
-{
- uint32_t cpu_suspend_fn;
- uint32_t cpu_off_fn;
- uint32_t cpu_on_fn;
- uint32_t migrate_fn;
- void *fdt = vms->fdt;
- ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
- const char *psci_method;
-
- switch (vms->psci_conduit) {
- case QEMU_PSCI_CONDUIT_DISABLED:
- return;
- case QEMU_PSCI_CONDUIT_HVC:
- psci_method = "hvc";
- break;
- case QEMU_PSCI_CONDUIT_SMC:
- psci_method = "smc";
- break;
- default:
- g_assert_not_reached();
- }
-
- qemu_fdt_add_subnode(fdt, "/psci");
- if (armcpu->psci_version == 2) {
- const char comp[] = "arm,psci-0.2\0arm,psci";
- qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
-
- cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
- if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
- cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
- cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
- migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
- } else {
- cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND;
- cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON;
- migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE;
- }
- } else {
- qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
-
- cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND;
- cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF;
- cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON;
- migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE;
- }
-
- /* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer
- * to the instruction that should be used to invoke PSCI functions.
- * However, the device tree binding uses 'method' instead, so that is
- * what we should use here.
- */
- qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method);
-
- qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn);
- qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn);
- qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn);
- qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
-}
-
static void fdt_add_timer_nodes(const VirtMachineState *vms)
{
/* On real hardware these interrupts are level-triggered.
0x7 /* PCI irq */);
}
-static void create_pcie(const VirtMachineState *vms, qemu_irq *pic)
+static void create_smmu(const VirtMachineState *vms, qemu_irq *pic,
+ PCIBus *bus)
+{
+ char *node;
+ const char compat[] = "arm,smmu-v3";
+ int irq = vms->irqmap[VIRT_SMMU];
+ int i;
+ hwaddr base = vms->memmap[VIRT_SMMU].base;
+ hwaddr size = vms->memmap[VIRT_SMMU].size;
+ const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror";
+ DeviceState *dev;
+
+ if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) {
+ return;
+ }
+
+ dev = qdev_create(NULL, "arm-smmuv3");
+
+ object_property_set_link(OBJECT(dev), OBJECT(bus), "primary-bus",
+ &error_abort);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+ for (i = 0; i < NUM_SMMU_IRQS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
+ }
+
+ node = g_strdup_printf("/smmuv3@%" PRIx64, base);
+ qemu_fdt_add_subnode(vms->fdt, node);
+ qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat));
+ qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", 2, base, 2, size);
+
+ qemu_fdt_setprop_cells(vms->fdt, node, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
+ GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
+ GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
+ GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
+
+ qemu_fdt_setprop(vms->fdt, node, "interrupt-names", irq_names,
+ sizeof(irq_names));
+
+ qemu_fdt_setprop_cell(vms->fdt, node, "clocks", vms->clock_phandle);
+ qemu_fdt_setprop_string(vms->fdt, node, "clock-names", "apb_pclk");
+ qemu_fdt_setprop(vms->fdt, node, "dma-coherent", NULL, 0);
+
+ qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1);
+
+ qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle);
+ g_free(node);
+}
+
+static void create_pcie(VirtMachineState *vms, qemu_irq *pic)
{
hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size;
qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "pci");
qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 3);
qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 2);
+ qemu_fdt_setprop_cell(vms->fdt, nodename, "linux,pci-domain", 0);
qemu_fdt_setprop_cells(vms->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0);
qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 1);
create_pcie_irq_map(vms, vms->gic_phandle, irq, nodename);
+ if (vms->iommu) {
+ vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt);
+
+ create_smmu(vms, pic, pci->bus);
+
+ qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map",
+ 0x0, vms->iommu_phandle, 0x0, 0x10000);
+ }
+
g_free(nodename);
}
DeviceState *dev;
SysBusDevice *s;
int i;
- ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1);
MemoryRegion *sysmem = get_system_memory();
- platform_bus_params.platform_bus_base = vms->memmap[VIRT_PLATFORM_BUS].base;
- platform_bus_params.platform_bus_size = vms->memmap[VIRT_PLATFORM_BUS].size;
- platform_bus_params.platform_bus_first_irq = vms->irqmap[VIRT_PLATFORM_BUS];
- platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS;
-
- fdt_params->system_params = &platform_bus_params;
- fdt_params->binfo = &vms->bootinfo;
- fdt_params->intc = "/intc";
- /*
- * register a machine init done notifier that creates the device tree
- * nodes of the platform bus and its children dynamic sysbus devices
- */
- arm_register_platform_bus_fdt_creator(fdt_params);
-
dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE);
dev->id = TYPE_PLATFORM_BUS_DEVICE;
- qdev_prop_set_uint32(dev, "num_irqs",
- platform_bus_params.platform_bus_num_irqs);
- qdev_prop_set_uint32(dev, "mmio_size",
- platform_bus_params.platform_bus_size);
+ qdev_prop_set_uint32(dev, "num_irqs", PLATFORM_BUS_NUM_IRQS);
+ qdev_prop_set_uint32(dev, "mmio_size", vms->memmap[VIRT_PLATFORM_BUS].size);
qdev_init_nofail(dev);
- s = SYS_BUS_DEVICE(dev);
+ vms->platform_bus_dev = dev;
- for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) {
- int irqn = platform_bus_params.platform_bus_first_irq + i;
+ s = SYS_BUS_DEVICE(dev);
+ for (i = 0; i < PLATFORM_BUS_NUM_IRQS; i++) {
+ int irqn = vms->irqmap[VIRT_PLATFORM_BUS] + i;
sysbus_connect_irq(s, i, pic[irqn]);
}
memory_region_add_subregion(sysmem,
- platform_bus_params.platform_bus_base,
+ vms->memmap[VIRT_PLATFORM_BUS].base,
sysbus_mmio_get_region(s, 0));
}
static void virt_build_smbios(VirtMachineState *vms)
{
+ MachineClass *mc = MACHINE_GET_CLASS(vms);
+ VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
uint8_t *smbios_tables, *smbios_anchor;
size_t smbios_tables_len, smbios_anchor_len;
const char *product = "QEMU Virtual Machine";
}
smbios_set_defaults("QEMU", product,
- "1.0", false, true, SMBIOS_ENTRY_POINT_30);
+ vmc->smbios_old_sys_ver ? "1.0" : mc->name, false,
+ true, SMBIOS_ENTRY_POINT_30);
smbios_get_tables(NULL, 0, &smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len);
{
VirtMachineState *vms = container_of(notifier, VirtMachineState,
machine_done);
+ ARMCPU *cpu = ARM_CPU(first_cpu);
+ struct arm_boot_info *info = &vms->bootinfo;
+ AddressSpace *as = arm_boot_address_space(cpu, info);
+
+ /*
+ * If the user provided a dtb, we assume the dynamic sysbus nodes
+ * already are integrated there. This corresponds to a use case where
+ * the dynamic sysbus nodes are complex and their generation is not yet
+ * supported. In that case the user can take charge of the guest dt
+ * while qemu takes charge of the qom stuff.
+ */
+ if (info->dtb_filename == NULL) {
+ platform_bus_add_all_fdt_nodes(vms->fdt, "/intc",
+ vms->memmap[VIRT_PLATFORM_BUS].base,
+ vms->memmap[VIRT_PLATFORM_BUS].size,
+ vms->irqmap[VIRT_PLATFORM_BUS]);
+ }
+ if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) {
+ exit(1);
+ }
virt_acpi_setup(vms);
virt_build_smbios(vms);
/* We can probe only here because during property set
* KVM is not available yet
*/
- if (!vms->gic_version) {
+ if (vms->gic_version <= 0) {
+ /* "host" or "max" */
if (!kvm_enabled()) {
- error_report("gic-version=host requires KVM");
- exit(1);
- }
-
- vms->gic_version = kvm_arm_vgic_probe();
- if (!vms->gic_version) {
- error_report("Unable to determine GIC version supported by host");
- exit(1);
+ if (vms->gic_version == 0) {
+ error_report("gic-version=host requires KVM");
+ exit(1);
+ } else {
+ /* "max": currently means 3 for TCG */
+ vms->gic_version = 3;
+ }
+ } else {
+ vms->gic_version = kvm_arm_vgic_probe();
+ if (!vms->gic_version) {
+ error_report(
+ "Unable to determine GIC version supported by host");
+ exit(1);
+ }
}
}
break;
}
- cpuobj = object_new(machine->cpu_type);
+ cpuobj = object_new(possible_cpus->cpus[n].type);
object_property_set_int(cpuobj, possible_cpus->cpus[n].arch_id,
"mp-affinity", NULL);
"secure-memory", &error_abort);
}
- object_property_set_bool(cpuobj, true, "realized", NULL);
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
object_unref(cpuobj);
}
fdt_add_timer_nodes(vms);
fdt_add_cpu_nodes(vms);
- fdt_add_psci_node(vms);
memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram",
machine->ram_size);
fdt_add_pmu_nodes(vms);
- create_uart(vms, pic, VIRT_UART, sysmem, serial_hds[0]);
+ create_uart(vms, pic, VIRT_UART, sysmem, serial_hd(0));
if (vms->secure) {
create_secure_ram(vms, secure_sysmem);
- create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hds[1]);
+ create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1));
}
create_rtc(vms, pic);
vms->fw_cfg = create_fw_cfg(vms, &address_space_memory);
rom_set_fw(vms->fw_cfg);
- vms->machine_done.notify = virt_machine_done;
- qemu_add_machine_init_done_notifier(&vms->machine_done);
+ create_platform_bus(vms, pic);
vms->bootinfo.ram_size = machine->ram_size;
vms->bootinfo.kernel_filename = machine->kernel_filename;
vms->bootinfo.board_id = -1;
vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base;
vms->bootinfo.get_dtb = machvirt_dtb;
+ vms->bootinfo.skip_dtb_autoload = true;
vms->bootinfo.firmware_loaded = firmware_loaded;
arm_load_kernel(ARM_CPU(first_cpu), &vms->bootinfo);
- /*
- * arm_load_kernel machine init done notifier registration must
- * happen before the platform_bus_create call. In this latter,
- * another notifier is registered which adds platform bus nodes.
- * Notifiers are executed in registration reverse order.
- */
- create_platform_bus(vms, pic);
+ vms->machine_done.notify = virt_machine_done;
+ qemu_add_machine_init_done_notifier(&vms->machine_done);
}
static bool virt_get_secure(Object *obj, Error **errp)
vms->gic_version = 2;
} else if (!strcmp(value, "host")) {
vms->gic_version = 0; /* Will probe later */
+ } else if (!strcmp(value, "max")) {
+ vms->gic_version = -1; /* Will probe later */
} else {
error_setg(errp, "Invalid gic-version value");
- error_append_hint(errp, "Valid values are 3, 2, host.\n");
+ error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
+ }
+}
+
+static char *virt_get_iommu(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ switch (vms->iommu) {
+ case VIRT_IOMMU_NONE:
+ return g_strdup("none");
+ case VIRT_IOMMU_SMMUV3:
+ return g_strdup("smmuv3");
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void virt_set_iommu(Object *obj, const char *value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ if (!strcmp(value, "smmuv3")) {
+ vms->iommu = VIRT_IOMMU_SMMUV3;
+ } else if (!strcmp(value, "none")) {
+ vms->iommu = VIRT_IOMMU_NONE;
+ } else {
+ error_setg(errp, "Invalid iommu value");
+ error_append_hint(errp, "Valid values are none, smmuv3.\n");
}
}
sizeof(CPUArchId) * max_cpus);
ms->possible_cpus->len = max_cpus;
for (n = 0; n < ms->possible_cpus->len; n++) {
+ ms->possible_cpus->cpus[n].type = ms->cpu_type;
ms->possible_cpus->cpus[n].arch_id =
virt_cpu_mp_affinity(vms, n);
ms->possible_cpus->cpus[n].props.has_thread_id = true;
return ms->possible_cpus;
}
+static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+
+ if (vms->platform_bus_dev) {
+ if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ platform_bus_link_device(PLATFORM_BUS_DEVICE(vms->platform_bus_dev),
+ SYS_BUS_DEVICE(dev));
+ }
+ }
+}
+
+static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
+ DeviceState *dev)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ return HOTPLUG_HANDLER(machine);
+ }
+
+ return NULL;
+}
+
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
mc->init = machvirt_init;
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
mc->cpu_index_to_instance_props = virt_cpu_index_to_props;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
mc->get_default_cpu_node_id = virt_get_default_cpu_node_id;
+ assert(!mc->get_hotplug_handler);
+ mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+ hc->plug = virt_machine_device_plug_cb;
}
static const TypeInfo virt_machine_info = {
.instance_size = sizeof(VirtMachineState),
.class_size = sizeof(VirtMachineClass),
.class_init = virt_machine_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ },
};
static void machvirt_machine_init(void)
}
type_init(machvirt_machine_init);
+#define VIRT_COMPAT_2_12 \
+ HW_COMPAT_2_12
+
static void virt_2_12_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
NULL);
}
+ /* Default disallows iommu instantiation */
+ vms->iommu = VIRT_IOMMU_NONE;
+ object_property_add_str(obj, "iommu", virt_get_iommu, virt_set_iommu, NULL);
+ object_property_set_description(obj, "iommu",
+ "Set the IOMMU type. "
+ "Valid values are none and smmuv3",
+ NULL);
+
vms->memmap = a15memmap;
vms->irqmap = a15irqmap;
}
static void virt_machine_2_12_options(MachineClass *mc)
{
+ SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12);
}
DEFINE_VIRT_MACHINE_AS_LATEST(2, 12)
static void virt_machine_2_11_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
virt_machine_2_12_options(mc);
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_11);
+ vmc->smbios_old_sys_ver = true;
}
DEFINE_VIRT_MACHINE(2, 11)