ia64/xen-unstable

view xen/drivers/passthrough/vtd/dmar.c @ 19402:f02a528d2e56

Xen: use proper device ID to search VT-d unit for ARI and SR-IOV device

PCIe Alternative Routing-ID Interpretation (ARI) ECN defines the Extended
Function -- a function whose function number is greater than 7 within an
ARI Device. Intel VT-d spec 1.2 section 8.3.2 specifies that the Extended
Function is under the scope of the same remapping unit as the traditional
function. The hypervisor needs to know if a function is Extended
Function so it can find proper DMAR for it.

And section 8.3.3 specifies that the SR-IOV Virtual Function is under the
scope of the same remapping unit as the Physical Function. The hypervisor
also needs to know if a function is the Virtual Function and which
Physical Function it's associated with for same reason.

Signed-off-by: Yu Zhao <yu.zhao@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Mar 19 10:20:11 2009 +0000 (2009-03-19)
parents 6d65dc14d21b
children f3bed18decfc
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 *found = NULL, *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 found = drhd;
182 if ( drhd->include_all )
183 include_all = drhd;
184 }
186 return found ? found : include_all;
187 }
189 struct acpi_atsr_unit * acpi_find_matched_atsr_unit(u8 bus, u8 devfn)
190 {
191 struct acpi_atsr_unit *atsr;
192 struct acpi_atsr_unit *found = NULL, *include_all = NULL;
193 int i;
195 list_for_each_entry ( atsr, &acpi_atsr_units, list )
196 {
197 for (i = 0; i < atsr->scope.devices_cnt; i++)
198 if ( atsr->scope.devices[i] == PCI_BDF2(bus, devfn) )
199 return atsr;
201 if ( test_bit(bus, atsr->scope.buses) )
202 found = atsr;
204 if ( atsr->all_ports )
205 include_all = atsr;
206 }
208 return found ? found : include_all;
209 }
211 /*
212 * Count number of devices in device scope. Do not include PCI sub
213 * hierarchies.
214 */
215 static int scope_device_count(void *start, void *end)
216 {
217 struct acpi_dev_scope *scope;
218 int count = 0;
220 while ( start < end )
221 {
222 scope = start;
223 if ( (scope->length < MIN_SCOPE_LEN) ||
224 (scope->dev_type >= ACPI_DEV_ENTRY_COUNT) )
225 {
226 dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope.\n");
227 return -EINVAL;
228 }
230 if ( scope->dev_type == ACPI_DEV_ENDPOINT ||
231 scope->dev_type == ACPI_DEV_IOAPIC ||
232 scope->dev_type == ACPI_DEV_MSI_HPET )
233 count++;
235 start += scope->length;
236 }
238 return count;
239 }
242 static int __init acpi_parse_dev_scope(void *start, void *end,
243 void *acpi_entry, int type)
244 {
245 struct dmar_scope *scope = acpi_entry;
246 struct acpi_ioapic_unit *acpi_ioapic_unit;
247 struct acpi_dev_scope *acpi_scope;
248 u16 bus, sub_bus, sec_bus;
249 struct acpi_pci_path *path;
250 int depth, cnt, didx = 0;
252 if ( (cnt = scope_device_count(start, end)) < 0 )
253 return cnt;
255 scope->devices_cnt = cnt;
256 if ( cnt > 0 )
257 {
258 scope->devices = xmalloc_array(u16, cnt);
259 if ( !scope->devices )
260 return -ENOMEM;
261 memset(scope->devices, 0, sizeof(u16) * cnt);
262 }
264 while ( start < end )
265 {
266 acpi_scope = start;
267 path = (struct acpi_pci_path *)(acpi_scope + 1);
268 depth = (acpi_scope->length - sizeof(struct acpi_dev_scope))
269 / sizeof(struct acpi_pci_path);
270 bus = acpi_scope->start_bus;
272 while ( --depth > 0 )
273 {
274 bus = pci_conf_read8(bus, path->dev, path->fn, PCI_SECONDARY_BUS);
275 path++;
276 }
278 switch ( acpi_scope->dev_type )
279 {
280 case ACPI_DEV_P2PBRIDGE:
281 sec_bus = pci_conf_read8(
282 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
283 sub_bus = pci_conf_read8(
284 bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
285 dprintk(XENLOG_INFO VTDPREFIX,
286 "found bridge: bdf = %x:%x.%x sec = %x sub = %x\n",
287 bus, path->dev, path->fn, sec_bus, sub_bus);
289 dmar_scope_add_buses(scope, sec_bus, sub_bus);
290 break;
292 case ACPI_DEV_MSI_HPET:
293 dprintk(XENLOG_INFO VTDPREFIX, "found MSI HPET: bdf = %x:%x.%x\n",
294 bus, path->dev, path->fn);
295 scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
296 break;
298 case ACPI_DEV_ENDPOINT:
299 dprintk(XENLOG_INFO VTDPREFIX, "found endpoint: bdf = %x:%x.%x\n",
300 bus, path->dev, path->fn);
301 scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
302 break;
304 case ACPI_DEV_IOAPIC:
305 dprintk(XENLOG_INFO VTDPREFIX, "found IOAPIC: bdf = %x:%x.%x\n",
306 bus, path->dev, path->fn);
308 if ( type == DMAR_TYPE )
309 {
310 struct acpi_drhd_unit *drhd = acpi_entry;
311 acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
312 if ( !acpi_ioapic_unit )
313 return -ENOMEM;
314 acpi_ioapic_unit->apic_id = acpi_scope->enum_id;
315 acpi_ioapic_unit->ioapic.bdf.bus = bus;
316 acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
317 acpi_ioapic_unit->ioapic.bdf.func = path->fn;
318 list_add(&acpi_ioapic_unit->list, &drhd->ioapic_list);
319 }
321 scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
322 break;
323 }
325 start += acpi_scope->length;
326 }
328 return 0;
329 }
331 static int __init
332 acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
333 {
334 struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
335 void *dev_scope_start, *dev_scope_end;
336 struct acpi_drhd_unit *dmaru;
337 int ret = 0;
338 static int include_all = 0;
340 dmaru = xmalloc(struct acpi_drhd_unit);
341 if ( !dmaru )
342 return -ENOMEM;
343 memset(dmaru, 0, sizeof(struct acpi_drhd_unit));
345 dmaru->address = drhd->address;
346 dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */
347 INIT_LIST_HEAD(&dmaru->ioapic_list);
348 dprintk(XENLOG_INFO VTDPREFIX, "dmaru->address = %"PRIx64"\n",
349 dmaru->address);
351 dev_scope_start = (void *)(drhd + 1);
352 dev_scope_end = ((void *)drhd) + header->length;
353 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
354 dmaru, DMAR_TYPE);
356 if ( dmaru->include_all )
357 {
358 dprintk(XENLOG_INFO VTDPREFIX, "found INCLUDE_ALL\n");
359 /* Only allow one INCLUDE_ALL */
360 if ( include_all )
361 {
362 dprintk(XENLOG_WARNING VTDPREFIX,
363 "Only one INCLUDE_ALL device scope is allowed\n");
364 ret = -EINVAL;
365 }
366 include_all = 1;
367 }
369 if ( ret )
370 xfree(dmaru);
371 else
372 acpi_register_drhd_unit(dmaru);
373 return ret;
374 }
376 static int __init
377 acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
378 {
379 struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header;
380 struct acpi_rmrr_unit *rmrru;
381 void *dev_scope_start, *dev_scope_end;
382 int ret = 0;
384 if ( rmrr->base_address >= rmrr->end_address )
385 {
386 dprintk(XENLOG_ERR VTDPREFIX,
387 "RMRR error: base_addr %"PRIx64" end_address %"PRIx64"\n",
388 rmrr->base_address, rmrr->end_address);
389 return -EFAULT;
390 }
392 #ifdef CONFIG_X86
393 /* This check is here simply to detect when RMRR values are not properly represented in the
394 system memory map and inform the user */
395 if ( (!page_is_ram_type(paddr_to_pfn(rmrr->base_address), RAM_TYPE_RESERVED))||
396 (!page_is_ram_type(paddr_to_pfn(rmrr->end_address) - 1, RAM_TYPE_RESERVED)) )
397 {
398 dprintk(XENLOG_WARNING VTDPREFIX,
399 "RMRR address range not in reserved memory base = %"PRIx64" end = %"PRIx64"; " \
400 "iommu_inclusive_mapping=1 parameter may be needed.\n",
401 rmrr->base_address, rmrr->end_address);
402 }
403 #endif
405 rmrru = xmalloc(struct acpi_rmrr_unit);
406 if ( !rmrru )
407 return -ENOMEM;
408 memset(rmrru, 0, sizeof(struct acpi_rmrr_unit));
410 rmrru->base_address = rmrr->base_address;
411 rmrru->end_address = rmrr->end_address;
412 dev_scope_start = (void *)(rmrr + 1);
413 dev_scope_end = ((void *)rmrr) + header->length;
414 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
415 rmrru, RMRR_TYPE);
417 if ( ret || (rmrru->scope.devices_cnt == 0) )
418 xfree(rmrru);
419 else
420 acpi_register_rmrr_unit(rmrru);
421 return ret;
422 }
424 static int __init
425 acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
426 {
427 struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
428 struct acpi_atsr_unit *atsru;
429 int ret = 0;
430 static int all_ports;
431 void *dev_scope_start, *dev_scope_end;
433 atsru = xmalloc(struct acpi_atsr_unit);
434 if ( !atsru )
435 return -ENOMEM;
436 memset(atsru, 0, sizeof(struct acpi_atsr_unit));
438 atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */
439 if ( !atsru->all_ports )
440 {
441 dev_scope_start = (void *)(atsr + 1);
442 dev_scope_end = ((void *)atsr) + header->length;
443 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
444 atsru, ATSR_TYPE);
445 }
446 else
447 {
448 dprintk(XENLOG_INFO VTDPREFIX, "found ALL_PORTS\n");
449 /* Only allow one ALL_PORTS */
450 if ( all_ports )
451 {
452 dprintk(XENLOG_WARNING VTDPREFIX,
453 "Only one ALL_PORTS device scope is allowed\n");
454 ret = -EINVAL;
455 }
456 all_ports = 1;
457 }
459 if ( ret )
460 xfree(atsr);
461 else
462 acpi_register_atsr_unit(atsru);
463 return ret;
464 }
466 static int __init acpi_parse_dmar(struct acpi_table_header *table)
467 {
468 struct acpi_table_dmar *dmar;
469 struct acpi_dmar_entry_header *entry_header;
470 int ret = 0;
472 dmar = (struct acpi_table_dmar *)table;
474 if ( !dmar->width )
475 {
476 dprintk(XENLOG_WARNING VTDPREFIX, "Zero: Invalid DMAR width\n");
477 if ( force_iommu )
478 panic("acpi_parse_dmar: Invalid DMAR width,"
479 " crash Xen for security purpose!\n");
480 return -EINVAL;
481 }
483 dmar_host_address_width = dmar->width + 1;
484 dprintk(XENLOG_INFO VTDPREFIX, "Host address width %d\n",
485 dmar_host_address_width);
487 entry_header = (struct acpi_dmar_entry_header *)(dmar + 1);
488 while ( ((unsigned long)entry_header) <
489 (((unsigned long)dmar) + table->length) )
490 {
491 switch ( entry_header->type )
492 {
493 case ACPI_DMAR_DRHD:
494 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_DRHD\n");
495 ret = acpi_parse_one_drhd(entry_header);
496 break;
497 case ACPI_DMAR_RMRR:
498 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RMRR\n");
499 ret = acpi_parse_one_rmrr(entry_header);
500 break;
501 case ACPI_DMAR_ATSR:
502 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_ATSR\n");
503 ret = acpi_parse_one_atsr(entry_header);
504 break;
505 default:
506 dprintk(XENLOG_WARNING VTDPREFIX, "Unknown DMAR structure type\n");
507 ret = -EINVAL;
508 break;
509 }
510 if ( ret )
511 break;
513 entry_header = ((void *)entry_header + entry_header->length);
514 }
516 /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */
517 dmar->header.signature[0] = '\0';
519 if ( ret )
520 {
521 if ( force_iommu )
522 panic("acpi_parse_dmar: Failed to parse ACPI DMAR,"
523 " crash Xen for security purpose!\n");
524 else
525 {
526 printk(XENLOG_WARNING
527 "Failed to parse ACPI DMAR. Disabling VT-d.\n");
528 disable_all_dmar_units();
529 }
530 }
532 return ret;
533 }
535 #ifdef CONFIG_X86
536 #include <asm/tboot.h>
537 /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
538 /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
539 #define parse_dmar_table(h) tboot_parse_dmar_table(h)
540 #else
541 #define parse_dmar_table(h) acpi_table_parse(ACPI_SIG_DMAR, h)
542 #endif
544 int acpi_dmar_init(void)
545 {
546 int rc;
548 rc = -ENODEV;
549 if ( force_iommu )
550 iommu_enabled = 1;
552 if ( !iommu_enabled )
553 goto fail;
555 rc = parse_dmar_table(acpi_parse_dmar);
556 if ( rc )
557 goto fail;
559 rc = -ENODEV;
560 if ( list_empty(&acpi_drhd_units) )
561 goto fail;
563 printk("Intel VT-d DMAR tables have been parsed.\n");
565 return 0;
567 fail:
568 if ( force_iommu )
569 panic("acpi_dmar_init: acpi_dmar_init failed,"
570 " crash Xen for security purpose!\n");
572 vtd_enabled = 0;
573 return -ENODEV;
574 }