Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.27-rc3 343 lines 8.6 kB view raw
1/* 2 * Copyright (c) 2006, Intel Corporation. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 15 * Place - Suite 330, Boston, MA 02111-1307 USA. 16 * 17 * Copyright (C) 2006-2008 Intel Corporation 18 * Author: Ashok Raj <ashok.raj@intel.com> 19 * Author: Shaohua Li <shaohua.li@intel.com> 20 * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> 21 * 22 * This file implements early detection/parsing of DMA Remapping Devices 23 * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI 24 * tables. 25 */ 26 27#include <linux/pci.h> 28#include <linux/dmar.h> 29#include "iova.h" 30#include "intel-iommu.h" 31 32#undef PREFIX 33#define PREFIX "DMAR:" 34 35/* No locks are needed as DMA remapping hardware unit 36 * list is constructed at boot time and hotplug of 37 * these units are not supported by the architecture. 38 */ 39LIST_HEAD(dmar_drhd_units); 40LIST_HEAD(dmar_rmrr_units); 41 42static struct acpi_table_header * __initdata dmar_tbl; 43 44static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) 45{ 46 /* 47 * add INCLUDE_ALL at the tail, so scan the list will find it at 48 * the very end. 49 */ 50 if (drhd->include_all) 51 list_add_tail(&drhd->list, &dmar_drhd_units); 52 else 53 list_add(&drhd->list, &dmar_drhd_units); 54} 55 56static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr) 57{ 58 list_add(&rmrr->list, &dmar_rmrr_units); 59} 60 61static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, 62 struct pci_dev **dev, u16 segment) 63{ 64 struct pci_bus *bus; 65 struct pci_dev *pdev = NULL; 66 struct acpi_dmar_pci_path *path; 67 int count; 68 69 bus = pci_find_bus(segment, scope->bus); 70 path = (struct acpi_dmar_pci_path *)(scope + 1); 71 count = (scope->length - sizeof(struct acpi_dmar_device_scope)) 72 / sizeof(struct acpi_dmar_pci_path); 73 74 while (count) { 75 if (pdev) 76 pci_dev_put(pdev); 77 /* 78 * Some BIOSes list non-exist devices in DMAR table, just 79 * ignore it 80 */ 81 if (!bus) { 82 printk(KERN_WARNING 83 PREFIX "Device scope bus [%d] not found\n", 84 scope->bus); 85 break; 86 } 87 pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn)); 88 if (!pdev) { 89 printk(KERN_WARNING PREFIX 90 "Device scope device [%04x:%02x:%02x.%02x] not found\n", 91 segment, bus->number, path->dev, path->fn); 92 break; 93 } 94 path ++; 95 count --; 96 bus = pdev->subordinate; 97 } 98 if (!pdev) { 99 printk(KERN_WARNING PREFIX 100 "Device scope device [%04x:%02x:%02x.%02x] not found\n", 101 segment, scope->bus, path->dev, path->fn); 102 *dev = NULL; 103 return 0; 104 } 105 if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && \ 106 pdev->subordinate) || (scope->entry_type == \ 107 ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) { 108 pci_dev_put(pdev); 109 printk(KERN_WARNING PREFIX 110 "Device scope type does not match for %s\n", 111 pci_name(pdev)); 112 return -EINVAL; 113 } 114 *dev = pdev; 115 return 0; 116} 117 118static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, 119 struct pci_dev ***devices, u16 segment) 120{ 121 struct acpi_dmar_device_scope *scope; 122 void * tmp = start; 123 int index; 124 int ret; 125 126 *cnt = 0; 127 while (start < end) { 128 scope = start; 129 if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT || 130 scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) 131 (*cnt)++; 132 else 133 printk(KERN_WARNING PREFIX 134 "Unsupported device scope\n"); 135 start += scope->length; 136 } 137 if (*cnt == 0) 138 return 0; 139 140 *devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL); 141 if (!*devices) 142 return -ENOMEM; 143 144 start = tmp; 145 index = 0; 146 while (start < end) { 147 scope = start; 148 if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT || 149 scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) { 150 ret = dmar_parse_one_dev_scope(scope, 151 &(*devices)[index], segment); 152 if (ret) { 153 kfree(*devices); 154 return ret; 155 } 156 index ++; 157 } 158 start += scope->length; 159 } 160 161 return 0; 162} 163 164/** 165 * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition 166 * structure which uniquely represent one DMA remapping hardware unit 167 * present in the platform 168 */ 169static int __init 170dmar_parse_one_drhd(struct acpi_dmar_header *header) 171{ 172 struct acpi_dmar_hardware_unit *drhd; 173 struct dmar_drhd_unit *dmaru; 174 int ret = 0; 175 static int include_all; 176 177 dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); 178 if (!dmaru) 179 return -ENOMEM; 180 181 drhd = (struct acpi_dmar_hardware_unit *)header; 182 dmaru->reg_base_addr = drhd->address; 183 dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ 184 185 if (!dmaru->include_all) 186 ret = dmar_parse_dev_scope((void *)(drhd + 1), 187 ((void *)drhd) + header->length, 188 &dmaru->devices_cnt, &dmaru->devices, 189 drhd->segment); 190 else { 191 /* Only allow one INCLUDE_ALL */ 192 if (include_all) { 193 printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL " 194 "device scope is allowed\n"); 195 ret = -EINVAL; 196 } 197 include_all = 1; 198 } 199 200 if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) 201 kfree(dmaru); 202 else 203 dmar_register_drhd_unit(dmaru); 204 return ret; 205} 206 207static int __init 208dmar_parse_one_rmrr(struct acpi_dmar_header *header) 209{ 210 struct acpi_dmar_reserved_memory *rmrr; 211 struct dmar_rmrr_unit *rmrru; 212 int ret = 0; 213 214 rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL); 215 if (!rmrru) 216 return -ENOMEM; 217 218 rmrr = (struct acpi_dmar_reserved_memory *)header; 219 rmrru->base_address = rmrr->base_address; 220 rmrru->end_address = rmrr->end_address; 221 ret = dmar_parse_dev_scope((void *)(rmrr + 1), 222 ((void *)rmrr) + header->length, 223 &rmrru->devices_cnt, &rmrru->devices, rmrr->segment); 224 225 if (ret || (rmrru->devices_cnt == 0)) 226 kfree(rmrru); 227 else 228 dmar_register_rmrr_unit(rmrru); 229 return ret; 230} 231 232static void __init 233dmar_table_print_dmar_entry(struct acpi_dmar_header *header) 234{ 235 struct acpi_dmar_hardware_unit *drhd; 236 struct acpi_dmar_reserved_memory *rmrr; 237 238 switch (header->type) { 239 case ACPI_DMAR_TYPE_HARDWARE_UNIT: 240 drhd = (struct acpi_dmar_hardware_unit *)header; 241 printk (KERN_INFO PREFIX 242 "DRHD (flags: 0x%08x)base: 0x%016Lx\n", 243 drhd->flags, drhd->address); 244 break; 245 case ACPI_DMAR_TYPE_RESERVED_MEMORY: 246 rmrr = (struct acpi_dmar_reserved_memory *)header; 247 248 printk (KERN_INFO PREFIX 249 "RMRR base: 0x%016Lx end: 0x%016Lx\n", 250 rmrr->base_address, rmrr->end_address); 251 break; 252 } 253} 254 255/** 256 * parse_dmar_table - parses the DMA reporting table 257 */ 258static int __init 259parse_dmar_table(void) 260{ 261 struct acpi_table_dmar *dmar; 262 struct acpi_dmar_header *entry_header; 263 int ret = 0; 264 265 dmar = (struct acpi_table_dmar *)dmar_tbl; 266 if (!dmar) 267 return -ENODEV; 268 269 if (dmar->width < PAGE_SHIFT_4K - 1) { 270 printk(KERN_WARNING PREFIX "Invalid DMAR haw\n"); 271 return -EINVAL; 272 } 273 274 printk (KERN_INFO PREFIX "Host address width %d\n", 275 dmar->width + 1); 276 277 entry_header = (struct acpi_dmar_header *)(dmar + 1); 278 while (((unsigned long)entry_header) < 279 (((unsigned long)dmar) + dmar_tbl->length)) { 280 dmar_table_print_dmar_entry(entry_header); 281 282 switch (entry_header->type) { 283 case ACPI_DMAR_TYPE_HARDWARE_UNIT: 284 ret = dmar_parse_one_drhd(entry_header); 285 break; 286 case ACPI_DMAR_TYPE_RESERVED_MEMORY: 287 ret = dmar_parse_one_rmrr(entry_header); 288 break; 289 default: 290 printk(KERN_WARNING PREFIX 291 "Unknown DMAR structure type\n"); 292 ret = 0; /* for forward compatibility */ 293 break; 294 } 295 if (ret) 296 break; 297 298 entry_header = ((void *)entry_header + entry_header->length); 299 } 300 return ret; 301} 302 303 304int __init dmar_table_init(void) 305{ 306 307 int ret; 308 309 ret = parse_dmar_table(); 310 if (ret) { 311 printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); 312 return ret; 313 } 314 315 if (list_empty(&dmar_drhd_units)) { 316 printk(KERN_INFO PREFIX "No DMAR devices found\n"); 317 return -ENODEV; 318 } 319 320 if (list_empty(&dmar_rmrr_units)) 321 printk(KERN_INFO PREFIX "No RMRR found\n"); 322 323 return 0; 324} 325 326/** 327 * early_dmar_detect - checks to see if the platform supports DMAR devices 328 */ 329int __init early_dmar_detect(void) 330{ 331 acpi_status status = AE_OK; 332 333 /* if we could find DMAR table, then there are DMAR devices */ 334 status = acpi_get_table(ACPI_SIG_DMAR, 0, 335 (struct acpi_table_header **)&dmar_tbl); 336 337 if (ACPI_SUCCESS(status) && !dmar_tbl) { 338 printk (KERN_WARNING PREFIX "Unable to map DMAR\n"); 339 status = AE_NOT_FOUND; 340 } 341 342 return (ACPI_SUCCESS(status) ? 1 : 0); 343}