This patch adds support for early detection and parsing of DMAR's
reported to OS via ACPI tables.

Signed-off-by: Anil S Keshavamurthy <[EMAIL PROTECTED]>
---
 arch/x86_64/Kconfig   |   11 +
 drivers/pci/Makefile  |    3 
 drivers/pci/dmar.c    |  318 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/acpi/actbl1.h |   27 +++-
 include/linux/dmar.h  |   52 ++++++++
 5 files changed, 404 insertions(+), 7 deletions(-)

Index: linux-2.6.22-rc3/arch/x86_64/Kconfig
===================================================================
--- linux-2.6.22-rc3.orig/arch/x86_64/Kconfig   2007-06-04 12:28:13.000000000 
-0700
+++ linux-2.6.22-rc3/arch/x86_64/Kconfig        2007-06-04 12:33:15.000000000 
-0700
@@ -716,6 +716,17 @@
        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
+       default y
+       help
+         DMA remapping(DMAR) devices support enables independent address
+         translations for Direct Memory Access(DMA) from Devices.
+         These DMA remapping devices are reported via ACPI tables
+         and includes pci device scope covered by these DMA
+         remapping device.
+
 source "drivers/pci/pcie/Kconfig"
 
 source "drivers/pci/Kconfig"
Index: linux-2.6.22-rc3/drivers/pci/Makefile
===================================================================
--- linux-2.6.22-rc3.orig/drivers/pci/Makefile  2007-06-04 12:28:13.000000000 
-0700
+++ linux-2.6.22-rc3/drivers/pci/Makefile       2007-06-04 12:33:15.000000000 
-0700
@@ -20,6 +20,9 @@
 # Build the Hypertransport interrupt support
 obj-$(CONFIG_HT_IRQ) += htirq.o
 
+# Build Intel IOMMU support
+obj-$(CONFIG_DMAR) += dmar.o
+
 #
 # Some architectures use the generic PCI setup functions
 #
