Skip to contentSkip to conversation

Intel IOMMU Support

PATCH In review Ashok Raj 2 participants 11 messages Apr 24, 2007

Subsystems (4)
lore
Share & tools
lore

Ashok Raj

author
#0 lore raw

cover Intel IOMMU Support

Hello again!

Andrew: Could you help include in -mm to give it more exposure preparing for mainline inclusion with more testing?

This is a resend of the patches after addressing some feedback we received.

1. As len requested, we moved most of the acpi parts to drivers/pci instead of leaving them in drivers/acpi, including some renaming of functions, using just acpi_get_table() only.

2. Made the guard page support configurable.
3. Added new option to allocate consecutive address instead of re-using free addresses as an experimental option, Not validated extensively. Its expected to improve in certain cases...
4. Fixed a couple minor bugs that got exposed in testing.

Some more interesting possibilities. -- enhancements to work on.

- In order to ensure we dont break any driver that may not be using dma api's here are some suggestions to work on.
- Create a single 1-1 map, and make sure any pci device gets this map when they do a pci_set_master() to enable bus mastering automatically.
- When the device driver does a first call to do a dma mapping, then dissociate the device from the unity map domain, to its own. This gives limited protection but doesnt break drivers that do not use dma mapping.
- Creating context entries only 1 per segment.. today we create one per IOMMU which is not required.

This way we can avoid doing some of the workarounds we do for some devices today, and will function as a default container for compatibility.

On one hand, this will provide more compatibility, but we will lose oppertunity to identify broken device drivers that dont use dma api's and fix them.... Depending on who you talk to.. some like it.. some just hate it! and would like to fix the broken ones instead.

Cheers,
Ashok Raj

author
#1 lore raw

PATCH 1/8 ACPI support for Intel Virtualization Technology for Directed I/O

This patch contains basic ACPI parsing and enumeration support.

arch/x86_64/Kconfig +8 -0
Index: linux-2.6.21-rc5/arch/x86_64/Kconfig
===================================================================
@@ -687,6 +687,14 @@
 	bool "Support mmconfig PCI config space access"
 	depends on PCI && ACPI
 
+config DMAR
+	bool "Support for DMA Remapping Devices (EXPERIMENTAL)"
+	depends on PCI_MSI && ACPI && EXPERIMENTAL
+	help
+	  Support DMA Remapping Devices. The devices are reported via
+	  ACPI tables and includes pci device scope under each DMA
+	  remapping device.
+
 source "drivers/pci/pcie/Kconfig"
 
 source "drivers/pci/Kconfig"
drivers/pci/Makefile +1 -0
Index: linux-2.6.21-rc5/drivers/pci/Makefile
===================================================================
@@ -38,6 +38,7 @@
 #
 obj-$(CONFIG_ACPI)    += pci-acpi.o
 
+obj-$(CONFIG_DMAR)		+= dmar.o
 # Cardbus & CompactPCI use setup-bus
 obj-$(CONFIG_HOTPLUG) += setup-bus.o
 
drivers/pci/dmar.c new file +350 -0

350 lines not on this page · raw message

drivers/pci/dmar.h new file +65 -0

65 lines not on this page · raw message

include/acpi/actbl1.h +20 -7
Index: linux-2.6.21-rc5/include/acpi/actbl1.h
===================================================================
@@ -257,7 +257,8 @@
 struct acpi_table_dmar {
 	struct acpi_table_header header;	/* Common ACPI table header */
 	u8 width;		/* Host Address Width */
-	u8 reserved[11];
+	u8 flags;
+	u8 reserved[10];
 };
 
 /* DMAR subtable header */
@@ -265,8 +266,6 @@
 struct acpi_dmar_header {
 	u16 type;
 	u16 length;
-	u8 flags;
-	u8 reserved[3];
 };
 
 /* Values for subtable type in struct acpi_dmar_header */
@@ -274,13 +273,15 @@
 enum acpi_dmar_type {
 	ACPI_DMAR_TYPE_HARDWARE_UNIT = 0,
 	ACPI_DMAR_TYPE_RESERVED_MEMORY = 1,
-	ACPI_DMAR_TYPE_RESERVED = 2	/* 2 and greater are reserved */
+	ACPI_DMAR_TYPE_ATSR = 2,
+	ACPI_DMAR_TYPE_RESERVED = 3	/* 3 and greater are reserved */
 };
 
 struct acpi_dmar_device_scope {
 	u8 entry_type;
 	u8 length;
-	u8 segment;
+	u16 reserved;
+	u8 enumeration_id;
 	u8 bus;
 };
 
@@ -290,7 +291,14 @@
 	ACPI_DMAR_SCOPE_TYPE_NOT_USED = 0,
 	ACPI_DMAR_SCOPE_TYPE_ENDPOINT = 1,
 	ACPI_DMAR_SCOPE_TYPE_BRIDGE = 2,
-	ACPI_DMAR_SCOPE_TYPE_RESERVED = 3	/* 3 and greater are reserved */
+	ACPI_DMAR_SCOPE_TYPE_IOAPIC = 3,
+	ACPI_DMAR_SCOPE_TYPE_HPET = 4,
+	ACPI_DMAR_SCOPE_TYPE_RESERVED = 5	/* 5 and greater are reserved */
+};
+
+struct acpi_dmar_pci_path {
+	u8 dev;
+	u8 fn;
 };
 
 /*
@@ -301,6 +309,9 @@
 
 struct acpi_dmar_hardware_unit {
 	struct acpi_dmar_header header;
+	u8 flags;
+	u8 reserved;
+	u16 segment;
 	u64 address;		/* Register Base Address */
 };
 
