ia64/xen-unstable

view xen/drivers/passthrough/vtd/dmar.c @ 19731:01748ccc4da3

Intel VT-d: fix Stoakley boot issue with iommu=1

Signed-off-by: Weidong Han <Weidong.han@intel.com>
Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 09:25:50 2009 +0100 (2009-06-05)
parents f3bed18decfc
children 931dbe86e5f3
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/errno.h>
25 #include <xen/kernel.h>
26 #include <xen/acpi.h>
27 #include <xen/mm.h>
28 #include <xen/xmalloc.h>
29 #include <xen/pci.h>
30 #include <xen/pci_regs.h>
31 #include <asm/string.h>
32 #include "dmar.h"
33 #include "iommu.h"
35 int vtd_enabled = 1;
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);
48 u8 dmar_host_address_width;
50 void dmar_scope_add_buses(struct dmar_scope *scope, u16 sec_bus, u16 sub_bus)
51 {
52 sub_bus &= 0xff;
53 if (sec_bus > sub_bus)
54 return;
56 while ( sec_bus <= sub_bus )
57 set_bit(sec_bus++, scope->buses);
58 }
60 void dmar_scope_remove_buses(struct dmar_scope *scope, u16 sec_bus, u16 sub_bus)
61 {
62 sub_bus &= 0xff;
63 if (sec_bus > sub_bus)
64 return;
66 while ( sec_bus <= sub_bus )
67 clear_bit(sec_bus++, scope->buses);
68 }
70 static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd)
71 {
72 /*
73 * add INCLUDE_ALL at the tail, so scan the list will find it at
74 * the very end.
75 */
76 if ( drhd->include_all )
77 list_add_tail(&drhd->list, &acpi_drhd_units);
78 else
79 list_add(&drhd->list, &acpi_drhd_units);
80 return 0;
81 }
83 static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr)
84 {
85 list_add(&rmrr->list, &acpi_rmrr_units);
86 return 0;
87 }
89 static void __init disable_all_dmar_units(void)
90 {
91 struct acpi_drhd_unit *drhd, *_drhd;
92 struct acpi_rmrr_unit *rmrr, *_rmrr;
93 struct acpi_atsr_unit *atsr, *_atsr;
95 list_for_each_entry_safe ( drhd, _drhd, &acpi_drhd_units, list )
96 {
97 list_del(&drhd->list);
98 xfree(drhd);
99 }
100 list_for_each_entry_safe ( rmrr, _rmrr, &acpi_rmrr_units, list )
101 {
102 list_del(&rmrr->list);
103 xfree(rmrr);
104 }
105 list_for_each_entry_safe ( atsr, _atsr, &acpi_atsr_units, list )
106 {
107 list_del(&atsr->list);
108 xfree(atsr);
109 }
110 }
112 static int acpi_ioapic_device_match(
113 struct list_head *ioapic_list, unsigned int apic_id)
114 {
115 struct acpi_ioapic_unit *ioapic;
116 list_for_each_entry( ioapic, ioapic_list, list ) {
117 if (ioapic->apic_id == apic_id)
118 return 1;
119 }
120 return 0;
121 }
123 struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id)
124 {
125 struct acpi_drhd_unit *drhd;
126 list_for_each_entry( drhd, &acpi_drhd_units, list )
127 if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) )
128 return drhd;
129 return NULL;
130 }
132 struct iommu * ioapic_to_iommu(unsigned int apic_id)
133 {
134 struct acpi_drhd_unit *drhd;
136 list_for_each_entry( drhd, &acpi_drhd_units, list )
137 if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) )
138 return drhd->iommu;
139 return NULL;
140 }
142 static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr)
143 {
144 /*
145 * add ALL_PORTS at the tail, so scan the list will find it at
146 * the very end.
147 */
148 if ( atsr->all_ports )
149 list_add_tail(&atsr->list, &acpi_atsr_units);
150 else
151 list_add(&atsr->list, &acpi_atsr_units);
152 return 0;
153 }
155 struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *pdev)
156 {
157 u8 bus, devfn;
158 struct acpi_drhd_unit *drhd;
159 struct acpi_drhd_unit *include_all = NULL;
160 int i;
162 if (pdev->info.is_extfn) {
163 bus = pdev->bus;
164 devfn = 0;
165 } else if (pdev->info.is_virtfn) {
166 bus = pdev->info.physfn.bus;
167 devfn = PCI_SLOT(pdev->info.physfn.devfn) ? 0 : pdev->info.physfn.devfn;
168 } else {
169 bus = pdev->bus;
170 devfn = pdev->devfn;
171 }
173 list_for_each_entry ( drhd, &acpi_drhd_units, list )
174 {
175 for (i = 0; i < drhd->scope.devices_cnt; i++)
176 if ( drhd->scope.devices[i] == PCI_BDF2(bus, devfn) )
177 return drhd;
179 if ( test_bit(bus, drhd->scope.buses) )
180 return drhd;
182 if ( drhd->include_all )
183 include_all = drhd;
184 }
185 return include_all;
186 }
188 struct acpi_atsr_unit * acpi_find_matched_atsr_unit(u8 bus, u8 devfn)
189 {
190 struct acpi_atsr_unit *atsr;
191 struct acpi_atsr_unit *all_ports = NULL;
193 list_for_each_entry ( atsr, &acpi_atsr_units, list )
194 {
195 if ( test_bit(bus, atsr->scope.buses) )
196 return atsr;
198 if ( atsr->all_ports )
199 all_ports = atsr;
200 }
201 return all_ports;
202 }
204 /*
205 * Count number of devices in device scope. Do not include PCI sub
206 * hierarchies.
207 */
208 static int scope_device_count(void *start, void *end)
209 {
210 struct acpi_dev_scope *scope;
211 int count = 0;
213 while ( start < end )
214 {
215 scope = start;
216 if ( (scope->length < MIN_SCOPE_LEN) ||
217 (scope->dev_type >= ACPI_DEV_ENTRY_COUNT) )
218 {
219 dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope.\n");
220 return -EINVAL;
221 }
223 if ( scope->dev_type == ACPI_DEV_P2PBRIDGE ||
224 scope->dev_type == ACPI_DEV_ENDPOINT ||
225 scope->dev_type == ACPI_DEV_IOAPIC ||
226 scope->dev_type == ACPI_DEV_MSI_HPET )
227 count++;
229 start += scope->length;
230 }
232 return count;
233 }
236 static int __init acpi_parse_dev_scope(void *start, void *end,
237 void *acpi_entry, int type)
238 {
239 struct dmar_scope *scope = acpi_entry;
240 struct acpi_ioapic_unit *acpi_ioapic_unit;
241 struct acpi_dev_scope *acpi_scope;
242 u16 bus, sub_bus, sec_bus;
243 struct acpi_pci_path *path;
244 int depth, cnt, didx = 0;
246 if ( (cnt = scope_device_count(start, end)) < 0 )
247 return cnt;
249 scope->devices_cnt = cnt;
250 if ( cnt > 0 )
251 {
252 scope->devices = xmalloc_array(u16, cnt);
253 if ( !scope->devices )
254 return -ENOMEM;
255 memset(scope->devices, 0, sizeof(u16) * cnt);
256 }
258 while ( start < end )
259 {
260 acpi_scope = start;
261 path = (struct acpi_pci_path *)(acpi_scope + 1);
262 depth = (acpi_scope->length - sizeof(struct acpi_dev_scope))
263 / sizeof(struct acpi_pci_path);
264 bus = acpi_scope->start_bus;
266 while ( --depth > 0 )
267 {
268 bus = pci_conf_read8(bus, path->dev, path->fn, PCI_SECONDARY_BUS);
269 path++;
270 }
272 switch ( acpi_scope->dev_type )
273 {
274 case ACPI_DEV_P2PBRIDGE:
275 sec_bus = pci_conf_read8(
276 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
277 sub_bus = pci_conf_read8(
278 bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
279 dprintk(XENLOG_INFO VTDPREFIX,
280 "bridge: %x:%x.%x start = %x sec = %x sub = %x\n",
281 bus, path->dev, path->fn,
282 acpi_scope->start_bus, sec_bus, sub_bus);
284 dmar_scope_add_buses(scope, sec_bus, sub_bus);
285 break;
287 case ACPI_DEV_MSI_HPET:
288 dprintk(XENLOG_INFO VTDPREFIX, "MSI HPET: %x:%x.%x\n",
289 bus, path->dev, path->fn);
290 break;
292 case ACPI_DEV_ENDPOINT:
293 dprintk(XENLOG_INFO VTDPREFIX, "endpoint: %x:%x.%x\n",
294 bus, path->dev, path->fn);
295 break;
297 case ACPI_DEV_IOAPIC:
298 dprintk(XENLOG_INFO VTDPREFIX, "IOAPIC: %x:%x.%x\n",
299 bus, path->dev, path->fn);
301 if ( type == DMAR_TYPE )
302 {
303 struct acpi_drhd_unit *drhd = acpi_entry;
304 acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
305 if ( !acpi_ioapic_unit )
306 return -ENOMEM;
307 acpi_ioapic_unit->apic_id = acpi_scope->enum_id;
308 acpi_ioapic_unit->ioapic.bdf.bus = bus;
309 acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
310 acpi_ioapic_unit->ioapic.bdf.func = path->fn;
311 list_add(&acpi_ioapic_unit->list, &drhd->ioapic_list);
312 }
314 break;
315 }
316 scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
317 start += acpi_scope->length;
318 }
320 return 0;
321 }
323 static int __init
324 acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
325 {
326 struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
327 void *dev_scope_start, *dev_scope_end;
328 struct acpi_drhd_unit *dmaru;
329 int ret = 0;
330 static int include_all = 0;
332 dmaru = xmalloc(struct acpi_drhd_unit);
333 if ( !dmaru )
334 return -ENOMEM;
335 memset(dmaru, 0, sizeof(struct acpi_drhd_unit));
337 dmaru->address = drhd->address;
338 dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */
339 INIT_LIST_HEAD(&dmaru->ioapic_list);
340 dprintk(XENLOG_INFO VTDPREFIX, "dmaru->address = %"PRIx64"\n",
341 dmaru->address);
343 dev_scope_start = (void *)(drhd + 1);
344 dev_scope_end = ((void *)drhd) + header->length;
345 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
346 dmaru, DMAR_TYPE);
348 if ( dmaru->include_all )
349 {
350 dprintk(XENLOG_INFO VTDPREFIX, "found INCLUDE_ALL\n");
351 /* Only allow one INCLUDE_ALL */
352 if ( include_all )
353 {
354 dprintk(XENLOG_WARNING VTDPREFIX,
355 "Only one INCLUDE_ALL device scope is allowed\n");
356 ret = -EINVAL;
357 }
358 include_all = 1;
359 }
361 if ( ret )
362 xfree(dmaru);
363 else
364 acpi_register_drhd_unit(dmaru);
365 return ret;
366 }
368 static int __init
369 acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
370 {
371 struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header;
372 struct acpi_rmrr_unit *rmrru;
373 void *dev_scope_start, *dev_scope_end;
374 int ret = 0;
376 if ( rmrr->base_address >= rmrr->end_address )
377 {
378 dprintk(XENLOG_ERR VTDPREFIX,
379 "RMRR error: base_addr %"PRIx64" end_address %"PRIx64"\n",
380 rmrr->base_address, rmrr->end_address);
381 return -EFAULT;
382 }
384 #ifdef CONFIG_X86
385 /* This check is here simply to detect when RMRR values are not properly represented in the
386 system memory map and inform the user */
387 if ( (!page_is_ram_type(paddr_to_pfn(rmrr->base_address), RAM_TYPE_RESERVED))||
388 (!page_is_ram_type(paddr_to_pfn(rmrr->end_address) - 1, RAM_TYPE_RESERVED)) )
389 {
390 dprintk(XENLOG_WARNING VTDPREFIX,
391 "RMRR address range not in reserved memory base = %"PRIx64" end = %"PRIx64"; " \
392 "iommu_inclusive_mapping=1 parameter may be needed.\n",
393 rmrr->base_address, rmrr->end_address);
394 }
395 #endif
397 rmrru = xmalloc(struct acpi_rmrr_unit);
398 if ( !rmrru )
399 return -ENOMEM;
400 memset(rmrru, 0, sizeof(struct acpi_rmrr_unit));
402 rmrru->base_address = rmrr->base_address;
403 rmrru->end_address = rmrr->end_address;
404 dev_scope_start = (void *)(rmrr + 1);
405 dev_scope_end = ((void *)rmrr) + header->length;
406 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
407 rmrru, RMRR_TYPE);
409 if ( ret || (rmrru->scope.devices_cnt == 0) )
410 xfree(rmrru);
411 else
412 acpi_register_rmrr_unit(rmrru);
413 return ret;
414 }
416 static int __init
417 acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
418 {
419 struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
420 struct acpi_atsr_unit *atsru;
421 int ret = 0;
422 static int all_ports;
423 void *dev_scope_start, *dev_scope_end;
425 atsru = xmalloc(struct acpi_atsr_unit);
426 if ( !atsru )
427 return -ENOMEM;
428 memset(atsru, 0, sizeof(struct acpi_atsr_unit));
430 atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */
431 if ( !atsru->all_ports )
432 {
433 dev_scope_start = (void *)(atsr + 1);
434 dev_scope_end = ((void *)atsr) + header->length;
435 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
436 atsru, ATSR_TYPE);
437 }
438 else
439 {
440 dprintk(XENLOG_INFO VTDPREFIX, "found ALL_PORTS\n");
441 /* Only allow one ALL_PORTS */
442 if ( all_ports )
443 {
444 dprintk(XENLOG_WARNING VTDPREFIX,
445 "Only one ALL_PORTS device scope is allowed\n");
446 ret = -EINVAL;
447 }
448 all_ports = 1;
449 }
451 if ( ret )
452 xfree(atsr);
453 else
454 acpi_register_atsr_unit(atsru);
455 return ret;
456 }
458 static int __init acpi_parse_dmar(struct acpi_table_header *table)
459 {
460 struct acpi_table_dmar *dmar;
461 struct acpi_dmar_entry_header *entry_header;
462 int ret = 0;
464 dmar = (struct acpi_table_dmar *)table;
466 if ( !dmar->width )
467 {
468 dprintk(XENLOG_WARNING VTDPREFIX, "Zero: Invalid DMAR width\n");
469 if ( force_iommu )
470 panic("acpi_parse_dmar: Invalid DMAR width,"
471 " crash Xen for security purpose!\n");
472 return -EINVAL;
473 }
475 dmar_host_address_width = dmar->width + 1;
476 dprintk(XENLOG_INFO VTDPREFIX, "Host address width %d\n",
477 dmar_host_address_width);
479 entry_header = (struct acpi_dmar_entry_header *)(dmar + 1);
480 while ( ((unsigned long)entry_header) <
481 (((unsigned long)dmar) + table->length) )
482 {
483 switch ( entry_header->type )
484 {
485 case ACPI_DMAR_DRHD:
486 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_DRHD\n");
487 ret = acpi_parse_one_drhd(entry_header);
488 break;
489 case ACPI_DMAR_RMRR:
490 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RMRR\n");
491 ret = acpi_parse_one_rmrr(entry_header);
492 break;
493 case ACPI_DMAR_ATSR:
494 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_ATSR\n");
495 ret = acpi_parse_one_atsr(entry_header);
496 break;
497 default:
498 dprintk(XENLOG_WARNING VTDPREFIX, "Unknown DMAR structure type\n");
499 ret = -EINVAL;
500 break;
501 }
502 if ( ret )
503 break;
505 entry_header = ((void *)entry_header + entry_header->length);
506 }
508 /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */
509 dmar->header.signature[0] = '\0';
511 if ( ret )
512 {
513 if ( force_iommu )
514 panic("acpi_parse_dmar: Failed to parse ACPI DMAR,"
515 " crash Xen for security purpose!\n");
516 else
517 {
518 printk(XENLOG_WARNING
519 "Failed to parse ACPI DMAR. Disabling VT-d.\n");
520 disable_all_dmar_units();
521 }
522 }
524 return ret;
525 }
527 #ifdef CONFIG_X86
528 #include <asm/tboot.h>
529 /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
530 /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
531 #define parse_dmar_table(h) tboot_parse_dmar_table(h)
532 #else
533 #define parse_dmar_table(h) acpi_table_parse(ACPI_SIG_DMAR, h)
534 #endif
536 int acpi_dmar_init(void)
537 {
538 int rc;
540 rc = -ENODEV;
541 if ( force_iommu )
542 iommu_enabled = 1;
544 if ( !iommu_enabled )
545 goto fail;
547 rc = parse_dmar_table(acpi_parse_dmar);
548 if ( rc )
549 goto fail;
551 rc = -ENODEV;
552 if ( list_empty(&acpi_drhd_units) )
553 goto fail;
555 printk("Intel VT-d DMAR tables have been parsed.\n");
557 return 0;
559 fail:
560 if ( force_iommu )
561 panic("acpi_dmar_init: acpi_dmar_init failed,"
562 " crash Xen for security purpose!\n");
564 vtd_enabled = 0;
565 return -ENODEV;
566 }