AMD IOMMU: allow command line overrides for broken IVRS tables
authorJan Beulich <jbeulich@suse.com>
Thu, 29 Aug 2013 07:53:07 +0000 (09:53 +0200)
committerJan Beulich <jbeulich@suse.com>
Thu, 29 Aug 2013 07:53:07 +0000 (09:53 +0200)
With there being so many systems with broken ACPI tables, and with it
generally being known what's wrong with those tables, give people a
handle to overcome the resulting disabling of their IOMMUs.

Inspired by Linux side patches providing similar functionality.

Suggested-by: Sander Eikelenboom <linux@eikelenboom.it>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Tested-By: Sander Eikelenboom <linux@eikelenboom.it>
Acked-by: Keir Fraser <keir@xen.org>
Acked-by: Suravee Suthikulpanit <suravee.suthikulapanit@amd.com>
docs/misc/xen-command-line.markdown
xen/common/kernel.c
xen/drivers/passthrough/amd/iommu_acpi.c
xen/include/asm-x86/hvm/svm/amd-iommu-proto.h

index 13c0306c61166555cdf11cabd9f4a8f35b5676d6..a86014b4d602c9797b6d9a775c91b281e6eaf7f3 100644 (file)
@@ -578,6 +578,20 @@ debug hypervisor only).
 > `= <integer>`
 
 ### irq\_vector\_map
+### ivrs_hpet[`<hpet>`]
+> `=[<seg>:]<bus>:<device>.<func>`
+
+Force the use of `[<seg>:]<bus>:<device>.<func>` as device ID of HPET
+`<hpet>` instead of the one specified by the IVHD sub-tables of the IVRS
+ACPI table.
+
+### ivrs_ioapic[`<ioapic>`]
+> `=[<seg>:]<bus>:<device>.<func>`
+
+Force the use of `[<seg>:]<bus>:<device>.<func>` as device ID of IO-APIC
+`<ioapic>` instead of the one specified by the IVHD sub-tables of the IVRS
+ACPI table.
+
 ### lapic
 
 Force the use of use of the local APIC on a uniprocessor system, even
index 72fb9055b1851766760a10b8f5b7bda9314c6b6d..b8707d90a14bf206da4d4d7e712d41ddfa0887b4 100644 (file)
@@ -81,9 +81,15 @@ void __init cmdline_parse(const char *cmdline)
         /* Search for value part of a key=value option. */
         optval = strchr(opt, '=');
         if ( optval != NULL )
+        {
             *optval++ = '\0'; /* nul-terminate the option value */
+            q = strpbrk(opt, "([{<");
+        }
         else
+        {
             optval = q;       /* default option value is empty string */
+            q = NULL;
+        }
 
         /* Boolean parameters can be inverted with 'no-' prefix. */
         bool_assert = !!strncmp("no-", optkey, 3);