Index: linux-2.6.22-rc3/drivers/pci/dmar.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/drivers/pci/dmar.c 2007-06-04 12:33:15.000000000 -0700
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *     Copyright (C) Ashok Raj <[EMAIL PROTECTED]>
+ *     Copyright (C) Shaohua Li <[EMAIL PROTECTED]>
+ *
+ *     This file implements early detection/parsing of DMA Remapping Devices
+ * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
+ * tables.
+ */
+
+#include <linux/pci.h>
+#include <linux/dmar.h>
+
+#undef PREFIX
+#define PREFIX "DMAR:"
+
+/* No locks are needed as DMA remapping hardware unit
+ * list is constructed at boot time and hotplug of
+ * these units are not supported by the architecture.
+ */
+LIST_HEAD(dmar_drhd_units);
+LIST_HEAD(dmar_rmrr_units);
+
+static struct acpi_table_header * __initdata dmar_tbl;
+
+static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
+{
+       /*
+        * add INCLUDE_ALL at the tail, so scan the list will find it at
+        * the very end.
+        */
+       if (drhd->include_all)
+               list_add_tail(&drhd->list, &dmar_drhd_units);
+       else
+               list_add(&drhd->list, &dmar_drhd_units);
+}
+
+static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
+{
+       list_add(&rmrr->list, &dmar_rmrr_units);
+}
+
+static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope 
*scope,
+                                          struct pci_dev **dev, u16 segment)
+{
+       struct pci_bus *bus;
+       struct pci_dev *pdev = NULL;
+       struct acpi_dmar_pci_path *path;
+       int count;
+
+       bus = pci_find_bus(segment, scope->bus);
+       path = (struct acpi_dmar_pci_path *)(scope + 1);
+       count = (scope->length - sizeof(struct acpi_dmar_device_scope))
+               /sizeof(struct acpi_dmar_pci_path);
+
+       while (count) {
+               if (pdev)
+                       pci_dev_put(pdev);
+               /*
+                * Some BIOSes list non-exist devices in DMAR table, just
+                * ignore it
+                */
+               if (!bus) {
+                       printk(KERN_WARNING
+                       PREFIX "Device scope bus [%d] not found\n",
+                       scope->bus);
+                       break;
+               }
+               pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn));
+               if (!pdev) {
+                       printk(KERN_WARNING PREFIX
+                       "Device scope device [%04x:%02x:%02x.%02x] not found\n",
+                               segment, bus->number, path->dev, path->fn);
+                       break;
+               }
+               path ++;
+               count --;
+               bus = pdev->subordinate;
+       }
+       if (!pdev) {
+               printk(KERN_WARNING PREFIX
+               "Device scope device [%04x:%02x:%02x.%02x] not found\n",
+               segment, scope->bus, path->dev, path->fn);
+               *dev = NULL;
+               return 0;
+       }
+       if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && 
pdev->subordinate)
+          || (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE && 
!pdev->subordinate)) {
+               pci_dev_put(pdev);
+               printk(KERN_WARNING PREFIX "Device scope type does not match 
for %s\n", pci_name(pdev));
+               return -EINVAL;
+       }
+       *dev = pdev;
+       return 0;
+}
+
+static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
+                                      struct pci_dev ***devices, u16 segment)
+{
+       struct acpi_dmar_device_scope *scope;
+       void * tmp = start;
+       int index;
+       int ret;
+
+       *cnt = 0;
+       while (start < end) {
+               scope = start;
+               if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
+                   scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
+                       (*cnt)++;
+               else
+                       printk(KERN_WARNING PREFIX "Unsupported device 
scope\n");
+               start += scope->length;
+       }
+       if (*cnt == 0)
+               return 0;
+
+       *devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
+       if (!*devices)
+               return -ENOMEM;
+
+       start = tmp;
+       index = 0;
+       while (start < end) {
+               scope = start;
+               if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
+                   scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) {
+                       ret = dmar_parse_one_dev_scope(scope,
+                               &(*devices)[index], segment);
+                       if (ret) {
+                               kfree(*devices);
+                               return ret;
+                       }
+                       index ++;
+               }
+               start += scope->length;
+       }
+
+       return 0;
+}
+
+/**
+ * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
+ * structure which uniquely represent one DMA remapping hardware unit
+ * present in the platform
+ */
+static int __init
+dmar_parse_one_drhd(struct acpi_dmar_header *header)
+{
+       struct acpi_dmar_hardware_unit * drhd = (struct acpi_dmar_hardware_unit 
*)header;
+       struct dmar_drhd_unit *dmaru;
+       int ret = 0;
+       static int include_all;
+
+       dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
+       if (!dmaru)
+               return -ENOMEM;
+
+       dmaru->reg_base_addr = drhd->address;
+       dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
+
+       if (!dmaru->include_all)
+               ret = dmar_parse_dev_scope((void *)(drhd + 1),
+                               ((void *)drhd) + header->length,
+                               &dmaru->devices_cnt, &dmaru->devices,
+                               drhd->segment);
+       else {
+               /* Only allow one INCLUDE_ALL */
+               if (include_all) {
+                       printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL "
+                               "device scope is allowed\n");
+                       ret = -EINVAL;
+               }
+               include_all = 1;
+       }
+
+       if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all))
+               kfree(dmaru);
+       else
+               dmar_register_drhd_unit(dmaru);
+       return ret;
+}
+
+static int __init
+dmar_parse_one_rmrr(struct acpi_dmar_header *header)
+{
+       struct acpi_dmar_reserved_memory *rmrr = (struct 
acpi_dmar_reserved_memory *)header;
+       struct dmar_rmrr_unit *rmrru;
+       int ret = 0;
+
+       rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL);
+       if (!rmrru)
+               return -ENOMEM;
+
+       rmrru->base_address = rmrr->base_address;
+       rmrru->end_address = rmrr->end_address;
+       ret = dmar_parse_dev_scope((void *)(rmrr + 1),
+               ((void*)rmrr) + header->length,
+               &rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
+
+       if (ret || (rmrru->devices_cnt == 0))
+               kfree(rmrru);
+       else
+               dmar_register_rmrr_unit(rmrru);
+       return ret;
+}
+
+static void __init
+dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
+{
+       struct acpi_dmar_hardware_unit *drhd;
+       struct acpi_dmar_reserved_memory *rmrr;
+
+       switch (header->type) {
+       case ACPI_DMAR_TYPE_HARDWARE_UNIT:
+               drhd = (struct acpi_dmar_hardware_unit *)header;
+               printk (KERN_INFO PREFIX
+                       "DRHD (flags: 0x%08x)base: 0x%016Lx\n",
+                       drhd->flags, drhd->address);
+               break;
+       case ACPI_DMAR_TYPE_RESERVED_MEMORY:
+               rmrr = (struct acpi_dmar_reserved_memory *)header;
+
+               printk (KERN_INFO PREFIX
+                       "RMRR base: 0x%016Lx end: 0x%016Lx\n",
+                       rmrr->base_address, rmrr->end_address);
+               break;
+       }
+}
+
+/**
+ * parse_dmar_table - parses the DMA reporting table
+ */
+static int __init
+parse_dmar_table(void)
+{
+       struct acpi_table_dmar *dmar;
+       struct acpi_dmar_header *entry_header;
+       int ret = 0;
+
+       dmar = (struct acpi_table_dmar *)dmar_tbl;
+
+       if (!dmar->width) {
+               printk (KERN_WARNING PREFIX "Zero: Invalid DMAR haw\n");
+               return -EINVAL;
+       }
+
+       printk (KERN_INFO PREFIX "Host address width %d\n",
+               dmar->width + 1);
+
+       entry_header = (struct acpi_dmar_header *)(dmar + 1);
+       while (((unsigned long)entry_header) < (((unsigned long)dmar) + 
dmar_tbl->length)) {
+               dmar_table_print_dmar_entry(entry_header);
+
+               switch (entry_header->type) {
+               case ACPI_DMAR_TYPE_HARDWARE_UNIT:
+                       ret = dmar_parse_one_drhd(entry_header);
+                       break;
+               case ACPI_DMAR_TYPE_RESERVED_MEMORY:
+                       ret = dmar_parse_one_rmrr(entry_header);
+                       break;
+               default:
+                       printk(KERN_WARNING PREFIX "Unknown DMAR structure 
type\n");
+                       ret = 0; /* for forward compatibility */
+                       break;
+               }
+               if (ret)
+                       break;
+
+               entry_header = ((void *)entry_header + entry_header->length);
+       }
+       return ret;
+}
+
+
+int __init dmar_table_init(void)
+{
+
+       parse_dmar_table();
+       if (list_empty(&dmar_drhd_units)) {
+               printk(KERN_ERR PREFIX "No DMAR devices found\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+/**
+ * early_dmar_detect - checks to see if the platform supports DMAR devices
+ */
+int __init early_dmar_detect(void)
+{
+       acpi_status status = AE_OK;
+
+       /* if we could find DMAR table, then there are DMAR devices */
+       status = acpi_get_table(ACPI_SIG_DMAR, 0,
+                               (struct acpi_table_header **)&dmar_tbl);
+
+       if (ACPI_SUCCESS(status) && !dmar_tbl) {
+               printk (KERN_WARNING PREFIX "Unable to map DMAR\n");
+               status = AE_NOT_FOUND;
+       }
+
+       return (ACPI_SUCCESS(status) ? 1 : 0);
+}
Index: linux-2.6.22-rc3/include/acpi/actbl1.h
===================================================================
--- linux-2.6.22-rc3.orig/include/acpi/actbl1.h 2007-06-04 12:28:13.000000000 
-0700
+++ linux-2.6.22-rc3/include/acpi/actbl1.h      2007-06-04 12:33:15.000000000 
-0700
@@ -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 */
 };
 
Index: linux-2.6.22-rc3/include/linux/dmar.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/include/linux/dmar.h       2007-06-04 12:33:15.000000000 
-0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Copyright (C) Ashok Raj <[EMAIL PROTECTED]>
+ * Copyright (C) Shaohua Li <[EMAIL PROTECTED]>
+ */
+
+#ifndef __DMAR_H__
+#define __DMAR_H__
+
+#include <linux/acpi.h>
+#include <linux/types.h>
+
+
+extern int dmar_table_init(void);
+extern int early_dmar_detect(void);
+
+extern struct list_head dmar_drhd_units;
+extern struct list_head dmar_rmrr_units;
+
+struct dmar_drhd_unit {
+       struct list_head list;          /* list of drhd units   */
+       u64     reg_base_addr;          /* register base address*/
+       struct  pci_dev **devices;      /* target device array  */
+       int     devices_cnt;            /* target device count  */
+       u8      ignored:1;              /* ignore drhd          */
+       u8      include_all:1;
+       struct intel_iommu *iommu;
+};
+
+struct dmar_rmrr_unit {
+       struct list_head list;          /* list of rmrr units   */
+       u64     base_address;           /* reserved base address*/
+       u64     end_address;            /* reserved end address */
+       struct pci_dev **devices;       /* target devices */
+       int     devices_cnt;            /* target device count */
+};
+
+#endif /* __DMAR_H__ */

-- 
-
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/

Reply via email to