]> xenbits.xensource.com Git - people/iwj/xen.git/commitdiff
x86/iommu: add map-reserved dom0-iommu option to map reserved memory ranges
authorRoger Pau Monné <roger.pau@citrix.com>
Fri, 7 Sep 2018 09:08:00 +0000 (11:08 +0200)
committerJan Beulich <jbeulich@suse.com>
Wed, 12 Sep 2018 14:33:29 +0000 (16:33 +0200)
Several people have reported hardware issues (malfunctioning USB
controllers) due to iommu page faults on Intel hardware. Those faults
are caused by missing RMRR (VTd) entries in the ACPI tables. Those can
be worked around on VTd hardware by manually adding RMRR entries on
the command line, this is however limited to Intel hardware and quite
cumbersome to do.

In order to solve those issues add a new dom0-iommu=map-reserved
option that identity maps all regions marked as reserved in the memory
map. Note that regions used by devices emulated by Xen (LAPIC, IO-APIC
or PCIe MCFG regions) are specifically avoided. Note that this option
is available to all Dom0 modes (as opposed to the inclusive option
which only works for PV Dom0).

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Julien Grall <julien.grall@arm.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>
Acked-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
docs/misc/xen-command-line.markdown
xen/arch/x86/hvm/io.c
xen/drivers/passthrough/amd/pci_amd_iommu.c
xen/drivers/passthrough/arm/smmu.c
xen/drivers/passthrough/iommu.c
xen/drivers/passthrough/vtd/iommu.c
xen/drivers/passthrough/x86/iommu.c
xen/include/asm-x86/hvm/io.h
xen/include/xen/iommu.h

index 98f0f3b68b52c75882f21c558f301d72e662b2e4..1ffd5862240cf36649aa3b3954e46ac5520ec753 100644 (file)
@@ -704,6 +704,15 @@ This list of booleans controls the iommu usage by Dom0:
   option is only applicable to a PV Dom0 and is enabled by default on Intel
   hardware.
 
+* `map-reserved`: sets up DMA remapping for all the reserved regions in the
+  memory map for Dom0. Use this to work around firmware issues providing
+  incorrect RMRR/IVMD entries. Rather than only mapping RAM pages for IOMMU
+  accesses for Dom0, all memory regions marked as reserved in the memory map
+  that don't overlap with any MMIO region from emulated devices will be
+  identity mapped. This option maps a subset of the memory that would be
+  mapped when using the `map-inclusive` option. This option is available to all
+  Dom0 modes and is enabled by default on Intel hardware.
+
 ### dom0\_ioports\_disable (x86)
 > `= List of <hex>-<hex>`
 
index 47d6c850cab5c615e0399b19f784b625d4f783e0..a5b0a23f06e2960dcbeb390caf1de53b2d9febbb 100644 (file)
@@ -404,6 +404,11 @@ static const struct hvm_mmcfg *vpci_mmcfg_find(const struct domain *d,
     return NULL;
 }
 
+bool vpci_is_mmcfg_address(const struct domain *d, paddr_t addr)
+{
+    return vpci_mmcfg_find(d, addr);
+}
+
 static unsigned int vpci_mmcfg_decode_addr(const struct hvm_mmcfg *mmcfg,
                                            paddr_t addr, pci_sbdf_t *sbdf)
 {
index 073d18bd1023555281c9f9a7445692da8435181e..330f9ce38674bc7db6e6ce5c119745d0b14b8b61 100644 (file)
@@ -256,6 +256,9 @@ static void __hwdom_init amd_iommu_hwdom_init(struct domain *d)
     /* Inclusive IOMMU mappings are disabled by default on AMD hardware. */
     if ( iommu_hwdom_inclusive == -1 )
         iommu_hwdom_inclusive = 0;
+    /* Reserved IOMMU mappings are disabled by default on AMD hardware. */
+    if ( iommu_hwdom_reserved == -1 )
+        iommu_hwdom_reserved = 0;
 
     if ( allocate_domain_resources(dom_iommu(d)) )
         BUG();
index a5158b0bdfa37b4856c5be402c9b7efd4f4a27e8..43ece42a50ee06909e3bdd5aedc79fe73c6f0203 100644 (file)
@@ -2732,6 +2732,10 @@ static void __hwdom_init arm_smmu_iommu_hwdom_init(struct domain *d)
                printk(XENLOG_WARNING
                "map-inclusive dom0-iommu option is not supported on ARM\n");
        iommu_hwdom_inclusive = 0;
+       if ( iommu_hwdom_reserved == 1 )
+               printk(XENLOG_WARNING
+               "map-reserved dom0-iommu option is not supported on ARM\n");
+       iommu_hwdom_reserved = 0;
 }
 
 static void arm_smmu_iommu_domain_teardown(struct domain *d)
