The virt board currently only supports either a GICv2 with a possible
GICv2m for handling MSI interrupts, or else a GICv3 with a possible
ITS for handling MSI interrupts.  There is one board property, which
lets you say "its=(off|on)" and controls only whether there is an ITS
or not.

This is awkward for macOS HVF, where you get a GICv3
without ITS and MSI handling needs a GICv2m.

Create a new board property "msi" which gives the user clearer
control over how MSI interrupts are handled:

 - msi=its : create an ITS
 - msi=gicv2m : create a GICv2m
 - msi=off : do not create any MSI handling device
 - msi=auto : create the best MSI handling device available
              for the GIC version and accelerator

The default is 'auto'.

The existing 'its' property becomes a deprecated property
kept for compatibility. Existing users of "its=on" should
prefer "msi=auto"; users of "its=off" should use "msi=off".

The backwards compatibility cases we need to support are:
 (1) TCG, virt-6.1 and earlier: no_tcg_its is set
   -- you can have a gicv2 (always with a gicv2m)
   -- if you specify gic-version=3 you get a GICv3 without ITS
 (2) TCG, virt-6.2 and later:
   -- gic-version=2 still has gicv2m
   -- gic-version=3 by default gives you an ITS; if you also
      say its=off you get GICv3 with no ITS
   -- there is no case where we provide a GICv3 and are
      unable to provide an ITS for it
 (3) KVM (any version):
   -- gic-version=2 has a gicv2m
   -- gic-version=3 gives you an ITS by default; its=off
      will remove it
   -- there is no case where we provide a GICv3 and are
      unable to provide an ITS for it
 (4) HVF:
   -- only gic-version=2 works, you get a gicv2m

Signed-off-by: Peter Maydell <[email protected]>
---
 docs/system/arm/virt.rst | 21 ++++++++++++-
 hw/arm/virt.c            | 64 +++++++++++++++++++++++++++++++++++++++-
 include/hw/arm/virt.h    |  1 +
 3 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index e5570773ba..4cc54e38db 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -167,9 +167,28 @@ gic-version
     with TCG this is currently ``3`` if ``virtualization`` is ``off`` and
     ``4`` if ``virtualization`` is ``on``, but this may change in future)
 
+msi
+  Specify the MSI controller to use to handle MSI and MSI-X interrupts
+  from PCI devices. Valid values are:
+
+  ``its``
+     ITS, which can be used with a GICv3 or better.
+  ``gicv2m``
+     The GICv2m; this is typically used with a GICv2, but it is possible
+     to use it with a GICv3.
+  ``none``
+     Do not provide any MSI controller. MSI and MSI-X interrupts will
+     not be supported.
+  ``auto``
+     Pick the best available controller. This will be an ITS if the
+     GIC and virtualization accelerator support it, and a GICv2m if not.
+     This is the default.
+
 its
   Set ``on``/``off`` to enable/disable ITS instantiation. The default is ``on``
-  for machine types later than ``virt-2.7``.
+  for machine types later than ``virt-2.7``. This is a deprecated option;
+  instead of ``its=on`` use ``msi=its`` or ``msi=auto``, and instead of
+  ``its=off`` use ``msi=none``.
 
 iommu
   Set the IOMMU type to create for the guest. Valid values are:
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index b65f571532..471852e4b1 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -957,6 +957,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion 
*mem)
     case VIRT_MSI_CTRL_GICV2M:
         create_v2m(vms);
         break;
+    default:
+        g_assert_not_reached();
     }
 }
 
@@ -2084,6 +2086,23 @@ static void finalize_msi_controller(VirtMachineState 
*vms)
      */
     VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
 