@@ -312,7 +323,9 @@
 
 struct acpi_dmar_reserved_memory {
 	struct acpi_dmar_header header;
-	u64 address;		/* 4_k aligned base address */
+	u16 reserved;
+	u16 segment;
+	u64 base_address;		/* 4_k aligned base address */
 	u64 end_address;	/* 4_k aligned limit address */
 };
 
author
#2 lore raw

PATCH 2/8 Some generic search functions required to lookup device relationships.

In reply to Ashok Raj

PCI support functions for DMAR, to find parent bridge.

When devices are under a p2p bridge, upstream
transactions get replaced by the device id of the bridge as it owns the PCIE transaction. Hence its necessary to setup translations on behalf of the bridge as well. Due to this limitation all devices under a p2p share the same domain in a DMAR.

We just cache the type of device, if its a native PCIe device or not for later use.

-------------------------------------------------

drivers/pci/pci.h +1 -0
Index: 2.6.21-rc6/drivers/pci/pci.h
===================================================================
@@ -90,3 +90,4 @@ pci_match_one_device(const struct pci_de
 	return NULL;
 }
 
+struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
drivers/pci/probe.c +14 -0
Index: 2.6.21-rc6/drivers/pci/probe.c
===================================================================
@@ -822,6 +822,19 @@ static void pci_release_dev(struct devic
 	kfree(pci_dev);
 }
 
+static void set_pcie_port_type(struct pci_dev *pdev)
+{
+	int pos;
+	u16 reg16;
+
+	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	if (!pos)
+		return;
+	pdev->is_pcie = 1;
+	pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
+	pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
+}
+
 /**
  * pci_cfg_space_size - get the configuration space size of the PCI device.
  * @dev: PCI device
@@ -919,6 +932,7 @@ pci_scan_device(struct pci_bus *bus, int
 	dev->device = (l >> 16) & 0xffff;
 	dev->cfg_size = pci_cfg_space_size(dev);
 	dev->error_state = pci_channel_io_normal;
+	set_pcie_port_type(dev);
 
 	/* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
 	   set this higher, assuming the system even supports it.  */
drivers/pci/search.c +30 -0
Index: 2.6.21-rc6/drivers/pci/search.c
===================================================================
@@ -14,6 +14,36 @@
 #include "pci.h"
 
 DECLARE_RWSEM(pci_bus_sem);
+/*
+ * find the upstream PCIE-to-PCI bridge of a PCI device
+ * if the device is PCIE, return NULL
+ * if the device isn't connected to a PCIE bridge (that is its parent is a
+ * legacy PCI bridge and the bridge is directly connected to bus 0), return its
+ * parent
+ */
+struct pci_dev *
+pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
+{
+	struct pci_dev *tmp = NULL;
+
+	if (pdev->is_pcie)
+		return NULL;
+	while (1) {
+		if (!pdev->bus->self)
+			break;
+		pdev = pdev->bus->self;
+		/* a p2p bridge */
+		if (!pdev->is_pcie) {
+			tmp = pdev;
+			continue;
+		}
+		/* PCI device should connect to a PCIE bridge */
+		BUG_ON(pdev->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE);
+		return pdev;
+	}
+
+	return tmp;
+}
 
 static struct pci_bus *
 pci_do_find_bus(struct pci_bus* bus, unsigned char busnr)
include/linux/pci.h +2 -0
Index: 2.6.21-rc6/include/linux/pci.h
===================================================================
@@ -126,6 +126,7 @@ struct pci_dev {
 	unsigned short	subsystem_device;
 	unsigned int	class;		/* 3 bytes: (base,sub,prog-if) */
 	u8		hdr_type;	/* PCI header type (`multi' flag masked out) */
+	u8		pcie_type;	/* PCI-E device/port type */
 	u8		rom_base_reg;	/* which config register controls the ROM */
 	u8		pin;  		/* which interrupt pin this device uses */
 
@@ -168,6 +169,7 @@ struct pci_dev {
 	unsigned int 	msi_enabled:1;
 	unsigned int	msix_enabled:1;
 	unsigned int	is_managed:1;
+	unsigned int	is_pcie:1;
 	atomic_t	enable_cnt;	/* pci_enable_device has been called */
 
 	u32		saved_config_space[16]; /* config space saved at suspend time */
author
#3 lore raw

PATCH 3/8 Generic hardware support for Intel IOMMU.

In reply to Ashok Raj

Actual intel IOMMU driver. Hardware spec can be found at:
http://www.intel.com/technology/virtualization

The current version of spec is slightly outdated, but an updated version of spec is in works and comming very soon. I will send a note to the list when new updated spec is available. For the most part existing spec should be good for basic understanding of the mechanics of the IOMMU.

This driver sets X86_64 'dma_ops', so hook into standard DMA APIs. In this way, PCI driver will get virtual DMA address. This change is transparent to PCI drivers.

arch/x86_64/kernel/io_apic.c +58 -1
Index: 2.6.21-rc6/arch/x86_64/kernel/io_apic.c
===================================================================
@@ -32,6 +32,7 @@
 #include <linux/sysdev.h>
 #include <linux/msi.h>
 #include <linux/htirq.h>
+#include <linux/intel-iommu.h>
 #ifdef CONFIG_ACPI
 #include <acpi/acpi_bus.h>
 #endif
@@ -2002,8 +2003,64 @@ void arch_teardown_msi_irq(unsigned int 
 	destroy_irq(irq);
 }
 
-#endif /* CONFIG_PCI_MSI */
+#ifdef CONFIG_DMAR
+#ifdef CONFIG_SMP
+static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask)
+{
+	struct irq_cfg *cfg = irq_cfg + irq;
+	struct msi_msg msg;
+	unsigned int dest;
+	cpumask_t tmp;
+
+	cpus_and(tmp, mask, cpu_online_map);
+	if (cpus_empty(tmp))
+		return;
+
+	if (assign_irq_vector(irq, mask))
+		return;
+
+	cpus_and(tmp, cfg->domain, mask);
+	dest = cpu_mask_to_apicid(tmp);
+
+	dmar_msi_read(irq, &msg);
+
+	msg.data &= ~MSI_DATA_VECTOR_MASK;
+	msg.data |= MSI_DATA_VECTOR(cfg->vector);
+	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
+	msg.address_lo |= MSI_ADDR_DEST_ID(dest);
+
+	dmar_msi_write(irq, &msg);
+	irq_desc[irq].affinity = mask;
+}
+#endif /* CONFIG_SMP */
+
+struct irq_chip dmar_msi_type = {
+	.name = "DMAR_MSI",
+	.unmask = dmar_msi_unmask,
+	.mask = dmar_msi_mask,
+	.ack = ack_apic_edge,
+#ifdef CONFIG_SMP
+	.set_affinity = dmar_msi_set_affinity,
+#endif
+	.retrigger = ioapic_retrigger_irq,
+};
+
+int arch_setup_dmar_msi(unsigned int irq)
+{
+	int ret;
+	struct msi_msg msg;
+
+	ret = msi_compose_msg(NULL, irq, &msg);
+	if (ret < 0)
+		return ret;
+	dmar_msi_write(irq, &msg);
+	set_irq_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
+		"edge");
+	return 0;
+}
+#endif
 
