ia64/xen-unstable

view xen/drivers/passthrough/vtd/dmar.c @ 19673:f3bed18decfc

[VTD] laying the ground work for ATS

These changes lay the ground work for ATS enabling in Xen. It will be
followed by patch which enables PCI MMCFG which is needed for actual
enabling of ATS functionality.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri May 29 09:19:30 2009 +0100 (2009-05-29)
parents f02a528d2e56
children 01748ccc4da3
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 "found bridge: bdf = %x:%x.%x sec = %x sub = %x\n",
281 bus, path->dev, path->fn, sec_bus, sub_bus);
283 dmar_scope_add_buses(scope, acpi_scope->start_bus, acpi_scope->start_bus);
284 dmar_scope_add_buses(scope, sec_bus, sub_bus);
285 break;
287 case ACPI_DEV_MSI_HPET:
288 dprintk(XENLOG_INFO VTDPREFIX, "found MSI HPET: bdf = %x:%x.%x\n",
289 bus, path->dev, path->fn);
290 break;
292 case ACPI_DEV_ENDPOINT:
293 dprintk(XENLOG_INFO VTDPREFIX, "found endpoint: bdf = %x:%x.%x\n",
294 bus, path->dev, path->fn);
295 break;
297 case ACPI_DEV_IOAPIC:
298 dprintk(XENLOG_INFO VTDPREFIX, "found IOAPIC: bdf = %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 }