+    if (vms->msi_controller != VIRT_MSI_CTRL_NOSEL) {
+        /*
+         * User specified an "msi" option: check what they
+         * specified, and use it.
+         */
+        if (vms->msi_controller == VIRT_MSI_CTRL_ITS &&
+            vms->gic_version == VIRT_GIC_VERSION_2) {
+            error_report("A GICv2 cannot use an ITS");
+            exit(1);
+        }
+        return;
+    }
+
+    /*
+     * Pick a "best available" MSI controller, including handling
+     * the legacy "its" option and the no_tcg_its compat flag.
+     */
     if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) {
         if (!kvm_irqchip_in_kernel() && vmc->no_tcg_its) {
             vms->msi_controller = VIRT_MSI_CTRL_NONE;
@@ -2092,6 +2111,8 @@ static void finalize_msi_controller(VirtMachineState *vms)
         }
     } else if (vms->gic_version == VIRT_GIC_VERSION_2) {
         vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
+    } else {
+        vms->msi_controller = VIRT_MSI_CTRL_NONE;
     }
 }
 
@@ -2881,6 +2902,36 @@ static void virt_set_gic_version(Object *obj, const char 
*value, Error **errp)
     }
 }
 
+static const char *msi_option_values[] = {
+    [VIRT_MSI_CTRL_NONE] = "off",
+    [VIRT_MSI_CTRL_GICV2M] = "gicv2m",
+    [VIRT_MSI_CTRL_ITS] = "its",
+    [VIRT_MSI_CTRL_NOSEL] = "auto",
+};
+
+static char *virt_get_msi(Object *obj, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(obj);
+
+    assert(vms->msi_controller >= 0 &&
+           vms->msi_controller < ARRAY_SIZE(msi_option_values));
+    return g_strdup(msi_option_values[vms->msi_controller]);
+}
+
+static void virt_set_msi(Object *obj, const char *value, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(obj);
+
+    for (int i = 0; i < ARRAY_SIZE(msi_option_values); i++) {
+        if (!strcmp(value, msi_option_values[i])) {
+            vms->msi_controller = i;
+            return;
+        }
+    }
+    error_setg(errp, "Invalid msi value");
+    error_append_hint(errp, "Valid values are off, its, gicv2m, auto.\n");
+}
+
 static char *virt_get_iommu(Object *obj, Error **errp)
 {
     VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -3056,6 +3107,8 @@ 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;
+        default:
+            g_assert_not_reached();
         }
         resv_prop_str = g_strdup_printf("0x%"PRIx64":0x%"PRIx64":%u",
                                         db_start, db_end,
@@ -3454,7 +3507,13 @@ static void virt_machine_class_init(ObjectClass *oc, 
const void *data)
                                    virt_set_its);
     object_class_property_set_description(oc, "its",
                                           "Set on/off to enable/disable "
-                                          "ITS instantiation");
+                                          "ITS instantiation. Deprecated; "
+                                          "use the msi option instead");
+
+    object_class_property_add_str(oc, "msi", virt_get_msi, virt_set_msi);
+    object_class_property_set_description(oc, "msi",
+                                          "Set to configure MSI handling. "
+                                          "Valid values are auto, its, gicv2m, 
and off");
 
     object_class_property_add_bool(oc, "dtb-randomness",
                                    virt_get_dtb_randomness,
@@ -3514,6 +3573,9 @@ static void virt_instance_init(Object *obj)
     /* Default allows ITS instantiation */
     vms->its = true;
 
+    /* Default to autoselection of MSI controller */
+    vms->msi_controller = VIRT_MSI_CTRL_NOSEL;
+
     /* Default disallows iommu instantiation */
     vms->iommu = VIRT_IOMMU_NONE;
 
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 577b4b3362..53f1dc2199 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -103,6 +103,7 @@ typedef enum VirtMSIControllerType {
     VIRT_MSI_CTRL_NONE,
     VIRT_MSI_CTRL_GICV2M,
     VIRT_MSI_CTRL_ITS,
+    VIRT_MSI_CTRL_NOSEL,
 } VirtMSIControllerType;
 
 typedef enum VirtGICType {
-- 
2.47.3


Reply via email to