+#endif /* CONFIG_PCI_MSI */
 /*
  * Hypertransport interrupt support
  */
arch/x86_64/kernel/pci-dma.c +9 -0
Index: 2.6.21-rc6/arch/x86_64/kernel/pci-dma.c
===================================================================
@@ -7,6 +7,7 @@
 #include <linux/string.h>
 #include <linux/pci.h>
 #include <linux/module.h>
+#include <linux/intel-iommu.h>
 #include <asm/io.h>
 #include <asm/proto.h>
 #include <asm/calgary.h>
@@ -303,6 +304,10 @@ void __init pci_iommu_alloc(void)
 	detect_calgary();
 #endif
 
+#ifdef CONFIG_DMAR
+	detect_intel_iommu();
+#endif
+
 #ifdef CONFIG_SWIOTLB
 	pci_swiotlb_init();
 #endif
@@ -314,6 +319,10 @@ static int __init pci_iommu_init(void)
 	calgary_iommu_init();
 #endif
 
+#ifdef CONFIG_DMAR
+	intel_iommu_init();
+#endif
+
 #ifdef CONFIG_IOMMU
 	gart_iommu_init();
 #endif
arch/x86_64/mm/pageattr.c +3 -3
Index: 2.6.21-rc6/arch/x86_64/mm/pageattr.c
===================================================================
@@ -61,10 +61,10 @@ static struct page *split_large_page(uns
 	return base;
 } 
 
-static void cache_flush_page(void *adr)
+void clflush_cache_range(void *adr, int size)
 {
 	int i;
-	for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size)
+	for (i = 0; i < size; i += boot_cpu_data.x86_clflush_size)
 		asm volatile("clflush (%0)" :: "r" (adr + i));
 }
 
@@ -80,7 +80,7 @@ static void flush_kernel_map(void *arg)
 	list_for_each_entry(pg, l, lru) {
 		void *adr = page_address(pg);
 		if (cpu_has_clflush)
-			cache_flush_page(adr);
+			clflush_cache_range(adr, PAGE_SIZE);
 		__flush_tlb_one(adr);
 	}
 }
drivers/pci/Kconfig +8 -0
Index: 2.6.21-rc6/drivers/pci/Kconfig
===================================================================
@@ -60,3 +60,11 @@ config HT_IRQ
 	   This allows native hypertransport devices to use interrupts.
 
 	   If unsure say Y.
+
+config IOVA_GUARD_PAGE
+	bool "Enables gaurd page when allocating IO Virtual Address for IOMMU"
+	depends on DMAR
+
+config IOVA_NEXT_CONTIG
+	bool "Keeps IOVA allocations consequent between allocations"
+	depends on DMAR && EXPERIMENTAL
drivers/pci/Makefile +3 -0
Index: 2.6.21-rc6/drivers/pci/Makefile
===================================================================
@@ -20,6 +20,9 @@ obj-$(CONFIG_PCI_MSI) += msi.o
 # Build the Hypertransport interrupt support
 obj-$(CONFIG_HT_IRQ) += htirq.o
 
+# Build DMAR if configured
+obj-$(CONFIG_DMAR) += intel-iommu.o iova.o
+
 #
 # Some architectures use the generic PCI setup functions
 #
drivers/pci/intel-iommu.c new file +2070 -0

2,070 lines not on this page · raw message

drivers/pci/iova.c new file +274 -0

274 lines not on this page · raw message

drivers/pci/iova.h new file +56 -0

56 lines not on this page · raw message

include/asm-x86_64/cacheflush.h +1 -0
Index: 2.6.21-rc6/include/asm-x86_64/cacheflush.h
===================================================================
@@ -27,6 +27,7 @@
 void global_flush_tlb(void); 
 int change_page_attr(struct page *page, int numpages, pgprot_t prot);
 int change_page_attr_addr(unsigned long addr, int numpages, pgprot_t prot);
