ia64/xen-unstable

view xen/drivers/passthrough/vtd/dmar.c @ 19810:aa472909b39c

vtd: IO NUMA support

This patch adds VT-d RHSA processing for IO NUMA support. The basic
idea is to parse ACPI RHSA structure to obtain VT-d HW to proximity
domain mapping. This mapping is then used when allocating pages for
Vt-d HW data structures.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 23 11:14:24 2009 +0100 (2009-06-23)
parents 931dbe86e5f3
children
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 #undef PREFIX
36 #define PREFIX VTDPREFIX "ACPI DMAR:"
37 #define DEBUG
39 #define MIN_SCOPE_LEN (sizeof(struct acpi_pci_path) + \
40 sizeof(struct acpi_dev_scope))
42 LIST_HEAD(acpi_drhd_units);
43 LIST_HEAD(acpi_rmrr_units);
44 LIST_HEAD(acpi_atsr_units);
45 LIST_HEAD(acpi_rhsa_units);
47 u8 dmar_host_address_width;
49 void dmar_scope_add_buses(struct dmar_scope *scope, u16 sec_bus, u16 sub_bus)
50 {
51 sub_bus &= 0xff;
52 if (sec_bus > sub_bus)
53 return;
55 while ( sec_bus <= sub_bus )
56 set_bit(sec_bus++, scope->buses);
57 }
59 void dmar_scope_remove_buses(struct dmar_scope *scope, u16 sec_bus, u16 sub_bus)
60 {
61 sub_bus &= 0xff;
62 if (sec_bus > sub_bus)
63 return;
65 while ( sec_bus <= sub_bus )
66 clear_bit(sec_bus++, scope->buses);
67 }
69 static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd)
70 {
71 /*
72 * add INCLUDE_ALL at the tail, so scan the list will find it at
73 * the very end.
74 */
75 if ( drhd->include_all )
76 list_add_tail(&drhd->list, &acpi_drhd_units);
77 else
78 list_add(&drhd->list, &acpi_drhd_units);
79 return 0;
80 }
82 static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr)
83 {
84 list_add(&rmrr->list, &acpi_rmrr_units);
85 return 0;
86 }
88 static void __init disable_all_dmar_units(void)
89 {
90 struct acpi_drhd_unit *drhd, *_drhd;
91 struct acpi_rmrr_unit *rmrr, *_rmrr;
92 struct acpi_atsr_unit *atsr, *_atsr;
94 list_for_each_entry_safe ( drhd, _drhd, &acpi_drhd_units, list )
95 {
96 list_del(&drhd->list);
97 xfree(drhd);
98 }
99 list_for_each_entry_safe ( rmrr, _rmrr, &acpi_rmrr_units, list )
100 {
101 list_del(&rmrr->list);
102 xfree(rmrr);
103 }
104 list_for_each_entry_safe ( atsr, _atsr, &acpi_atsr_units, list )
105 {
106 list_del(&atsr->list);
107 xfree(atsr);
108 }
109 }
111 static int acpi_ioapic_device_match(
112 struct list_head *ioapic_list, unsigned int apic_id)
113 {
114 struct acpi_ioapic_unit *ioapic;
115 list_for_each_entry( ioapic, ioapic_list, list ) {
116 if (ioapic->apic_id == apic_id)
117 return 1;
118 }
119 return 0;
120 }
122 struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id)
123 {
124 struct acpi_drhd_unit *drhd;
125 list_for_each_entry( drhd, &acpi_drhd_units, list )
126 if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) )
127 return drhd;
128 return NULL;
129 }
131 struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu)
132 {
133 struct acpi_drhd_unit *drhd;
135 if ( iommu == NULL )
136 return NULL;
138 list_for_each_entry( drhd, &acpi_drhd_units, list )
139 if ( drhd->iommu == iommu )
140 return drhd;
142 return NULL;
143 }
145 struct iommu * ioapic_to_iommu(unsigned int apic_id)
146 {
147 struct acpi_drhd_unit *drhd;
149 list_for_each_entry( drhd, &acpi_drhd_units, list )
150 if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) )
151 return drhd->iommu;
152 return NULL;
153 }
155 static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr)
156 {
157 /*
158 * add ALL_PORTS at the tail, so scan the list will find it at
159 * the very end.
160 */
161 if ( atsr->all_ports )
162 list_add_tail(&atsr->list, &acpi_atsr_units);
163 else
164 list_add(&atsr->list, &acpi_atsr_units);
165 return 0;
166 }
168 struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *pdev)
169 {
170 u8 bus, devfn;
171 struct acpi_drhd_unit *drhd;
172 struct acpi_drhd_unit *include_all = NULL;
173 int i;
175 if ( pdev == NULL )
176 return NULL;
178 if (pdev->info.is_extfn) {
179 bus = pdev->bus;
180 devfn = 0;
181 } else if (pdev->info.is_virtfn) {
182 bus = pdev->info.physfn.bus;
183 devfn = PCI_SLOT(pdev->info.physfn.devfn) ? 0 : pdev->info.physfn.devfn;
184 } else {
185 bus = pdev->bus;
186 devfn = pdev->devfn;
187 }
189 list_for_each_entry ( drhd, &acpi_drhd_units, list )
190 {
191 for (i = 0; i < drhd->scope.devices_cnt; i++)
192 if ( drhd->scope.devices[i] == PCI_BDF2(bus, devfn) )
193 return drhd;
195 if ( test_bit(bus, drhd->scope.buses) )
196 return drhd;
198 if ( drhd->include_all )
199 include_all = drhd;
200 }
201 return include_all;
202 }
204 struct acpi_atsr_unit * acpi_find_matched_atsr_unit(u8 bus, u8 devfn)
205 {
206 struct acpi_atsr_unit *atsr;
207 struct acpi_atsr_unit *all_ports = NULL;
209 list_for_each_entry ( atsr, &acpi_atsr_units, list )
210 {
211 if ( test_bit(bus, atsr->scope.buses) )
212 return atsr;
214 if ( atsr->all_ports )
215 all_ports = atsr;
216 }
217 return all_ports;
218 }
220 struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd)
221 {
222 struct acpi_rhsa_unit *rhsa;
224 if ( drhd == NULL )
225 return NULL;
227 list_for_each_entry ( rhsa, &acpi_rhsa_units, list )
228 {
229 if ( rhsa->address == drhd->address )
230 return rhsa;
231 }
232 return NULL;
233 }
235 /*
236 * Count number of devices in device scope. Do not include PCI sub
237 * hierarchies.
238 */
239 static int scope_device_count(void *start, void *end)
240 {
241 struct acpi_dev_scope *scope;
242 int count = 0;
244 while ( start < end )
245 {
246 scope = start;
247 if ( (scope->length < MIN_SCOPE_LEN) ||
248 (scope->dev_type >= ACPI_DEV_ENTRY_COUNT) )
249 {
250 dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope.\n");
251 return -EINVAL;
252 }
254 if ( scope->dev_type == ACPI_DEV_P2PBRIDGE ||
255 scope->dev_type == ACPI_DEV_ENDPOINT ||
256 scope->dev_type == ACPI_DEV_IOAPIC ||
257 scope->dev_type == ACPI_DEV_MSI_HPET )
258 count++;
260 start += scope->length;
261 }
263 return count;
264 }
267 static int __init acpi_parse_dev_scope(void *start, void *end,
268 void *acpi_entry, int type)
269 {
270 struct dmar_scope *scope = acpi_entry;
271 struct acpi_ioapic_unit *acpi_ioapic_unit;
272 struct acpi_dev_scope *acpi_scope;
273 u16 bus, sub_bus, sec_bus;
274 struct acpi_pci_path *path;
275 int depth, cnt, didx = 0;
277 if ( (cnt = scope_device_count(start, end)) < 0 )
278 return cnt;
280 scope->devices_cnt = cnt;
281 if ( cnt > 0 )
282 {
283 scope->devices = xmalloc_array(u16, cnt);
284 if ( !scope->devices )
285 return -ENOMEM;
286 memset(scope->devices, 0, sizeof(u16) * cnt);
287 }
289 while ( start < end )
290 {
291 acpi_scope = start;
292 path = (struct acpi_pci_path *)(acpi_scope + 1);
293 depth = (acpi_scope->length - sizeof(struct acpi_dev_scope))
294 / sizeof(struct acpi_pci_path);
295 bus = acpi_scope->start_bus;
297 while ( --depth > 0 )
298 {
299 bus = pci_conf_read8(bus, path->dev, path->fn, PCI_SECONDARY_BUS);
300 path++;
301 }
303 switch ( acpi_scope->dev_type )
304 {
305 case ACPI_DEV_P2PBRIDGE:
306 sec_bus = pci_conf_read8(
307 bus, path->dev, path->fn, PCI_SECONDARY_BUS);
308 sub_bus = pci_conf_read8(
309 bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
310 dprintk(XENLOG_INFO VTDPREFIX,
311 "bridge: %x:%x.%x start = %x sec = %x sub = %x\n",
312 bus, path->dev, path->fn,
313 acpi_scope->start_bus, sec_bus, sub_bus);
315 dmar_scope_add_buses(scope, sec_bus, sub_bus);
316 break;
318 case ACPI_DEV_MSI_HPET:
319 dprintk(XENLOG_INFO VTDPREFIX, "MSI HPET: %x:%x.%x\n",
320 bus, path->dev, path->fn);
321 break;
323 case ACPI_DEV_ENDPOINT:
324 dprintk(XENLOG_INFO VTDPREFIX, "endpoint: %x:%x.%x\n",
325 bus, path->dev, path->fn);
326 break;
328 case ACPI_DEV_IOAPIC:
329 dprintk(XENLOG_INFO VTDPREFIX, "IOAPIC: %x:%x.%x\n",
330 bus, path->dev, path->fn);
332 if ( type == DMAR_TYPE )
333 {
334 struct acpi_drhd_unit *drhd = acpi_entry;
335 acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
336 if ( !acpi_ioapic_unit )
337 return -ENOMEM;
338 acpi_ioapic_unit->apic_id = acpi_scope->enum_id;
339 acpi_ioapic_unit->ioapic.bdf.bus = bus;
340 acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
341 acpi_ioapic_unit->ioapic.bdf.func = path->fn;
342 list_add(&acpi_ioapic_unit->list, &drhd->ioapic_list);
343 }
345 break;
346 }
347 scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
348 start += acpi_scope->length;
349 }
351 return 0;
352 }
354 static int __init
355 acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
356 {
357 struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
358 void *dev_scope_start, *dev_scope_end;
359 struct acpi_drhd_unit *dmaru;
360 int ret = 0;
361 static int include_all = 0;
363 dmaru = xmalloc(struct acpi_drhd_unit);
364 if ( !dmaru )
365 return -ENOMEM;
366 memset(dmaru, 0, sizeof(struct acpi_drhd_unit));
368 dmaru->address = drhd->address;
369 dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */
370 INIT_LIST_HEAD(&dmaru->ioapic_list);
371 dprintk(XENLOG_INFO VTDPREFIX, "dmaru->address = %"PRIx64"\n",
372 dmaru->address);
374 dev_scope_start = (void *)(drhd + 1);
375 dev_scope_end = ((void *)drhd) + header->length;
376 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
377 dmaru, DMAR_TYPE);
379 if ( dmaru->include_all )
380 {
381 dprintk(XENLOG_INFO VTDPREFIX, "found INCLUDE_ALL\n");
382 /* Only allow one INCLUDE_ALL */
383 if ( include_all )
384 {
385 dprintk(XENLOG_WARNING VTDPREFIX,
386 "Only one INCLUDE_ALL device scope is allowed\n");
387 ret = -EINVAL;
388 }
389 include_all = 1;
390 }
392 if ( ret )
393 xfree(dmaru);
394 else
395 acpi_register_drhd_unit(dmaru);
396 return ret;
397 }
399 static int __init
400 acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
401 {
402 struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header;
403 struct acpi_rmrr_unit *rmrru;
404 void *dev_scope_start, *dev_scope_end;
405 int ret = 0;
407 if ( rmrr->base_address >= rmrr->end_address )
408 {
409 dprintk(XENLOG_ERR VTDPREFIX,
410 "RMRR error: base_addr %"PRIx64" end_address %"PRIx64"\n",
411 rmrr->base_address, rmrr->end_address);
412 return -EFAULT;
413 }
415 #ifdef CONFIG_X86
416 /* This check is here simply to detect when RMRR values are not properly represented in the
417 system memory map and inform the user */
418 if ( (!page_is_ram_type(paddr_to_pfn(rmrr->base_address), RAM_TYPE_RESERVED))||
419 (!page_is_ram_type(paddr_to_pfn(rmrr->end_address) - 1, RAM_TYPE_RESERVED)) )
420 {
421 dprintk(XENLOG_WARNING VTDPREFIX,
422 "RMRR address range not in reserved memory base = %"PRIx64" end = %"PRIx64"; " \
423 "iommu_inclusive_mapping=1 parameter may be needed.\n",
424 rmrr->base_address, rmrr->end_address);
425 }
426 #endif
428 rmrru = xmalloc(struct acpi_rmrr_unit);
429 if ( !rmrru )
430 return -ENOMEM;
431 memset(rmrru, 0, sizeof(struct acpi_rmrr_unit));
433 rmrru->base_address = rmrr->base_address;
434 rmrru->end_address = rmrr->end_address;
435 dev_scope_start = (void *)(rmrr + 1);
436 dev_scope_end = ((void *)rmrr) + header->length;
437 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
438 rmrru, RMRR_TYPE);
440 if ( ret || (rmrru->scope.devices_cnt == 0) )
441 xfree(rmrru);
442 else
443 acpi_register_rmrr_unit(rmrru);
444 return ret;
445 }
447 static int __init
448 acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
449 {
450 struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
451 struct acpi_atsr_unit *atsru;
452 int ret = 0;
453 static int all_ports;
454 void *dev_scope_start, *dev_scope_end;
456 atsru = xmalloc(struct acpi_atsr_unit);
457 if ( !atsru )
458 return -ENOMEM;
459 memset(atsru, 0, sizeof(struct acpi_atsr_unit));
461 atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */
462 if ( !atsru->all_ports )
463 {
464 dev_scope_start = (void *)(atsr + 1);
465 dev_scope_end = ((void *)atsr) + header->length;
466 ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
467 atsru, ATSR_TYPE);
468 }
469 else
470 {
471 dprintk(XENLOG_INFO VTDPREFIX, "found ALL_PORTS\n");
472 /* Only allow one ALL_PORTS */
473 if ( all_ports )
474 {
475 dprintk(XENLOG_WARNING VTDPREFIX,
476 "Only one ALL_PORTS device scope is allowed\n");
477 ret = -EINVAL;
478 }
479 all_ports = 1;
480 }
482 if ( ret )
483 xfree(atsr);
484 else
485 acpi_register_atsr_unit(atsru);
486 return ret;
487 }
489 static int __init
490 acpi_parse_one_rhsa(struct acpi_dmar_entry_header *header)
491 {
492 struct acpi_table_rhsa *rhsa = (struct acpi_table_rhsa *)header;
493 struct acpi_rhsa_unit *rhsau;
494 int ret = 0;
496 rhsau = xmalloc(struct acpi_rhsa_unit);
497 if ( !rhsau )
498 return -ENOMEM;
499 memset(rhsau, 0, sizeof(struct acpi_rhsa_unit));
501 rhsau->address = rhsa->address;
502 rhsau->domain = rhsa->domain;
503 list_add_tail(&rhsau->list, &acpi_rhsa_units);
505 return ret;
506 }
508 static int __init acpi_parse_dmar(struct acpi_table_header *table)
509 {
510 struct acpi_table_dmar *dmar;
511 struct acpi_dmar_entry_header *entry_header;
512 int ret = 0;
514 dmar = (struct acpi_table_dmar *)table;
516 if ( !dmar->width )
517 {
518 dprintk(XENLOG_WARNING VTDPREFIX, "Zero: Invalid DMAR width\n");
519 if ( force_iommu )
520 panic("acpi_parse_dmar: Invalid DMAR width,"
521 " crash Xen for security purpose!\n");
522 return -EINVAL;
523 }
525 dmar_host_address_width = dmar->width + 1;
526 dprintk(XENLOG_INFO VTDPREFIX, "Host address width %d\n",
527 dmar_host_address_width);
529 entry_header = (struct acpi_dmar_entry_header *)(dmar + 1);
530 while ( ((unsigned long)entry_header) <
531 (((unsigned long)dmar) + table->length) )
532 {
533 switch ( entry_header->type )
534 {
535 case ACPI_DMAR_DRHD:
536 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_DRHD\n");
537 ret = acpi_parse_one_drhd(entry_header);
538 break;
539 case ACPI_DMAR_RMRR:
540 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RMRR\n");
541 ret = acpi_parse_one_rmrr(entry_header);
542 break;
543 case ACPI_DMAR_ATSR:
544 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_ATSR\n");
545 ret = acpi_parse_one_atsr(entry_header);
546 break;
547 case ACPI_DMAR_RHSA:
548 dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RHSA\n");
549 ret = acpi_parse_one_rhsa(entry_header);
550 break;
551 default:
552 dprintk(XENLOG_WARNING VTDPREFIX, "Unknown DMAR structure type\n");
553 ret = -EINVAL;
554 break;
555 }
556 if ( ret )
557 break;
559 entry_header = ((void *)entry_header + entry_header->length);
560 }
562 /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */
563 dmar->header.signature[0] = '\0';
565 if ( ret )
566 {
567 if ( force_iommu )
568 panic("acpi_parse_dmar: Failed to parse ACPI DMAR,"
569 " crash Xen for security purpose!\n");
570 else
571 {
572 printk(XENLOG_WARNING
573 "Failed to parse ACPI DMAR. Disabling VT-d.\n");
574 disable_all_dmar_units();
575 }
576 }
578 return ret;
579 }
581 #ifdef CONFIG_X86
582 #include <asm/tboot.h>
583 /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
584 /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
585 #define parse_dmar_table(h) tboot_parse_dmar_table(h)
586 #else
587 #define parse_dmar_table(h) acpi_table_parse(ACPI_SIG_DMAR, h)
588 #endif
590 int acpi_dmar_init(void)
591 {
592 int rc;
594 rc = -ENODEV;
595 if ( force_iommu )
596 iommu_enabled = 1;
598 if ( !iommu_enabled )
599 goto fail;
601 rc = parse_dmar_table(acpi_parse_dmar);
602 if ( rc )
603 goto fail;
605 rc = -ENODEV;
606 if ( list_empty(&acpi_drhd_units) )
607 goto fail;
609 printk("Intel VT-d DMAR tables have been parsed.\n");
611 return 0;
613 fail:
614 if ( force_iommu )
615 panic("acpi_dmar_init: acpi_dmar_init failed,"
616 " crash Xen for security purpose!\n");
618 iommu_enabled = 0;
619 return -ENODEV;
620 }