index 6ac66e0622a5a119d821884f79fc6b37819ade2a..ee3f523fdfafde2d3bfe2765f35423783693d8ec 100644 (file)
@@ -62,6 +62,7 @@ bool_t __read_mostly iommu_intremap = 1;
 bool __hwdom_initdata iommu_hwdom_strict;
 bool __read_mostly iommu_hwdom_passthrough;
 int8_t __hwdom_initdata iommu_hwdom_inclusive = -1;
+int8_t __hwdom_initdata iommu_hwdom_reserved = -1;
 
 /*
  * In the current implementation of VT-d posted interrupts, in some extreme
@@ -155,6 +156,8 @@ static int __init parse_dom0_iommu_param(const char *s)
             iommu_hwdom_strict = val;
         else if ( (val = parse_boolean("map-inclusive", s, ss)) >= 0 )
             iommu_hwdom_inclusive = val;
+        else if ( (val = parse_boolean("map-reserved", s, ss)) >= 0 )
+            iommu_hwdom_inclusive = val;
         else
             rc = -EINVAL;
 
@@ -236,7 +239,7 @@ void __hwdom_init iommu_hwdom_init(struct domain *d)
 
     hd->platform_ops->hwdom_init(d);
 
-    ASSERT(iommu_hwdom_inclusive != -1);
+    ASSERT(iommu_hwdom_inclusive != -1 && iommu_hwdom_inclusive != -1);
     if ( iommu_hwdom_inclusive && !is_pv_domain(d) )
     {
         printk(XENLOG_WARNING
index 2857546a5cd0043715846f8d7e727f7775fc1aac..adc70f205a7732c8d38a4320ef5116ede56c9a2a 100644 (file)
@@ -1307,6 +1307,9 @@ static void __hwdom_init intel_iommu_hwdom_init(struct domain *d)
     /* Inclusive mappings are enabled by default on Intel hardware for PV. */
     if ( iommu_hwdom_inclusive == -1 )
         iommu_hwdom_inclusive = is_pv_domain(d);
+    /* Reserved IOMMU mappings are enabled by default on Intel hardware. */
+    if ( iommu_hwdom_reserved == -1 )
+        iommu_hwdom_reserved = 1;
 
     setup_hwdom_pci_devices(d, setup_hwdom_device);
     setup_hwdom_rmrr(d);
index 5809027573cac03441d8574b2e2b6261c66be4c4..47a078272aa6e808f1bd1e0c8ca31cabe3cfcc21 100644 (file)
@@ -20,6 +20,7 @@
 #include <xen/softirq.h>
 #include <xsm/xsm.h>
 