+void clflush_cache_range(void *addr, int size);
 
 #ifdef CONFIG_DEBUG_RODATA
 void mark_rodata_ro(void);
include/linux/intel-iommu.h new file +269 -0

269 lines not on this page · raw message

author
#4 lore raw

PATCH 4/8 Supporting Zero Length Reads in Intel IOMMU.

In reply to Ashok Raj

PCI specs permit zero length reads (ZLR) even if the mapping for that region is write only. Support for this feature is indicated by the presence of a bit in the DMAR capability. If a particular DMAR does not support this capability we map write-only regions as read-write.

This option can also provides a workaround for some drivers that request a write-only mapping when they really should request a read-write. (We ran into one such case in eepro100.c in handling rx_ring_dma)

----------------------------------

 drivers/pci/intel-iommu.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/pci/intel-iommu.c +10 -2
Index: 2.6.21-rc6/drivers/pci/intel-iommu.c
===================================================================
@@ -84,7 +84,7 @@ struct iommu {
 	struct sys_device sysdev;
 };
 
-static int dmar_disabled;
+static int dmar_disabled, dmar_force_rw;
 
 static char *get_fault_reason(u8 fault_reason)
 {
@@ -102,6 +102,9 @@ static int __init intel_iommu_setup(char
 		if (!strncmp(str, "off", 3)) {
 			dmar_disabled = 1;
 			printk(KERN_INFO"Intel-IOMMU: disabled\n");
+		} else if (!strncmp(str, "forcerw", 7)) {
+			dmar_force_rw = 1;
+			printk(KERN_INFO"Intel-IOMMU: force R/W for W/O mapping\n");
 		}
 		str += strcspn(str, ",");
 		while (*str == ',')
@@ -1720,7 +1723,12 @@ static dma_addr_t __intel_map_single(str
 			goto error;
 	}
 
-	if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)
+	/*
+	 * Check if DMAR supports zero-length reads on write only
+	 * mappings..
+	 */
+	if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \
+			!cap_zlr(domain->iommu->cap) || dmar_force_rw)
 		prot |= DMA_PTE_READ;
 	if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)
 		prot |= DMA_PTE_WRITE;
include/linux/intel-iommu.h +1 -0
Index: 2.6.21-rc6/include/linux/intel-iommu.h
===================================================================
@@ -79,6 +79,7 @@
 #define cap_max_fault_reg_offset(c) \
 	(cap_fault_reg_offset(c) + cap_num_fault_regs(c) * 16)
 
+#define cap_zlr(c)		(((c) >> 22) & 1)
 #define cap_isoch(c)		(((c) >> 23) & 1)
 #define cap_mgaw(c)		((((c) >> 16) & 0x3f) + 1)
 #define cap_sagaw(c)		(((c) >> 8) & 0x1f)
author
#5 lore raw

PATCH 5/8 Graphics driver workarounds to provide unity map

In reply to Ashok Raj

Most GFX drivers don't call standard PCI DMA APIs to allocate DMA buffer, Such drivers will be broken with IOMMU enabled. To workaround this issue, we added two options.

Once graphics devices are converted over to use the DMA-API's this entire patch can be removed...

a. intel_iommu=igfx_off. With this option, DMAR who has just gfx devices
under it will be ignored. This mostly affect intergated gfx devices. If the DMAR is ignored, gfx device under it will get physical address for DMA.
b. intel_iommu=gfx_workaround. With this option, we will setup 1:1 mapping
for whole memory for gfx devices, that is physical address equals to virtual address.In this way, gfx will use physical address for DMA, this is primarily for add-in card GFX device.

arch/x86_64/kernel/e820.c +19 -0
Index: 2.6.21-rc6/arch/x86_64/kernel/e820.c
===================================================================
@@ -730,3 +730,22 @@ __init void e820_setup_gap(void)
 	printk(KERN_INFO "Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
 		pci_mem_start, gapstart, gapsize);
 }
