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