This patch contains basic ACPI parsing and enumeration support. Signed-off-by: Ashok Raj Signed-off-by: Shaohua Li Index: linux-2.6.21-rc5/arch/x86_64/Kconfig =================================================================== --- linux-2.6.21-rc5.orig/arch/x86_64/Kconfig 2007-04-03 04:30:40.000000000 -0700 +++ linux-2.6.21-rc5/arch/x86_64/Kconfig 2007-04-03 06:34:17.000000000 -0700 @@ -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" Index: linux-2.6.21-rc5/drivers/acpi/Makefile =================================================================== --- linux-2.6.21-rc5.orig/drivers/acpi/Makefile 2007-04-03 04:30:40.000000000 -0700 +++ linux-2.6.21-rc5/drivers/acpi/Makefile 2007-04-03 06:34:17.000000000 -0700 @@ -60,3 +60,4 @@ obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-y += cm_sbs.o obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o +obj-$(CONFIG_DMAR) += dmar.o Index: linux-2.6.21-rc5/drivers/acpi/dmar.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21-rc5/drivers/acpi/dmar.c 2007-04-03 06:54:27.000000000 -0700 @@ -0,0 +1,344 @@ +/* + * 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 + * Copyright (C) Shaohua Li + */ + +#include +#include +#include +#include + +#include + +#undef PREFIX +#define PREFIX "ACPI DMAR:" + +#define MIN_SCOPE_LEN (sizeof(struct acpi_pci_path) + sizeof(struct acpi_dev_scope)) + +LIST_HEAD(acpi_drhd_units); +LIST_HEAD(acpi_rmrr_units); +u8 dmar_host_address_width; + +static int __init acpi_register_drhd_unit(struct acpi_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, &acpi_drhd_units); + else + list_add(&drhd->list, &acpi_drhd_units); + return 0; +} + +static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr) +{ + list_add(&rmrr->list, &acpi_rmrr_units); + return 0; +} + +static int acpi_pci_device_match(struct pci_dev *devices[], int cnt, + struct pci_dev *dev) +{ + int index; + + while (dev) { + for (index = 0; index < cnt; index ++) + if (dev == devices[index]) + return 1; + + /* Check our parent */ + dev = dev->bus->self; + } + + return 0; +} + +struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev) +{ + struct acpi_drhd_unit *drhd = NULL; + + list_for_each_entry(drhd, &acpi_drhd_units, list) { + if (drhd->include_all || acpi_pci_device_match(drhd->devices, + drhd->devices_cnt, dev)) + break; + } + + return drhd; +} + +struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev) +{ + struct acpi_rmrr_unit *rmrr; + + list_for_each_entry(rmrr, &acpi_rmrr_units, list) { + if (acpi_pci_device_match(rmrr->devices, + rmrr->devices_cnt, dev)) + goto out; + } + rmrr = NULL; +out: + return rmrr; +} + +static int __init acpi_parse_one_dev_scope(struct acpi_dev_scope *scope, + struct pci_dev **dev, u16 segment) +{ + struct pci_bus *bus; + struct pci_dev *pdev = NULL; + struct acpi_pci_path *path; + int count; + + bus = pci_find_bus(segment, scope->start_bus); + path = (struct acpi_pci_path *)(scope + 1); + count = (scope->length - sizeof(struct acpi_dev_scope)) + /sizeof(struct acpi_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 not found\n"); + 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 not found\n"); + *dev = NULL; + return 0; + } + if ((scope->dev_type == ACPI_DEV_ENDPOINT && pdev->subordinate) + || (scope->dev_type == ACPI_DEV_P2PBRIDGE && !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 acpi_parse_dev_scope(void *start, void *end, int *cnt, + struct pci_dev ***devices, u16 segment) +{ + struct acpi_dev_scope *scope; + void * tmp = start; + int index; + int ret; + + *cnt = 0; + while (start < end) { + scope = start; + if (scope->dev_type == ACPI_DEV_ENDPOINT || + scope->dev_type == ACPI_DEV_P2PBRIDGE) + (*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->dev_type == ACPI_DEV_ENDPOINT || + scope->dev_type == ACPI_DEV_P2PBRIDGE) { + ret = acpi_parse_one_dev_scope(scope, + &(*devices)[index], segment); + if (ret) { + kfree(*devices); + return ret; + } + index ++; + } + start += scope->length; + } + + return 0; +} + +static int __init +acpi_parse_one_drhd(struct acpi_dmar_entry_header *header) +{ + struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header; + struct acpi_drhd_unit *dmaru; + int ret = 0; + static int include_all; + + dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); + if (!dmaru) + return -ENOMEM; + + dmaru->address = drhd->address; + dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */ + + if (!dmaru->include_all) + ret = acpi_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 + acpi_register_drhd_unit(dmaru); + return ret; +} + +static int __init +acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header) +{ + struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header; + struct acpi_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 = acpi_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 + acpi_register_rmrr_unit(rmrru); + return ret; +} + +static void __init +acpi_table_print_dmar_entry(struct acpi_dmar_entry_header *header) +{ + struct acpi_table_drhd *drhd; + struct acpi_table_rmrr *rmrr; + + switch (header->type) { + case ACPI_DMAR_DRHD: + drhd = (struct acpi_table_drhd *)header; + printk (KERN_INFO PREFIX + "DRHD (flags: 0x%08x)base: 0x%016Lx\n", + drhd->flags, drhd->address); + break; + case ACPI_DMAR_RMRR: + rmrr = (struct acpi_table_rmrr *)header; + + printk (KERN_INFO PREFIX + "RMRR base: 0x%016Lx end: 0x%016Lx\n", + rmrr->base_address, rmrr->end_address); + break; + } +} + +static int __init +acpi_parse_dmar(struct acpi_table_header *table) +{ + struct acpi_table_dmar *dmar; + struct acpi_dmar_entry_header *entry_header; + int ret = 0; + + dmar = (struct acpi_table_dmar *)table; + if (!dmar) { + printk (KERN_WARNING PREFIX "Unable to map DMAR\n"); + return -ENODEV; + } + + if (!dmar->haw) { + printk (KERN_WARNING PREFIX "Zero: Invalid DMAR haw\n"); + return -EINVAL; + } + + dmar_host_address_width = dmar->haw + 1; + printk (KERN_INFO PREFIX "Host address width %d\n", + dmar_host_address_width); + + entry_header = (struct acpi_dmar_entry_header *)(dmar + 1); + while (((unsigned long)entry_header) < (((unsigned long)dmar) + table->length)) { + acpi_table_print_dmar_entry(entry_header); + + switch (entry_header->type) { + case ACPI_DMAR_DRHD: + ret = acpi_parse_one_drhd(entry_header); + break; + case ACPI_DMAR_RMRR: + ret = acpi_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 early_dmar_detect(void) +{ + struct acpi_table_header header; + + /* if we could find DMAR table, then there are DMAR devices */ + if (!acpi_get_table_header(ACPI_SIG_DMAR, 0, &header)) + return 1; + return 0; +} + +static int __init acpi_dmar_init(void) +{ + acpi_table_parse(ACPI_SIG_DMAR, acpi_parse_dmar); + if (list_empty(&acpi_drhd_units)) { + printk(KERN_ERR PREFIX "No DMAR devices found\n"); + return -ENODEV; + } + return 0; +} +subsys_initcall(acpi_dmar_init); Index: linux-2.6.21-rc5/include/acpi/actbl1.h =================================================================== --- linux-2.6.21-rc5.orig/include/acpi/actbl1.h 2007-04-03 04:30:50.000000000 -0700 +++ linux-2.6.21-rc5/include/acpi/actbl1.h 2007-04-03 06:34:17.000000000 -0700 @@ -256,41 +256,48 @@ struct acpi_table_dmar { struct acpi_table_header header; /* Common ACPI table header */ - u8 width; /* Host Address Width */ - u8 reserved[11]; + u8 haw; /* Host Address Width */ + u8 flags; + u8 reserved[10]; }; /* DMAR subtable header */ -struct acpi_dmar_header { +struct acpi_dmar_entry_header { u16 type; u16 length; - u8 flags; - u8 reserved[3]; }; /* Values for subtable type in struct acpi_dmar_header */ -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 */ +enum acpi_dmar_entry_type { + ACPI_DMAR_DRHD = 0, + ACPI_DMAR_RMRR = 1, + ACPI_DMAR_ASTR = 2, + ACPI_DMAR_ENTRY_COUNT }; -struct acpi_dmar_device_scope { - u8 entry_type; +struct acpi_dev_scope { + u8 dev_type; u8 length; - u8 segment; - u8 bus; + u16 reserved; + u8 enumeration_id; + u8 start_bus; }; /* Values for entry_type in struct acpi_dmar_device_scope */ -enum acpi_dmar_scope_type { - 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 */ +enum acpi_dev_scope_type { + ACPI_DEV_ENDPOINT = 1, + ACPI_DEV_P2PBRIDGE, + ACPI_DEV_IOAPIC, + ACPI_DEV_HPET, + ACPI_DEV_ENTRY_COUNT +}; + +struct acpi_pci_path { + u8 dev; + u8 fn; }; /* @@ -299,9 +306,12 @@ /* 0: Hardware Unit Definition */ -struct acpi_dmar_hardware_unit { - struct acpi_dmar_header header; - u64 address; /* Register Base Address */ +struct acpi_table_drhd { + struct acpi_dmar_entry_header header; + u8 flags; /* BIT0: INCLUDE_ALL */ + u8 reserved; + u16 segment; + u64 address; /* register base address for this drhd */ }; /* Flags */ @@ -310,16 +320,14 @@ /* 1: Reserved Memory Defininition */ -struct acpi_dmar_reserved_memory { - struct acpi_dmar_header header; - u64 address; /* 4_k aligned base address */ - u64 end_address; /* 4_k aligned limit address */ +struct acpi_table_rmrr { + struct acpi_dmar_entry_header header; + u16 reserved; + u16 segment; + u64 base_address; + u64 end_address; }; -/* Flags */ - -#define ACPI_DMAR_ALLOW_ALL (1) - /******************************************************************************* * * ECDT - Embedded Controller Boot Resources Table Index: linux-2.6.21-rc5/include/acpi/dmar.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.21-rc5/include/acpi/dmar.h 2007-04-03 06:34:17.000000000 -0700 @@ -0,0 +1,60 @@ +/* + * 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 + * Copyright (C) Shaohua Li + */ + +extern u8 dmar_host_address_width; +extern struct list_head acpi_drhd_units; +extern struct list_head acpi_rmrr_units; + +#define no_drhd_detected() (list_empty(&acpi_drhd_units)) + +struct iommu; +struct acpi_drhd_unit { + struct list_head list; + u64 address; /* register base address of the unit */ + struct pci_dev **devices; /* target devices */ + int devices_cnt; + u8 include_all:1; + struct iommu *iommu; +}; + +struct acpi_rmrr_unit { + struct list_head list; + u64 base_address; + u64 end_address; + struct pci_dev **devices; /* target devices */ + int devices_cnt; +}; + +#define for_each_drhd_unit(drhd) \ + list_for_each_entry(drhd, &acpi_drhd_units, list) +#define for_each_rmrr_device(rmrr, pdev) \ + list_for_each_entry(rmrr, &acpi_rmrr_units, list) { \ + int _i; \ + for (_i = 0; _i < rmrr->devices_cnt; _i++) { \ + pdev = rmrr->devices[_i]; \ + /* some BIOS lists non-exist devices in DMAR table */\ + if (!pdev) \ + continue; +#define end_for_each_rmrr_device(rmrr, pdev) \ + } \ + } + +struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev); +struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev); -- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/