+
+int __init arch_get_ram_range(int slot, u64 *addr, u64 *size)
+{
+	int i;
+
+	if (slot < 0 || slot >= e820.nr_map)
+		return -1;
+	for (i = slot; i < e820.nr_map; i++) {
+		if(e820.map[i].type != E820_RAM)
+			continue;
+		break;
+	}
+	if (i == e820.nr_map || e820.map[i].addr > (max_pfn << PAGE_SHIFT))
+		return -1;
+	*addr = e820.map[i].addr;
+	*size = min_t(u64, e820.map[i].size + e820.map[i].addr,
+		max_pfn << PAGE_SHIFT) - *addr;
+	return i + 1;
+}
drivers/pci/dmar.h +1 -0
Index: 2.6.21-rc6/drivers/pci/dmar.h
===================================================================
@@ -35,6 +35,7 @@ struct dmar_drhd_unit {
 	int	devices_cnt;
 	u8	include_all:1;
 	struct iommu *iommu;
+	int	ignored:1; /* the drhd should be ignored */
 };
 
 struct dmar_rmrr_unit {
drivers/pci/intel-iommu.c +119 -0

226 lines not on this page · raw message

include/linux/intel-iommu.h +2 -0
Index: 2.6.21-rc6/include/linux/intel-iommu.h
===================================================================
@@ -263,6 +263,8 @@ extern void dmar_msi_read(int irq, struc
 extern void dmar_msi_write(int irq, struct msi_msg *msg);
 extern int arch_setup_dmar_msi(unsigned int irq);
 
+extern int arch_get_ram_range(int slot, u64 *addr, u64 *size);
+
 extern int init_dmars(void);
 extern void detect_intel_iommu(void);
 extern int early_dmar_detect(void);
author
#6 lore raw

PATCH 6/8 Doc updates for Intel Virtualization Technology for Directed I/O.

In reply to Ashok Raj

Document Intel IOMMU driver boot option.

Documentation/Intel-IOMMU.txt new file +119 -0
Index: linux-2.6.21-rc5/Documentation/Intel-IOMMU.txt
===================================================================
@@ -0,0 +1,119 @@
+Linux IOMMU Support
+===================
+
+The architecture spec can be obtained from the below location.
+
+http://www.intel.com/technology/virtualization/
+
+This guide gives a quick cheat sheet for some basic understanding.
+
+Some Keywords
+
+DMAR - DMA remapping
+DRHD - DMA Engine Reporting Structure
+RMRR - Reserved memory Region Reporting Structure
+ZLR  - Zero length reads from PCI devices
+IOVA - IO Virtual address.
+
+Basic stuff
+-----------
+
+ACPI enumerates and lists the different DMA engines in the platform, and
+device scope relationships between PCI devices and which DMA engine  controls
+them.
+
+What is RMRR?
+-------------
+
+There are some devices the BIOS controls, for e.g USB devices to perform
+PS2 emulation. The regions of memory used for these devices are marked
+reserved in the e820 map. When we turn on DMA translation, DMA to those
+regions will fail. Hence BIOS uses RMRR to specify these regions along with
+devices that need to access these regions. OS is expected to setup
+unity mappings for these regions for these devices to access these regions.
+
+How is IOVA generated?
+---------------------
+
+Well behaved drivers call pci_map_*() calls before sending command to device
+that needs to perform DMA. Once DMA is completed and mapping is no longer
+required, device performs a pci_unmap_*() calls to unmap the region.
+
+The Intel IOMMU driver allocates a virtual address per domain. Each PCIE
+device has its own domain (hence protection). Devices under p2p bridges
+share the virtual address with all devices under the p2p bridge due to
+transaction id aliasing for p2p bridges.
+
+IOVA generation is pretty generic. We used the same technique as vmalloc()
+but these are not global address spaces, but separate for each domain.
+Different DMA engines may support different number of domains.
+
+We also allocate gaurd pages with each mapping, so we can attempt to catch
+any overflow that might happen.
+
+
+Graphics Problems?
+------------------
+If you encounter issues with graphics devices, you can try adding
+option intel_iommu=igfx_off to turn off the integrated graphics engine.
+
+If it happens to be a PCI device included in the INCLUDE_ALL Engine,
+then try the intel_iommu=gfx_workaround to setup a 1-1 map. We hear
+graphics drivers may be in process of using DMA api's in the near
+future....
+
+Some exceptions to IOVA
+-----------------------
+Interrupt ranges are not address translated, (0xfee00000 - 0xfeefffff).
+The same is true for peer to peer transactions. Hence we reserve the
+address from PCI MMIO ranges so they are not allocated for IOVA addresses.
+
+
+Fault reporting
+---------------
+When errors are reported, the DMA engine signals via an interrupt. The fault
+reason and device that caused it with fault reason is printed on console.
+
+See below for sample.
+
+
+Boot Message Sample
+-------------------
+
+Something like this gets printed indicating presence of DMAR tables
+in ACPI.
+
+ACPI: DMAR (v001 A M I  OEMDMAR  0x00000001 MSFT 0x00000097) @ 0x000000007f5b5ef0
+
+When DMAR is being processed and initialized by ACPI, prints DMAR locations
+and any RMRR's processed.
+
+ACPI DMAR:Host address width 36
+ACPI DMAR:DRHD (flags: 0x00000000)base: 0x00000000fed90000
+ACPI DMAR:DRHD (flags: 0x00000000)base: 0x00000000fed91000
+ACPI DMAR:DRHD (flags: 0x00000001)base: 0x00000000fed93000
+ACPI DMAR:RMRR base: 0x00000000000ed000 end: 0x00000000000effff
+ACPI DMAR:RMRR base: 0x000000007f600000 end: 0x000000007fffffff
+
+When DMAR is enabled for use, you will notice..
+
+PCI-DMA: Using DMAR IOMMU
+
+Fault reporting
+---------------
+
+DMAR:[DMA Write] Request device [00:02.0] fault addr 6df084000
+DMAR:[fault reason 05] PTE Write access is not set
+DMAR:[DMA Write] Request device [00:02.0] fault addr 6df084000
+DMAR:[fault reason 05] PTE Write access is not set
+
+TBD
+----
+
+- No Performance tuning / analysis yet.
+- sysfs needs useful data to be populated.
+  DMAR info, device scope, stats could be exposed to some extent.
+- Add support to Firmware Developer Kit to test ACPI tables for DMAR.
+- For compatibility testing, could use unity map domain for all devices, just
+  provide a 1-1 for all useful memory under a single domain for all devices.
+- API for paravirt ops for abstracting functionlity for VMM folks.
Documentation/kernel-parameters.txt +20 -0
Index: linux-2.6.21-rc5/Documentation/kernel-parameters.txt
===================================================================
@@ -710,6 +710,26 @@
 
 	inttest=	[IA64]
 
+	intel_iommu=	[DMAR] Intel IOMMU driver (DMAR) option
+		off
+			Disable intel iommu driver.
+		forcerw
+			Some drivers might incorrectly map DMA buffers as
+			write-only even when the device, requires read/write
+			mapping.  This option works around the issue in IOMMU
+			driver by making all write-only map as read/write.
+			Default is off.
+		igfx_off [Default Off]
+			By default, gfx is mapped as normal device. If a gfx
+			device has a dedicated DMAR unit, the DMAR unit is
+			bypassed with this option. In this case, gfx device
+			will use physical address for DMA.
+		gfx_workaround [Default Off]
+			gfx devices tend to use physical address for DMA and
+			avoid using DMA api's. Setting this option permits
+			the IOMMU driver to set a unity map for all OS
+			visible memory. Hence the driver can continue to use
+			physical addresses for DMA.
 	io7=		[HW] IO7 for Marvel based alpha systems
 			See comment before marvel_specify_io7 in
 			arch/alpha/kernel/core_marvel.c.
#7 lore raw

Re: [Intel IOMMU][patch 6/8] Doc updates for Intel Virtualization Technology for Directed I/O.

Quoted 55 lines · Ashok Raj

Document Intel IOMMU driver boot option.

Index: linux-2.6.21-rc5/Documentation/Intel-IOMMU.txt
===================================================================

Documentation/Intel-IOMMU.txt new file +0 -0

05:41:56.000000000 -0700

Documentation/Intel-IOMMU.txt new file +22 -0
@@ -0,0 +1,119 @@
+Linux IOMMU Support
+===================
+
+The architecture spec can be obtained from the below location.
+
+http://www.intel.com/technology/virtualization/
+
+This guide gives a quick cheat sheet for some basic understanding.
+
+Some Keywords
+
+DMAR - DMA remapping
+DRHD - DMA Engine Reporting Structure
+RMRR - Reserved memory Region Reporting Structure
+ZLR  - Zero length reads from PCI devices
+IOVA - IO Virtual address.
+
+Basic stuff
+-----------
+
+ACPI enumerates and lists the different DMA engines in the platform, and
+device scope relationships between PCI devices and which DMA engine

controls

Documentation/Intel-IOMMU.txt new file +16 -0
+them.
+
+What is RMRR?
+-------------
+
+There are some devices the BIOS controls, for e.g USB devices to perform
+PS2 emulation. The regions of memory used for these devices are marked
+reserved in the e820 map. When we turn on DMA translation, DMA to those
+regions will fail. Hence BIOS uses RMRR to specify these regions along with
+devices that need to access these regions. OS is expected to setup
+unity mappings for these regions for these devices to access these regions.
+
+How is IOVA generated?
+---------------------
+
+Well behaved drivers call pci_map_*() calls before sending command to

device

Documentation/Intel-IOMMU.txt new file +5 -0
+that needs to perform DMA. Once DMA is completed and mapping is no longer
+required, device performs a pci_unmap_*() calls to unmap the region.
+
+The Intel IOMMU driver allocates a virtual address per domain. Each PCIE
+device has its own domain (hence protection). Devices under p2p bridges

Documentation/Intel-IOMMU.txt line 53

Quoted 10 lines
Documentation/Intel-IOMMU.txt new file +10 -0
+share the virtual address with all devices under the p2p bridge due to
+transaction id aliasing for p2p bridges.
+
+IOVA generation is pretty generic. We used the same technique as vmalloc()
+but these are not global address spaces, but separate for each domain.
+Different DMA engines may support different number of domains.
+
+We also allocate gaurd pages with each mapping, so we can attempt to catch
+any overflow that might happen.
+

guess you probably mean guard tables here...

Markus

Quoted 111 lines
Documentation/Intel-IOMMU.txt new file +21 -0
+
+Graphics Problems?
+------------------
+If you encounter issues with graphics devices, you can try adding
+option intel_iommu=igfx_off to turn off the integrated graphics engine.
+
+If it happens to be a PCI device included in the INCLUDE_ALL Engine,
+then try the intel_iommu=gfx_workaround to setup a 1-1 map. We hear
+graphics drivers may be in process of using DMA api's in the near
+future....
+
+Some exceptions to IOVA
+-----------------------
+Interrupt ranges are not address translated, (0xfee00000 - 0xfeefffff).
+The same is true for peer to peer transactions. Hence we reserve the
+address from PCI MMIO ranges so they are not allocated for IOVA addresses.
+
+
+Fault reporting
+---------------
+When errors are reported, the DMA engine signals via an interrupt. The

fault

Documentation/Intel-IOMMU.txt new file +12 -0
+reason and device that caused it with fault reason is printed on console.
+
+See below for sample.
+
+
+Boot Message Sample
+-------------------
+
+Something like this gets printed indicating presence of DMAR tables
+in ACPI.
+
+ACPI: DMAR (v001 A M I  OEMDMAR  0x00000001 MSFT 0x00000097) @

0x000000007f5b5ef0

Documentation/Intel-IOMMU.txt new file +31 -0
+
+When DMAR is being processed and initialized by ACPI, prints DMAR locations
+and any RMRR's processed.
+
+ACPI DMAR:Host address width 36
+ACPI DMAR:DRHD (flags: 0x00000000)base: 0x00000000fed90000
+ACPI DMAR:DRHD (flags: 0x00000000)base: 0x00000000fed91000
+ACPI DMAR:DRHD (flags: 0x00000001)base: 0x00000000fed93000
+ACPI DMAR:RMRR base: 0x00000000000ed000 end: 0x00000000000effff
+ACPI DMAR:RMRR base: 0x000000007f600000 end: 0x000000007fffffff
+
+When DMAR is enabled for use, you will notice..
+
+PCI-DMA: Using DMAR IOMMU
+
+Fault reporting
+---------------
+
+DMAR:[DMA Write] Request device [00:02.0] fault addr 6df084000
+DMAR:[fault reason 05] PTE Write access is not set
+DMAR:[DMA Write] Request device [00:02.0] fault addr 6df084000
+DMAR:[fault reason 05] PTE Write access is not set
+
+TBD
+----
+
+- No Performance tuning / analysis yet.
+- sysfs needs useful data to be populated.
+  DMAR info, device scope, stats could be exposed to some extent.
+- Add support to Firmware Developer Kit to test ACPI tables for DMAR.
+- For compatibility testing, could use unity map domain for all devices,

just
+ provide a 1-1 for all useful memory under a single domain for all devices.
+- API for paravirt ops for abstracting functionlity for VMM folks.
Index: linux-2.6.21-rc5/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.21-rc5.orig/Documentation/kernel-parameters.txt 2007-04-17
04:59:42.000000000 -0700
+++ linux-2.6.21-rc5/Documentation/kernel-parameters.txt 2007-04-17
05:41:56.000000000 -0700

Documentation/Intel-IOMMU.txt new file +20 -2
@@ -710,6 +710,26 @@
 
 	inttest=	[IA64]
 
+	intel_iommu=	[DMAR] Intel IOMMU driver (DMAR) option
+		off
+			Disable intel iommu driver.
+		forcerw
+			Some drivers might incorrectly map DMA buffers as
+			write-only even when the device, requires read/write
+			mapping.  This option works around the issue in IOMMU
+			driver by making all write-only map as read/write.
+			Default is off.
+		igfx_off [Default Off]
+			By default, gfx is mapped as normal device. If a gfx
+			device has a dedicated DMAR unit, the DMAR unit is
+			bypassed with this option. In this case, gfx device
+			will use physical address for DMA.
+		gfx_workaround [Default Off]
+			gfx devices tend to use physical address for DMA and
+			avoid using DMA api's. Setting this option permits
+			the IOMMU driver to set a unity map for all OS
+			visible memory. Hence the driver can continue to use
+			physical addresses for DMA.
 	io7=		[HW] IO7 for Marvel based alpha systems
 			See comment before marvel_specify_io7 in
 			arch/alpha/kernel/core_marvel.c.
 
--
-

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 1 line
Markus Rechberger
author
#8 lore raw

Re: [Intel IOMMU][patch 6/8] Doc updates for Intel Virtualization Technology for Directed I/O.

Quoted 6 lines · Markus Rechberger

Documentation/Intel-IOMMU.txt line 53

Quoted 3 lines
Documentation/Intel-IOMMU.txt new file +3 -0
+We also allocate gaurd pages with each mapping, so we can attempt to catch
+any overflow that might happen.
+

guess you probably mean guard tables here...

So there is a good chance i can be "The Governor of California" :-)

author
#9 lore raw

PATCH 7/8 Support for legacy ISA devices

In reply to Ashok Raj

Floppy disk drivers dont work well with DMA remapping. Its possible to extend the current use for x86_64, but the gain is very little. If someone feels compelled to clean this up, its up for grabs. Since these use 16M, we just provide a unity map for the ISA bridge device.

------------------------------------

Documentation/kernel-parameters.txt +5 -0
Index: linux-2.6.21-rc5/Documentation/kernel-parameters.txt
===================================================================
@@ -730,6 +730,11 @@
 			the IOMMU driver to set a unity map for all OS
 			visible memory. Hence the driver can continue to use
 			physical addresses for DMA.
+		noisamap
+			This option is required to setup identify map for
+			first 16M. The floppy disk could be modified to use
+			the DMA api's but thats a lot of pain for very small
+			gain. This option is turned on by default.
 	io7=		[HW] IO7 for Marvel based alpha systems
 			See comment before marvel_specify_io7 in
 			arch/alpha/kernel/core_marvel.c.
drivers/pci/intel-iommu.c +26 -0
Index: linux-2.6.21-rc5/drivers/pci/intel-iommu.c
===================================================================
@@ -37,6 +37,8 @@
 #include "pci.h"
 
 #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
+#define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
+
 #define IOAPIC_RANGE_START	(0xfee00000)
 #define IOAPIC_RANGE_END	(0xfeefffff)
 #define IOAPIC_RANGE_SIZE	(IOAPIC_RANGE_END - IOAPIC_RANGE_START + 1)
@@ -87,6 +89,7 @@
 
 static int dmar_disabled, dmar_force_rw;
 static int dmar_map_gfx = 1, dmar_no_gfx_identity_map = 1;
+static int dmar_fix_isa = 1;
 
 static char *get_fault_reason(u8 fault_reason)
 {
@@ -113,6 +116,9 @@
 		} else if (!strncmp(str, "gfx_workaround", 14)) {
 			dmar_no_gfx_identity_map = 0;
 			printk(KERN_INFO"Intel-IOMMU: do 1-1 mapping whole physical memory for GFX device\n");
+		} else if (!strncmp(str, "noisamap", 8)) {
+			dmar_fix_isa = 0;
+			printk (KERN_INFO"Intel-IOMMU: Turning off 16M unity map for LPC\n");
 		}
 
 		str += strcspn(str, ",");
@@ -1582,6 +1588,25 @@
 	}
 }
 