+#include <asm/hvm/io.h>
 #include <asm/setup.h>
 
 void iommu_update_ire_from_apic(
@@ -139,17 +140,23 @@ static bool __hwdom_init hwdom_iommu_map(const struct domain *d,
                                          unsigned long max_pfn)
 {
     mfn_t mfn = _mfn(pfn);
+    unsigned int i, type;
 
     /*
      * Set up 1:1 mapping for dom0. Default to include only conventional RAM
      * areas and let RMRRs include needed reserved regions. When set, the
      * inclusive mapping additionally maps in every pfn up to 4GB except those
-     * that fall in unusable ranges.
+     * that fall in unusable ranges for PV Dom0.
      */
-    if ( (pfn > max_pfn && !mfn_valid(mfn)) || xen_in_range(pfn) )
+    if ( (pfn > max_pfn && !mfn_valid(mfn)) || xen_in_range(pfn) ||
+         /*
+          * Ignore any address below 1MB, that's already identity mapped by the
+          * Dom0 builder for HVM.
+          */
+         (!d->domain_id && is_hvm_domain(d) && pfn < PFN_DOWN(MB(1))) )
         return false;
 
-    switch ( page_get_ram_type(mfn) )
+    switch ( type = page_get_ram_type(mfn) )
     {
     case RAM_TYPE_UNUSABLE:
         return false;
@@ -160,10 +167,40 @@ static bool __hwdom_init hwdom_iommu_map(const struct domain *d,
         break;
 
     default:
-        if ( !iommu_hwdom_inclusive || pfn > max_pfn )
+        if ( type & RAM_TYPE_RESERVED )
+        {
+            if ( !iommu_hwdom_inclusive && !iommu_hwdom_reserved )
+                return false;
+        }
+        else if ( is_hvm_domain(d) || !iommu_hwdom_inclusive || pfn > max_pfn )
             return false;
     }
 
+    /*
+     * Check that it doesn't overlap with the LAPIC
+     * TODO: if the guest relocates the MMIO area of the LAPIC Xen should make
+     * sure there's nothing in the new address that would prevent trapping.
+     */
+    if ( has_vlapic(d) )
+    {
+        const struct vcpu *v;
+
+        for_each_vcpu(d, v)
+            if ( pfn == PFN_DOWN(vlapic_base_address(vcpu_vlapic(v))) )
+                return false;
+    }
+    /* ... or the IO-APIC */
+    for ( i = 0; has_vioapic(d) && i < d->arch.hvm.nr_vioapics; i++ )
+        if ( pfn == PFN_DOWN(domain_vioapic(d, i)->base_address) )
+            return false;
+    /*
+     * ... or the PCIe MCFG regions.
+     * TODO: runtime added MMCFG regions are not checked to make sure they
+     * don't overlap with already mapped regions, thus preventing trapping.
+     */
+    if ( has_vpci(d) && vpci_is_mmcfg_address(d, pfn_to_paddr(pfn)) )
+        return false;
+
     return true;
 }
 
@@ -173,7 +210,7 @@ void __hwdom_init arch_iommu_hwdom_init(struct domain *d)
 
     BUG_ON(!is_hardware_domain(d));
 
-    if ( iommu_hwdom_passthrough || !is_pv_domain(d) )
+    if ( iommu_hwdom_passthrough )
         return;
 
     max_pfn = (GB(4) >> PAGE_SHIFT) - 1;
@@ -187,7 +224,10 @@ void __hwdom_init arch_iommu_hwdom_init(struct domain *d)
         if ( !hwdom_iommu_map(d, pfn, max_pfn) )
             continue;
 
-        rc = iommu_map_page(d, pfn, pfn, IOMMUF_readable|IOMMUF_writable);
+        if ( paging_mode_translate(d) )
+            rc = set_identity_p2m_entry(d, pfn, p2m_access_rw, 0);
+        else
+            rc = iommu_map_page(d, pfn, pfn, IOMMUF_readable|IOMMUF_writable);
         if ( rc )
             printk(XENLOG_WARNING " d%d: IOMMU mapping failed: %d\n",
                    d->domain_id, rc);
index 8c83fd0c8ba4b5b0be013b2010540a4ed494633c..7ceb119b642d86d7ffcf5a80bfaf2856b8761867 100644 (file)
@@ -185,6 +185,9 @@ int register_vpci_mmcfg_handler(struct domain *d, paddr_t addr,
 /* Destroy tracked MMCFG areas. */
 void destroy_vpci_mmcfg(struct domain *d);
 
+/* Check if an address is between a MMCFG region for a domain. */
+bool vpci_is_mmcfg_address(const struct domain *d, paddr_t addr);
+
 #endif /* __ASM_X86_HVM_IO_H__ */
 
 
index 89c68306891d0cab18921a2e1460d75be98c58f1..57c4e81ec6228625c99427c374eb9d24993559e6 100644 (file)
@@ -37,7 +37,7 @@ extern bool_t iommu_debug;
 extern bool_t amd_iommu_perdev_intremap;
 
 extern bool iommu_hwdom_strict, iommu_hwdom_passthrough;
-extern int8_t iommu_hwdom_inclusive;
+extern int8_t iommu_hwdom_inclusive, iommu_hwdom_reserved;
 
 extern unsigned int iommu_dev_iotlb_timeout;