ia64/xen-unstable

view xen/arch/x86/hvm/vmx/vtd/dmar.c @ 16509:0e8e68cfc8ac

vt-d: Print messages when:
- vt-d has been enabled by xen
- user attempts assign a PCI device that has already been assigned to another hvm guest

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Dec 04 10:29:00 2007 +0000 (2007-12-04)
parents 03d6d0f96e12
children 8ae3f083490a
line source
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) Allen Kay <allen.m.kay@intel.com> - adapted to xen
20 */
22 #include <xen/init.h>
23 #include <xen/bitmap.h>
24 #include <xen/kernel.h>
25 #include <xen/acpi.h>
26 #include <xen/mm.h>
27 #include <xen/xmalloc.h>
28 #include <asm/string.h>
29 #include "dmar.h"
30 #include "pci-direct.h"
31 #include "pci_regs.h"
33 #define VTDPREFIX
34 int vtd_enabled;
35 boolean_param("vtd", vtd_enabled);
37 #undef PREFIX
38 #define PREFIX VTDPREFIX "ACPI DMAR:"
39 #define DEBUG
41 #define MIN_SCOPE_LEN (sizeof(struct acpi_pci_path) + \
42 sizeof(struct acpi_dev_scope))
44 LIST_HEAD(acpi_drhd_units);
45 LIST_HEAD(acpi_rmrr_units);
46 LIST_HEAD(acpi_atsr_units);
47 LIST_HEAD(acpi_ioapic_units);
49 u8 dmar_host_address_width;
51 static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd)
52 {
53 /*
54 * add INCLUDE_ALL at the tail, so scan the list will find it at
55 * the very end.
56 */
57 if ( drhd->include_all )
58 list_add_tail(&drhd->list, &acpi_drhd_units);
59 else
60 list_add(&drhd->list, &acpi_drhd_units);
61 return 0;
62 }
64 static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr)
65 {
66 list_add(&rmrr->list, &acpi_rmrr_units);
67 return 0;
68 }
70 static int acpi_pci_device_match(struct pci_dev *devices, int cnt,
71 struct pci_dev *dev)
72 {
73 int i;
75 for ( i = 0; i < cnt; i++ )
76 {
77 if ( (dev->bus == devices->bus) &&
78 (dev->devfn == devices->devfn) )
79 return 1;
80 devices++;
81 }
82 return 0;
83 }
85 static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr)
86 {
87 /*
88 * add ALL_PORTS at the tail, so scan the list will find it at
89 * the very end.
90 */
91 if ( atsr->all_ports )
92 list_add_tail(&atsr->list, &acpi_atsr_units);
93 else
94 list_add(&atsr->list, &acpi_atsr_units);
95 return 0;
96 }
98 struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev)
99 {
100 struct acpi_drhd_unit *drhd;
101 struct acpi_drhd_unit *include_all_drhd;
103 include_all_drhd = NULL;
104 list_for_each_entry ( drhd, &acpi_drhd_units, list )
105 {
106 if ( drhd->include_all )
107 include_all_drhd = drhd;
108 if ( acpi_pci_device_match(drhd->devices,
109 drhd->devices_cnt, dev) )
110 {
111 gdprintk(XENLOG_INFO VTDPREFIX,
112 "acpi_find_matched_drhd_unit: drhd->address = %lx\n",
113 drhd->address);
114 return drhd;
115 }
116 }
118 if ( include_all_drhd )
119 {
120 gdprintk(XENLOG_INFO VTDPREFIX,
121 "acpi_find_matched_drhd_unit:include_all_drhd->addr = %lx\n",
122 include_all_drhd->address);
123 return include_all_drhd;;
124 }
126 return NULL;
127 }
129 struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev)
130 {
131 struct acpi_rmrr_unit *rmrr;
133 list_for_each_entry ( rmrr, &acpi_rmrr_units, list )
134 {
135 if ( acpi_pci_device_match(rmrr->devices,
136 rmrr->devices_cnt, dev) )
137 return rmrr;
138 }
140 return NULL;
141 }
143 struct acpi_atsr_unit * acpi_find_matched_atsr_unit(struct pci_dev *dev)
144 {
145 struct acpi_atsr_unit *atsru;
146 struct acpi_atsr_unit *all_ports_atsru;
148 all_ports_atsru = NULL;
149 list_for_each_entry ( atsru, &acpi_atsr_units, list )
150 {
151 if ( atsru->all_ports )
152 all_ports_atsru = atsru;
153 if ( acpi_pci_device_match(atsru->devices,
154 atsru->devices_cnt, dev) )
155 return atsru;
156 }
158 if ( all_ports_atsru )
159 {
160 gdprintk(XENLOG_INFO VTDPREFIX,
161 "acpi_find_matched_atsr_unit: all_ports_atsru\n");
162 return all_ports_atsru;;
163 }
165 return NULL;
166 }
168 static int scope_device_count(void *start, void *end)
169 {
170 struct acpi_dev_scope *scope;
171 u8 bus, sub_bus, sec_bus;
172 struct acpi_pci_path *path;
173 int depth, count = 0;
174 u8 dev, func;
175 u32 l;
177 while ( start < end )
178 {
179 scope = start;
180 if ( scope->length < MIN_SCOPE_LEN )
181 {
182 printk(KERN_WARNING PREFIX "Invalid device scope\n");
183 return -EINVAL;
184 }
186 path = (struct acpi_pci_path *)(scope + 1);
187 bus = scope->start_bus;
188 depth = (scope->length - sizeof(struct acpi_dev_scope))
189 / sizeof(struct acpi_pci_path);
190 while ( --depth )
191 {
192 bus = read_pci_config_byte(
193 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
194 path++;
195 }
197 if ( scope->dev_type == ACPI_DEV_ENDPOINT )
198 {
199 printk(KERN_INFO PREFIX
200 "found endpoint: bdf = %x:%x:%x\n",
201 bus, path->dev, path->fn);
202 count++;
203 }
204 else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE )
205 {
206 printk(KERN_INFO PREFIX
207 "found bridge: bdf = %x:%x:%x\n",
208 bus, path->dev, path->fn);
209 sec_bus = read_pci_config_byte(
210 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
211 sub_bus = read_pci_config_byte(
212 bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
214 while ( sec_bus <= sub_bus )
215 {
216 for ( dev = 0; dev < 32; dev++ )
217 {
218 for ( func = 0; func < 8; func++ )
219 {
220 l = read_pci_config(
221 sec_bus, dev, func, PCI_VENDOR_ID);
223 /* some broken boards return 0 or
224 * ~0 if a slot is empty
225 */
226 if ( l == 0xffffffff || l == 0x00000000 ||
227 l == 0x0000ffff || l == 0xffff0000 )
228 break;
229 count++;
230 }
231 }
232 sec_bus++;
233 }
234 }
235 else if ( scope->dev_type == ACPI_DEV_IOAPIC )
236 {
237 printk(KERN_INFO PREFIX
238 "found IOAPIC: bdf = %x:%x:%x\n",
239 bus, path->dev, path->fn);
240 count++;
241 }
242 else
243 {
244 printk(KERN_INFO PREFIX
245 "found MSI HPET: bdf = %x:%x:%x\n",
246 bus, path->dev, path->fn);
247 count++;
248 }
250 start += scope->length;
251 }
253 return count;
254 }
256 static int __init acpi_parse_dev_scope(void *start, void *end, int *cnt,
257 struct pci_dev **devices)
258 {
259 struct acpi_dev_scope *scope;
260 u8 bus, sub_bus, sec_bus;
261 struct acpi_pci_path *path;
262 struct acpi_ioapic_unit *acpi_ioapic_unit = NULL;
263 int depth;
264 struct pci_dev *pdev;
265 u8 dev, func;
266 u32 l;
268 *cnt = scope_device_count(start, end);
269 if ( *cnt == 0 )
270 {
271 printk(KERN_INFO PREFIX "acpi_parse_dev_scope: no device\n");
272 return 0;
273 }
275 *devices = xmalloc_array(struct pci_dev, *cnt);
276 if ( !*devices )
277 return -ENOMEM;
278 memset(*devices, 0, sizeof(struct pci_dev) * (*cnt));
280 pdev = *devices;
281 while ( start < end )
282 {
283 scope = start;
284 path = (struct acpi_pci_path *)(scope + 1);
285 depth = (scope->length - sizeof(struct acpi_dev_scope))
286 / sizeof(struct acpi_pci_path);
287 bus = scope->start_bus;
289 while ( --depth )
290 {
291 bus = read_pci_config_byte(
292 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
293 path++;
294 }
296 if ( scope->dev_type == ACPI_DEV_ENDPOINT )
297 {
298 printk(KERN_INFO PREFIX
299 "found endpoint: bdf = %x:%x:%x\n",
300 bus, path->dev, path->fn);
301 pdev->bus = bus;
302 pdev->devfn = PCI_DEVFN(path->dev, path->fn);
303 pdev++;
304 }
305 else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE )
306 {
307 printk(KERN_INFO PREFIX
308 "found bridge: bus = %x dev = %x func = %x\n",
309 bus, path->dev, path->fn);
310 sec_bus = read_pci_config_byte(
311 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
312 sub_bus = read_pci_config_byte(
313 bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
315 while ( sec_bus <= sub_bus )
316 {
317 for ( dev = 0; dev < 32; dev++ )
318 {
319 for ( func = 0; func < 8; func++ )
320 {
321 l = read_pci_config(
322 sec_bus, dev, func, PCI_VENDOR_ID);
324 /* some broken boards return 0 or
325 * ~0 if a slot is empty
326 */
327 if ( l == 0xffffffff || l == 0x00000000 ||
328 l == 0x0000ffff || l == 0xffff0000 )
329 break;
331 pdev->bus = sec_bus;
332 pdev->devfn = PCI_DEVFN(dev, func);
333 pdev++;
334 }
335 }
336 sec_bus++;
337 }
338 }
339 else if ( scope->dev_type == ACPI_DEV_IOAPIC )
340 {
341 acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
342 if ( !acpi_ioapic_unit )
343 return -ENOMEM;
344 acpi_ioapic_unit->apic_id = scope->enum_id;
345 acpi_ioapic_unit->ioapic.bdf.bus = bus;
346 acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
347 acpi_ioapic_unit->ioapic.bdf.func = path->fn;
348 list_add(&acpi_ioapic_unit->list, &acpi_ioapic_units);
349 printk(KERN_INFO PREFIX
350 "found IOAPIC: bus = %x dev = %x func = %x\n",
351 bus, path->dev, path->fn);
352 }
353 else
354 printk(KERN_INFO PREFIX
355 "found MSI HPET: bus = %x dev = %x func = %x\n",
356 bus, path->dev, path->fn);
358 start += scope->length;
359 }
361 return 0;
362 }
364 static int __init
365 acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
366 {
367 struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
368 struct acpi_drhd_unit *dmaru;
369 int ret = 0;
370 static int include_all;
372 dmaru = xmalloc(struct acpi_drhd_unit);
373 if ( !dmaru )
374 return -ENOMEM;
375 memset(dmaru, 0, sizeof(struct acpi_drhd_unit));
377 dmaru->address = drhd->address;
378 dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */
379 printk(KERN_INFO PREFIX "dmaru->address = %lx\n", dmaru->address);
381 if ( !dmaru->include_all )
382 ret = acpi_parse_dev_scope(
383 (void *)(drhd + 1),
384 ((void *)drhd) + header->length,
385 &dmaru->devices_cnt, &dmaru->devices);
386 else
387 {
388 printk(KERN_INFO PREFIX "found INCLUDE_ALL\n");
389 /* Only allow one INCLUDE_ALL */
390 if ( include_all )
391 {
392 printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL "
393 "device scope is allowed\n");
394 ret = -EINVAL;
395 }
396 include_all = 1;
397 }
399 if ( ret )
400 xfree(dmaru);
401 else
402 acpi_register_drhd_unit(dmaru);
403 return ret;
404 }
406 static int __init
407 acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
408 {
409 struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header;
410 struct acpi_rmrr_unit *rmrru;
411 int ret = 0;
413 rmrru = xmalloc(struct acpi_rmrr_unit);
414 if ( !rmrru )
415 return -ENOMEM;
416 memset(rmrru, 0, sizeof(struct acpi_rmrr_unit));
418 rmrru->base_address = rmrr->base_address;
419 rmrru->end_address = rmrr->end_address;
420 printk(KERN_INFO PREFIX
421 "acpi_parse_one_rmrr: base=%"PRIx64" end=%"PRIx64"\n",
422 rmrr->base_address, rmrr->end_address);
424 ret = acpi_parse_dev_scope(
425 (void *)(rmrr + 1),
426 ((void*)rmrr) + header->length,
427 &rmrru->devices_cnt, &rmrru->devices);
429 if ( ret || (rmrru->devices_cnt == 0) )
430 xfree(rmrru);
431 else
432 acpi_register_rmrr_unit(rmrru);
433 return ret;
434 }
436 static int __init
437 acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
438 {
439 struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
440 struct acpi_atsr_unit *atsru;
441 int ret = 0;
442 static int all_ports;
444 atsru = xmalloc(struct acpi_atsr_unit);
445 if ( !atsru )
446 return -ENOMEM;
447 memset(atsru, 0, sizeof(struct acpi_atsr_unit));
449 atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */
450 if ( !atsru->all_ports )
451 ret = acpi_parse_dev_scope(
452 (void *)(atsr + 1),
453 ((void *)atsr) + header->length,
454 &atsru->devices_cnt, &atsru->devices);
455 else
456 {
457 printk(KERN_INFO PREFIX "found ALL_PORTS\n");
458 /* Only allow one ALL_PORTS */
459 if ( all_ports )
460 {
461 printk(KERN_WARNING PREFIX "Only one ALL_PORTS "
462 "device scope is allowed\n");
463 ret = -EINVAL;
464 }
465 all_ports = 1;
466 }
468 if ( ret )
469 xfree(atsr);
470 else
471 acpi_register_atsr_unit(atsru);
472 return ret;
473 }
475 static int __init acpi_parse_dmar(unsigned long phys_addr,
476 unsigned long size)
477 {
478 struct acpi_table_dmar *dmar = NULL;
479 struct acpi_dmar_entry_header *entry_header;
480 int ret = 0;
482 if ( !phys_addr || !size )
483 return -EINVAL;
485 dmar = (struct acpi_table_dmar *)__acpi_map_table(phys_addr, size);
486 if ( !dmar )
487 {
488 printk(KERN_WARNING PREFIX "Unable to map DMAR\n");
489 return -ENODEV;
490 }
492 if ( !dmar->haw )
493 {
494 printk(KERN_WARNING PREFIX "Zero: Invalid DMAR haw\n");
495 return -EINVAL;
496 }
498 dmar_host_address_width = dmar->haw;
499 printk(KERN_INFO PREFIX "Host address width %d\n",
500 dmar_host_address_width);
502 entry_header = (struct acpi_dmar_entry_header *)(dmar + 1);
503 while ( ((unsigned long)entry_header) <
504 (((unsigned long)dmar) + size) )
505 {
506 switch ( entry_header->type )
507 {
508 case ACPI_DMAR_DRHD:
509 printk(KERN_INFO PREFIX "found ACPI_DMAR_DRHD\n");
510 ret = acpi_parse_one_drhd(entry_header);
511 break;
512 case ACPI_DMAR_RMRR:
513 printk(KERN_INFO PREFIX "found ACPI_DMAR_RMRR\n");
514 ret = acpi_parse_one_rmrr(entry_header);
515 break;
516 case ACPI_DMAR_ATSR:
517 printk(KERN_INFO PREFIX "found ACPI_DMAR_RMRR\n");
518 ret = acpi_parse_one_atsr(entry_header);
519 break;
520 default:
521 printk(KERN_WARNING PREFIX "Unknown DMAR structure type\n");
522 ret = -EINVAL;
523 break;
524 }
525 if ( ret )
526 break;
528 entry_header = ((void *)entry_header + entry_header->length);
529 }
531 /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */
532 dmar->header.signature[0] = '\0';
534 return ret;
535 }
537 int acpi_dmar_init(void)
538 {
539 int rc;
541 if ( !vtd_enabled )
542 return -ENODEV;
544 if ( (rc = vtd_hw_check()) != 0 )
545 return rc;
547 acpi_table_parse(ACPI_DMAR, acpi_parse_dmar);
549 if ( list_empty(&acpi_drhd_units) )
550 {
551 printk(KERN_ERR PREFIX "No DMAR devices found\n");
552 vtd_enabled = 0;
553 return -ENODEV;
554 }
556 printk("Intel VT-d has been enabled\n");
558 return 0;
559 }