+static void iommu_prepare_isa(void)
+{
+	struct pci_dev *pdev = NULL;
+	int ret;
+
+	if (!dmar_fix_isa)
+		return;
+
+	pdev = pci_get_class (PCI_CLASS_BRIDGE_ISA << 8, NULL);
+	if (!pdev)
+		return;
+
+	printk (KERN_INFO "IOMMU: Prepare 0-16M unity mapping for LPC\n");
+	ret = iommu_prepare_identity_map(pdev, 0, 16*1024*1024);
+
+	if (ret)
+		printk ("IOMMU: Failed to create 0-64M identity map, Floppy might not work\n");
+
+}
 int __init init_dmars(void)
 {
 	struct dmar_drhd_unit *drhd;
@@ -1638,6 +1663,7 @@
 	end_for_each_rmrr_device(rmrr, pdev)
 
 	iommu_prepare_gfx_mapping();
+	iommu_prepare_isa();
 
 	/*
 	 * for each drhd
author
#10 lore raw

PATCH 8/8 Preserve some Virtual Address when devices cannot address entire range.

In reply to Ashok Raj

Some devices may not support entire 64bit DMA. In a situation where such devices are co-located in a shared domain, we need to ensure there is some address space reserved for such devices without the low addresses getting depleted by other devices capable of handling high dma addresses.

Documentation/kernel-parameters.txt +5 -0
Index: linux-2.6.21-rc5/Documentation/kernel-parameters.txt
===================================================================
@@ -735,6 +735,11 @@
 			first 16M. The floppy disk could be modified to use
 			the DMA api's but thats a lot of pain for very small
 			gain. This option is turned on by default.
+		preserve_{1g/2g/4g/512m/256m/16m}
+			If a device is sharing a domain with other devices
+			and the device mask doesnt cover the 64bit range,
+			use this option to let the iommu code preserve some
+			virtual addr for such devices.
 	io7=		[HW] IO7 for Marvel based alpha systems
 			See comment before marvel_specify_io7 in
 			arch/alpha/kernel/core_marvel.c.
drivers/pci/intel-iommu.c +32 -5
Index: linux-2.6.21-rc5/drivers/pci/intel-iommu.c
===================================================================
@@ -90,6 +90,7 @@
 static int dmar_disabled, dmar_force_rw;
 static int dmar_map_gfx = 1, dmar_no_gfx_identity_map = 1;
 static int dmar_fix_isa = 1;
+static u64 dmar_preserve_iova_mask;
 
 static char *get_fault_reason(u8 fault_reason)
 {
@@ -119,6 +120,32 @@
 		} else if (!strncmp(str, "noisamap", 8)) {
 			dmar_fix_isa = 0;
 			printk (KERN_INFO"Intel-IOMMU: Turning off 16M unity map for LPC\n");
+ 		} else if (!strncmp(str, "preserve_", 9)) {
+ 			if (!strncmp(str + 9, "4g", 2) ||
+ 			    !strncmp(str + 9, "4G", 2))
+ 				dmar_preserve_iova_mask = DMA_32BIT_MASK;
+ 			else if (!strncmp(str + 9, "2g", 2) ||
+ 				!strncmp(str + 9, "2G", 2))
+ 				dmar_preserve_iova_mask = DMA_31BIT_MASK;
+ 			else if (!strncmp(str + 9, "1g", 2) ||
+ 			         !strncmp(str + 9, "1G", 2))
+ 				dmar_preserve_iova_mask = DMA_30BIT_MASK;
+ 			else if (!strncmp(str + 9, "512m", 2) ||
+ 			         !strncmp(str + 9, "512M", 2))
+ 				dmar_preserve_iova_mask = DMA_29BIT_MASK;
+ 			else if (!strncmp(str + 9, "256m", 4) ||
+ 			         !strncmp(str + 9, "256M", 4))
+ 				dmar_preserve_iova_mask = DMA_28BIT_MASK;
+ 			else if (!strncmp(str + 9, "16m", 3) ||
+ 			         !strncmp(str + 9, "16M", 3))
+ 				dmar_preserve_iova_mask = DMA_24BIT_MASK;
+			if (dmar_preserve_iova_mask)
+				printk(KERN_INFO
+				"DMAR: Preserved IOVA mask 0x%Lx for devices "
+				"sharing domain\n", dmar_preserve_iova_mask);
+			else
+				printk(KERN_ERR"DMAR: Unsuppored preserve mask"
+					" provided");
 		}
 
 		str += strcspn(str, ",");
@@ -1723,7 +1750,6 @@
 			last_addr : IOVA_START_ADDR);
 	}
 	return last_addr;
-
 }
 #endif
 
@@ -1751,13 +1777,14 @@
 
 	/*
 	 * If the device shares a domain with other devices and the device can
-	 * handle > 4G DMA, let the device use DMA address started from 4G, so to
-	 * leave rooms for other devices
+	 * can handle higher address, leave rooms for devices that cant
+	 * address high address ranges.
 	 */
 	if ((domain->flags & DOMAIN_FLAG_MULTIPLE_DEVICES) &&
-			pdev->dma_mask > DMA_32BIT_MASK)
+ 			dmar_preserve_iova_mask &&
+ 			(pdev->dma_mask > dmar_preserve_iova_mask))
 		iova = alloc_iova(domain, addr, size,
-				DMA_32BIT_MASK + 1, pdev->dma_mask);
+ 				dmar_preserve_iova_mask + 1, pdev->dma_mask);
 	else {
 #ifndef CONFIG_IOVA_NEXT_CONTIG
 		iova = alloc_iova(domain, addr, size,