Introduce a -M msi= argument to be able to control MSI-X support independently
from ITS, as part of supporting GICv3 + GICv2m platforms.
Remove vms->its as it's no longer needed after that change.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/virt-acpi-build.c | 20 ++++---
hw/arm/virt.c | 119 ++++++++++++++++++++++++++++++++++++---
include/hw/arm/virt.h | 5 +-
3 files changed, 128 insertions(+), 16 deletions(-)
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index c145678185..544004615d 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -569,7 +569,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
nb_nodes = num_smmus + 1; /* RC and SMMUv3 */
rc_mapping_count = rc_smmu_idmaps_len;
- if (vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
/*
* Knowing the ID ranges from the RC to the SMMU, it's possible to
* determine the ID ranges from RC that go directly to ITS.
@@ -590,7 +590,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
}
} else {
- if (vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
nb_nodes = 2; /* RC and ITS */
rc_mapping_count = 1; /* Direct map to ITS */
} else {
@@ -605,7 +605,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, IORT_NODE_OFFSET, 4);
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
- if (vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
/* Table 12 ITS Group Format */
build_append_int_noprefix(table_data, 0 /* ITS Group */, 1); /* Type */
node_size = 20 /* fixed header size */ + 4 /* 1 GIC ITS Identifier */;
@@ -624,7 +624,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
int smmu_mapping_count, offset_to_id_array;
int irq = sdev->irq;
- if (vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
smmu_mapping_count = 1; /* ITS Group node */
offset_to_id_array = SMMU_V3_ENTRY_SIZE; /* Just after the header */
} else {
@@ -717,7 +717,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
}
- if (vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
/*
* Map bypassed (don't go through the SMMU) RIDs (input) to
* ITS Group node directly: RC -> ITS.
@@ -735,7 +735,9 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
* SMMU: RC -> ITS.
* Output IORT node is the ITS Group node (the first node).
*/
- build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET, 0);
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
+ build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET, 0);
+ }
}
build_iort_rmr_nodes(table_data, smmuv3_devs, &id);
@@ -1053,7 +1055,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
memmap[VIRT_HIGH_GIC_REDIST2].size);
}
- if (vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
/*
* ACPI spec, Revision 6.0 Errata A
* (original 6.0 definition has invalid Length)
@@ -1067,7 +1069,9 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8);
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
}
- } else {
+ }
+
+ if (vms->msi_controller == VIRT_MSI_CTRL_GICV2M) {
const uint16_t spi_base = vms->irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE;
/* 5.2.12.16 GIC MSI Frame Structure */
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 390845c503..aa5e992712 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -737,7 +737,6 @@ static void create_its(VirtMachineState *vms)
{
DeviceState *dev;
- assert(vms->its);
if (!kvm_irqchip_in_kernel() && !vms->tcg_its) {
/*
* Do nothing if ITS is neither supported by the host nor emulated by
@@ -957,9 +956,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
fdt_add_gic_node(vms);
- if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) {
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
create_its(vms);
- } else if (vms->gic_version == VIRT_GIC_VERSION_2) {
+ } else if (vms->msi_controller == VIRT_MSI_CTRL_GICV2M) {
create_v2m(vms);
}
}
@@ -2137,6 +2136,44 @@ static void finalize_gic_version(VirtMachineState *vms)
gics_supported, max_cpus);
}
+static void finalize_msi_controller(VirtMachineState *vms)
+{
+ /*
+ * VIRT_MSI_LEGACY_OPT_ITS_OFF is an option to replicate
+ * behavior of its=off when running with a GICv2, where a
+ * GICv2m is still present. Otherwise, it behaves the same
+ * as msi=off.
+ */
+ if (vms->msi_controller == VIRT_MSI_LEGACY_OPT_ITS_OFF) {
+ if (vms->gic_version == 2) {
+ vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
+ } else {
+ vms->msi_controller = VIRT_MSI_CTRL_NONE;
+ }
+ }
+ if (vms->msi_controller == VIRT_MSI_CTRL_AUTO) {
+ if (vms->gic_version == VIRT_GIC_VERSION_2) {
+ vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
+ } else {
+ vms->msi_controller = VIRT_MSI_CTRL_ITS;
+ }
+ }
+
+ if (vms->msi_controller == VIRT_MSI_CTRL_ITS) {
+ if (vms->gic_version == VIRT_GIC_VERSION_2) {
+ /*
+ * The legacy its= option in earlier releases allowed specifying
+ * this configuration and treated it as GICv3 + GICv2m.
+ * Diagnose it as an error even for that case.
+ */
+ error_report("GICv2 + ITS is an invalid configuration.");
+ exit(1);
+ }
+ }
+
+ assert(vms->msi_controller != VIRT_MSI_CTRL_AUTO);
+}
+
/*
* virt_post_cpus_gic_realized() must be called after the CPUs and
* the GIC have both been realized.
@@ -2256,6 +2293,7 @@ static void machvirt_init(MachineState *machine)
* KVM is not available yet
*/
finalize_gic_version(vms);
+ finalize_msi_controller(vms);
if (vms->secure) {
/*
@@ -2705,18 +2743,76 @@ static void virt_set_highmem_mmio_size(Object *obj, Visitor *v,
extended_memmap[VIRT_HIGH_PCIE_MMIO].size = size;
}
+static char *virt_get_msi(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+ const char *val;
+
+ switch (vms->msi_controller) {
+ case VIRT_MSI_CTRL_NONE:
+ case VIRT_MSI_LEGACY_OPT_ITS_OFF:
+ val = "off";
+ break;
+ case VIRT_MSI_CTRL_ITS:
+ val = "its";
+ break;
+ case VIRT_MSI_CTRL_GICV2M:
+ val = "gicv2m";
+ break;
+ case VIRT_MSI_CTRL_AUTO:
+ val = "auto";
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return g_strdup(val);
+}
+
+static void virt_set_msi(Object *obj, const char *value, Error **errp)
+{
+ ERRP_GUARD();
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ if (!strcmp(value, "auto")) {
+ vms->msi_controller = VIRT_MSI_CTRL_AUTO; /* Will be overriden later */
+ } else if (!strcmp(value, "its")) {
+ vms->msi_controller = VIRT_MSI_CTRL_ITS;
+ } else if (!strcmp(value, "gicv2m")) {
+ vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
+ } else if (!strcmp(value, "off")) {
+ vms->msi_controller = VIRT_MSI_CTRL_NONE;
+ } else {
+ error_setg(errp, "Invalid msi value");
+ error_append_hint(errp, "Valid values are auto, gicv2m, its, off\n");
+ }
+}
+
static bool virt_get_its(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- return vms->its;
+ switch (vms->msi_controller) {
+ case VIRT_MSI_CTRL_AUTO:
+ case VIRT_MSI_CTRL_ITS:
+ return true;
+ case VIRT_MSI_CTRL_NONE:
+ case VIRT_MSI_CTRL_GICV2M:
+ case VIRT_MSI_LEGACY_OPT_ITS_OFF:
+ return false;
+ default:
+ g_assert_not_reached();
+ }
}
static void virt_set_its(Object *obj, bool value, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- vms->its = value;
+ if (value) {
+ vms->msi_controller = VIRT_MSI_CTRL_ITS;
+ } else {
+ vms->msi_controller = VIRT_MSI_LEGACY_OPT_ITS_OFF;
+ }
}
static bool virt_get_dtb_randomness(Object *obj, Error **errp)
@@ -3043,6 +3139,9 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
db_start = base_memmap[VIRT_GIC_V2M].base;
db_end = db_start + base_memmap[VIRT_GIC_V2M].size - 1;
break;
+ case VIRT_MSI_CTRL_AUTO:
+ case VIRT_MSI_LEGACY_OPT_ITS_OFF:
+ g_assert_not_reached();
}
resv_prop_str = g_strdup_printf("0x%"PRIx64":0x%"PRIx64":%u",
db_start, db_end,
@@ -3463,6 +3562,12 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
"Set on/off to enable/disable "
"ITS instantiation");
+ object_class_property_add_str(oc, "msi", virt_get_msi,
+ virt_set_msi);
+ object_class_property_set_description(oc, "msi",
+ "Set MSI settings. "
+ "Valid values are auto, gicv2m, its and off");
+
object_class_property_add_bool(oc, "dtb-randomness",
virt_get_dtb_randomness,
virt_set_dtb_randomness);
@@ -3518,8 +3623,8 @@ static void virt_instance_init(Object *obj)
vms->highmem_mmio = true;
vms->highmem_redists = true;
- /* Default allows ITS instantiation */
- vms->its = true;
+ /* Default allows ITS instantiation if available */
+ vms->msi_controller = VIRT_MSI_CTRL_AUTO;
/* Allow ITS emulation if the machine version supports it */
vms->tcg_its = !vmc->no_tcg_its;
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 3b382bdf49..8069422769 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -101,6 +101,10 @@ typedef enum VirtIOMMUType {
typedef enum VirtMSIControllerType {
VIRT_MSI_CTRL_NONE,
+ /* This value is overriden at runtime.*/
+ VIRT_MSI_CTRL_AUTO,
+ /* Legacy option: its=off provides a GICv2m when using GICv2 */
+ VIRT_MSI_LEGACY_OPT_ITS_OFF,
VIRT_MSI_CTRL_GICV2M,
VIRT_MSI_CTRL_ITS,
} VirtMSIControllerType;
@@ -146,7 +150,6 @@ struct VirtMachineState {
bool highmem_ecam;
bool highmem_mmio;
bool highmem_redists;
- bool its;
bool tcg_its;
bool virt;
bool ras;
--
2.50.1 (Apple Git-155)