@@ -93,7 +99,17 @@ void __init cmdline_parse(const char *cmdline)
         for ( param = &__setup_start; param < &__setup_end; param++ )
         {
             if ( strcmp(param->name, optkey) )
+            {
+                if ( param->type == OPT_CUSTOM && q &&
+                     strlen(param->name) == q + 1 - opt &&
+                     !strncmp(param->name, opt, q + 1 - opt) )
+                {
+                    optval[-1] = '=';
+                    ((void (*)(const char *))param->var)(q);
+                    optval[-1] = '\0';
+                }
                 continue;
+            }
 
             switch ( param->type )
             {
index c903f9385c1c18bcd906ff7ff5d958925eefe3fb..89b359c455cd7dc38361bdfe15f793bf5516c1de 100644 (file)
@@ -633,6 +633,50 @@ static u16 __init parse_ivhd_device_extended_range(
     return dev_length;
 }
 
+static __initdata DECLARE_BITMAP(ioapic_cmdline, ARRAY_SIZE(ioapic_sbdf));
+
+static void __init parse_ivrs_ioapic(char *str)
+{
+    const char *s = str;
+    unsigned long id;
+    unsigned int seg, bus, dev, func;
+
+    ASSERT(*s == '[');
+    id = simple_strtoul(s + 1, &s, 0);
+    if ( id >= ARRAY_SIZE(ioapic_sbdf) || *s != ']' || *++s != '=' )
+        return;
+
+    s = parse_pci(s + 1, &seg, &bus, &dev, &func);
+    if ( !s || *s )
+        return;
+
+    ioapic_sbdf[id].bdf = PCI_BDF(bus, dev, func);
+    ioapic_sbdf[id].seg = seg;
+    __set_bit(id, ioapic_cmdline);
+}
+custom_param("ivrs_ioapic[", parse_ivrs_ioapic);
+
+static void __init parse_ivrs_hpet(char *str)
+{
+    const char *s = str;
+    unsigned long id;
+    unsigned int seg, bus, dev, func;
+
+    ASSERT(*s == '[');
+    id = simple_strtoul(s + 1, &s, 0);
+    if ( id != (typeof(hpet_sbdf.id))id || *s != ']' || *++s != '=' )
+        return;
+
+    s = parse_pci(s + 1, &seg, &bus, &dev, &func);
+    if ( !s || *s )
+        return;
+
+    hpet_sbdf.bdf = PCI_BDF(bus, dev, func);
+    hpet_sbdf.seg = seg;
+    hpet_sbdf.cmdline = 1;
+}
+custom_param("ivrs_hpet[", parse_ivrs_hpet);
+
 static u16 __init parse_ivhd_device_special(
     const struct acpi_ivrs_device8c *special, u16 seg,
     u16 header_length, u16 block_length, struct amd_iommu *iommu)
@@ -681,7 +725,10 @@ static u16 __init parse_ivhd_device_special(
                 return 0;
             }
 
-            if ( ioapic_sbdf[special->handle].pin_2_idx )
+            if ( test_bit(special->handle, ioapic_cmdline) )
+                AMD_IOMMU_DEBUG("IVHD: Command line override present for IO-APIC %#x\n",
+                                special->handle);
+            else if ( ioapic_sbdf[special->handle].pin_2_idx )
             {
                 if ( ioapic_sbdf[special->handle].bdf == bdf &&
                      ioapic_sbdf[special->handle].seg == seg )
@@ -724,14 +771,18 @@ static u16 __init parse_ivhd_device_special(
         break;
     case ACPI_IVHD_HPET:
         /* set device id of hpet */
-        if ( hpet_sbdf.iommu )
+        if ( hpet_sbdf.iommu ||
+             (hpet_sbdf.cmdline && hpet_sbdf.id != special->handle) )
         {
             printk(XENLOG_WARNING "Only one IVHD HPET entry is supported\n");
             break;
         }
         hpet_sbdf.id = special->handle;
-        hpet_sbdf.bdf = bdf;
-        hpet_sbdf.seg = seg;
+        if ( !hpet_sbdf.cmdline )
+        {
+            hpet_sbdf.bdf = bdf;
+            hpet_sbdf.seg = seg;
+        }
         hpet_sbdf.iommu = iommu;
         break;
     default:
@@ -942,22 +993,23 @@ static int __init parse_ivrs_table(struct acpi_table_header *table)
              ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx )
             continue;
 
-        printk(XENLOG_ERR "IVHD Error: no information for IO-APIC %#x\n",
-               IO_APIC_ID(apic));
-        if ( amd_iommu_perdev_intremap )
-            error = -ENXIO;
+        if ( !test_bit(IO_APIC_ID(apic), ioapic_cmdline) )
+        {
+            printk(XENLOG_ERR "IVHD Error: no information for IO-APIC %#x\n",
+                   IO_APIC_ID(apic));
+            if ( amd_iommu_perdev_intremap )
+                return -ENXIO;
+        }
+
+        ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx = xmalloc_array(
+            u16, nr_ioapic_entries[apic]);
+        if ( ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx )
+            memset(ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx, -1,
+                   nr_ioapic_entries[apic] * sizeof(*ioapic_sbdf->pin_2_idx));
         else
         {
-            ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx = xmalloc_array(
-                u16, nr_ioapic_entries[apic]);
-            if ( ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx )
-                memset(ioapic_sbdf[IO_APIC_ID(apic)].pin_2_idx, -1,
-                       nr_ioapic_entries[apic] * sizeof(*ioapic_sbdf->pin_2_idx));
-            else
-            {
-                printk(XENLOG_ERR "IVHD Error: Out of memory\n");
-                error = -ENOMEM;
-            }
+            printk(XENLOG_ERR "IVHD Error: Out of memory\n");
+            error = -ENOMEM;
         }
     }
 
index 7736ff4d6889e0514cca5f79da94fac7f881e152..3e6961d5c0dec252684cea03fa4dd44873c26d08 100644 (file)
@@ -108,6 +108,7 @@ extern struct ioapic_sbdf {
 
 extern struct hpet_sbdf {
     u16 bdf, seg, id;
+    bool_t cmdline;
     struct amd_iommu *iommu;
 } hpet_sbdf;