ia64/xen-unstable

view xen/arch/x86/hvm/vmx/vtd/dmar.c @ 16063:4881f984e06e

This patch ensures xen vmm has exclusive access of vt-d hw by zapping
vt-d "DMAR" ACPI table signature after xen has finished using it.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir@xensource.com>
date Thu Oct 04 09:40:31 2007 +0100 (2007-10-04)
parents 4fdcea9881b2
children e733e6b73d56
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) + sizeof(struct acpi_dev_scope))
43 LIST_HEAD(acpi_drhd_units);
44 LIST_HEAD(acpi_rmrr_units);
45 LIST_HEAD(acpi_atsr_units);
46 LIST_HEAD(acpi_ioapic_units);
48 u8 dmar_host_address_width;
50 static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd)
51 {
52 /*
53 * add INCLUDE_ALL at the tail, so scan the list will find it at
54 * the very end.
55 */
56 if (drhd->include_all)
57 list_add_tail(&drhd->list, &acpi_drhd_units);
58 else
59 list_add(&drhd->list, &acpi_drhd_units);
60 return 0;
61 }
63 static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr)
64 {
65 list_add(&rmrr->list, &acpi_rmrr_units);
66 return 0;
67 }
69 static int acpi_pci_device_match(struct pci_dev *devices, int cnt,
70 struct pci_dev *dev)
71 {
72 int i;
74 for (i = 0; i < cnt; i++) {
75 if ((dev->bus == devices->bus) &&
76 (dev->devfn == devices->devfn))
77 return 1;
78 devices++;
79 }
80 return 0;
81 }
83 static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr)
84 {
85 /*
86 * add ALL_PORTS at the tail, so scan the list will find it at
87 * the very end.
88 */
89 if (atsr->all_ports)
90 list_add_tail(&atsr->list, &acpi_atsr_units);
91 else
92 list_add(&atsr->list, &acpi_atsr_units);
93 return 0;
94 }
96 struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev)
97 {
98 struct acpi_drhd_unit *drhd;
99 struct acpi_drhd_unit *include_all_drhd;
101 include_all_drhd = NULL;
102 list_for_each_entry(drhd, &acpi_drhd_units, list) {
103 if (drhd->include_all)
104 include_all_drhd = drhd;
105 if (acpi_pci_device_match(drhd->devices,
106 drhd->devices_cnt, dev))
107 {
108 gdprintk(XENLOG_INFO VTDPREFIX,
109 "acpi_find_matched_drhd_unit: drhd->address = %lx\n",
110 drhd->address);
111 return drhd;
112 }
113 }
115 if (include_all_drhd) {
116 gdprintk(XENLOG_INFO VTDPREFIX,
117 "acpi_find_matched_drhd_unit:include_all_drhd->addr = %lx\n",
118 include_all_drhd->address);
119 return include_all_drhd;;
120 }
122 return(NULL);
123 }
125 struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev)
126 {
127 struct acpi_rmrr_unit *rmrr;
129 list_for_each_entry(rmrr, &acpi_rmrr_units, list) {
130 if (acpi_pci_device_match(rmrr->devices,
131 rmrr->devices_cnt, dev))
132 goto out;
133 }
134 rmrr = NULL;
135 out:
136 return rmrr;
137 }
139 struct acpi_atsr_unit * acpi_find_matched_atsr_unit(struct pci_dev *dev)
140 {
141 struct acpi_atsr_unit *atsru;
142 struct acpi_atsr_unit *all_ports_atsru;
144 all_ports_atsru = NULL;
145 list_for_each_entry(atsru, &acpi_atsr_units, list) {
146 if (atsru->all_ports)
147 all_ports_atsru = atsru;
148 if (acpi_pci_device_match(atsru->devices, atsru->devices_cnt, dev))
149 return atsru;
150 }
151 if (all_ports_atsru) {
152 gdprintk(XENLOG_INFO VTDPREFIX,
153 "acpi_find_matched_atsr_unit: all_ports_atsru\n");
154 return all_ports_atsru;;
155 }
156 return(NULL);
157 }
159 static int __init acpi_parse_dev_scope(void *start, void *end, int *cnt,
160 struct pci_dev **devices)
161 {
162 struct acpi_dev_scope *scope;
163 u8 bus, sub_bus, sec_bus;
164 struct acpi_pci_path *path;
165 struct acpi_ioapic_unit *acpi_ioapic_unit = NULL;
166 int count, dev_count=0;
167 struct pci_dev *pdev;
168 u8 dev, func;
169 u32 l;
170 void *tmp;
172 *cnt = 0;
173 tmp = start;
174 while (start < end) {
175 scope = start;
176 if (scope->length < MIN_SCOPE_LEN ||
177 (scope->dev_type != ACPI_DEV_ENDPOINT &&
178 scope->dev_type != ACPI_DEV_P2PBRIDGE)) {
179 printk(KERN_WARNING PREFIX "Invalid device scope\n");
180 return -EINVAL;
181 }
182 (*cnt)++;
183 start += scope->length;
184 }
186 start = tmp;
187 while (start < end) {
188 scope = start;
189 path = (struct acpi_pci_path *)(scope + 1);
190 count = (scope->length - sizeof(struct acpi_dev_scope))
191 /sizeof(struct acpi_pci_path);
192 bus = scope->start_bus;
194 while (--count) {
195 bus = read_pci_config_byte(bus, path->dev,
196 path->fn, PCI_SECONDARY_BUS);
197 path++;
198 }
200 if (scope->dev_type == ACPI_DEV_ENDPOINT) {
201 printk(KERN_WARNING PREFIX
202 "found endpoint: bdf = %x:%x:%x\n", bus, path->dev, path->fn);
203 dev_count++;
204 } else if (scope->dev_type == ACPI_DEV_P2PBRIDGE) {
205 printk(KERN_WARNING PREFIX
206 "found bridge: bdf = %x:%x:%x\n", bus, path->dev, path->fn);
208 sec_bus = read_pci_config_byte(bus, path->dev,
209 path->fn, PCI_SECONDARY_BUS);
210 sub_bus = read_pci_config_byte(bus, path->dev,
211 path->fn, PCI_SUBORDINATE_BUS);
212 while (sec_bus <= sub_bus) {
213 for (dev = 0; dev < 32; dev++) {
214 for (func = 0; func < 8; func++) {
215 l = read_pci_config(sec_bus, dev, func, PCI_VENDOR_ID);
217 /* some broken boards return 0 or ~0 if a slot is empty: */
218 if (l == 0xffffffff || l == 0x00000000 ||
219 l == 0x0000ffff || l == 0xffff0000)
220 break;
221 dev_count++;
222 }
223 }
224 sec_bus++;
225 }
226 } else if (scope->dev_type == ACPI_DEV_IOAPIC) {
227 printk(KERN_WARNING PREFIX
228 "found IOAPIC: bdf = %x:%x:%x\n", bus, path->dev, path->fn);
229 dev_count++;
230 } else {
231 printk(KERN_WARNING PREFIX
232 "found MSI HPET: bdf = %x:%x:%x\n", bus, path->dev, path->fn);
233 dev_count++;
234 }
236 start += scope->length;
237 }
239 *cnt = dev_count;
240 *devices = xmalloc_array(struct pci_dev, *cnt);
241 if (!*devices)
242 return -ENOMEM;
243 memset(*devices, 0, sizeof(struct pci_dev) * (*cnt));
245 pdev = *devices;
246 start = tmp;
247 while (start < end) {
248 scope = start;
249 path = (struct acpi_pci_path *)(scope + 1);
250 count = (scope->length - sizeof(struct acpi_dev_scope))
251 /sizeof(struct acpi_pci_path);
252 bus = scope->start_bus;
254 while (--count) {
255 bus = read_pci_config_byte(bus, path->dev, path->fn, PCI_SECONDARY_BUS);
256 path++;
257 }
259 if (scope->dev_type == ACPI_DEV_ENDPOINT) {
260 printk(KERN_WARNING PREFIX
261 "found endpoint: bdf = %x:%x:%x\n", bus, path->dev, path->fn);
263 pdev->bus = bus;
264 pdev->devfn = PCI_DEVFN(path->dev, path->fn);
265 pdev++;
266 } else if (scope->dev_type == ACPI_DEV_P2PBRIDGE) {
267 printk(KERN_WARNING PREFIX
268 "found bridge: bus = %x dev = %x func = %x\n", bus, path->dev, path->fn);
270 sec_bus = read_pci_config_byte(bus, path->dev, path->fn, PCI_SECONDARY_BUS);
271 sub_bus = read_pci_config_byte(bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
273 while (sec_bus <= sub_bus) {
274 for (dev = 0; dev < 32; dev++) {
275 for (func = 0; func < 8; func++) {
276 l = read_pci_config(sec_bus, dev, func, PCI_VENDOR_ID);
278 /* some broken boards return 0 or ~0 if a slot is empty: */
279 if (l == 0xffffffff || l == 0x00000000 ||
280 l == 0x0000ffff || l == 0xffff0000)
281 break;
283 pdev->bus = sec_bus;
284 pdev->devfn = PCI_DEVFN(dev, func);
285 pdev++;
286 }
287 }
288 sec_bus++;
289 }
290 } else if (scope->dev_type == ACPI_DEV_IOAPIC) {
291 acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
292 acpi_ioapic_unit->apic_id = scope->enum_id;
293 acpi_ioapic_unit->ioapic.bdf.bus = bus;
294 acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
295 acpi_ioapic_unit->ioapic.bdf.func = path->fn;
296 list_add(&acpi_ioapic_unit->list, &acpi_ioapic_units);
297 printk(KERN_WARNING PREFIX
298 "found IOAPIC: bus = %x dev = %x func = %x\n", bus, path->dev, path->fn);
299 } else {
300 printk(KERN_WARNING PREFIX
301 "found MSI HPET: bus = %x dev = %x func = %x\n", bus, path->dev, path->fn);
302 }
304 start += scope->length;
305 }
307 return 0;
308 }
310 static int __init
311 acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
312 {
313 struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
314 struct acpi_drhd_unit *dmaru;
315 int ret = 0;
316 static int include_all;
318 dmaru = xmalloc(struct acpi_drhd_unit);
319 if (!dmaru)
320 return -ENOMEM;
321 memset(dmaru, 0, sizeof(struct acpi_drhd_unit));
323 dmaru->address = drhd->address;
324 dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */
325 printk(KERN_WARNING PREFIX "dmaru->address = %lx\n", dmaru->address);
327 if (!dmaru->include_all) {
328 ret = acpi_parse_dev_scope((void *)(drhd + 1),
329 ((void *)drhd) + header->length,
330 &dmaru->devices_cnt, &dmaru->devices);
331 }
332 else {
333 printk(KERN_WARNING PREFIX "found INCLUDE_ALL\n");
334 /* Only allow one INCLUDE_ALL */
335 if (include_all) {
336 printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL "
337 "device scope is allowed\n");
338 ret = -EINVAL;
339 }
340 include_all = 1;
341 }
343 if (ret)
344 xfree(dmaru);
345 else
346 acpi_register_drhd_unit(dmaru);
347 return ret;
348 }
350 static int __init
351 acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header)
352 {
353 struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header;
354 struct acpi_rmrr_unit *rmrru;
355 int ret = 0;
357 rmrru = xmalloc(struct acpi_rmrr_unit);
358 if (!rmrru)
359 return -ENOMEM;
360 memset(rmrru, 0, sizeof(struct acpi_rmrr_unit));
362 #ifdef VTD_DEBUG
363 gdprintk(XENLOG_INFO VTDPREFIX,
364 "acpi_parse_one_rmrr: base = %lx end = %lx\n",
365 rmrr->base_address, rmrr->end_address);
366 #endif
368 rmrru->base_address = rmrr->base_address;
369 rmrru->end_address = rmrr->end_address;
370 ret = acpi_parse_dev_scope((void *)(rmrr + 1),
371 ((void*)rmrr) + header->length,
372 &rmrru->devices_cnt, &rmrru->devices);
374 if (ret || (rmrru->devices_cnt == 0))
375 xfree(rmrru);
376 else
377 acpi_register_rmrr_unit(rmrru);
378 return ret;
379 }
381 static int __init
382 acpi_parse_one_atsr(struct acpi_dmar_entry_header *header)
383 {
384 struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header;
385 struct acpi_atsr_unit *atsru;
386 int ret = 0;
387 static int all_ports;
389 atsru = xmalloc(struct acpi_atsr_unit);
390 if (!atsru)
391 return -ENOMEM;
392 memset(atsru, 0, sizeof(struct acpi_atsr_unit));
394 atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */
395 if (!atsru->all_ports) {
396 ret = acpi_parse_dev_scope((void *)(atsr + 1),
397 ((void *)atsr) + header->length,
398 &atsru->devices_cnt, &atsru->devices);
399 }
400 else {
401 printk(KERN_WARNING PREFIX "found ALL_PORTS\n");
402 /* Only allow one ALL_PORTS */
403 if (all_ports) {
404 printk(KERN_WARNING PREFIX "Only one ALL_PORTS "
405 "device scope is allowed\n");
406 ret = -EINVAL;
407 }
408 all_ports = 1;
409 }
411 if (ret)
412 xfree(atsr);
413 else
414 acpi_register_atsr_unit(atsru);
415 return ret;
416 }
418 static void __init
419 acpi_table_print_dmar_entry(struct acpi_dmar_entry_header *header)
420 {
421 struct acpi_table_drhd *drhd;
422 struct acpi_table_rmrr *rmrr;
424 switch (header->type) {
425 case ACPI_DMAR_DRHD:
426 drhd = (struct acpi_table_drhd *)header;
427 break;
428 case ACPI_DMAR_RMRR:
429 rmrr = (struct acpi_table_rmrr *)header;
430 break;
431 }
432 }
434 static int __init
435 acpi_parse_dmar(unsigned long phys_addr, unsigned long size)
436 {
437 struct acpi_table_dmar *dmar = NULL;
438 struct acpi_dmar_entry_header *entry_header;
439 int ret = 0;
441 if (!phys_addr || !size)
442 return -EINVAL;
444 dmar = (struct acpi_table_dmar *)__acpi_map_table(phys_addr, size);
445 if (!dmar) {
446 printk (KERN_WARNING PREFIX "Unable to map DMAR\n");
447 return -ENODEV;
448 }
450 if (!dmar->haw) {
451 printk (KERN_WARNING PREFIX "Zero: Invalid DMAR haw\n");
452 return -EINVAL;
453 }
455 dmar_host_address_width = dmar->haw;
456 printk (KERN_INFO PREFIX "Host address width %d\n",
457 dmar_host_address_width);
459 entry_header = (struct acpi_dmar_entry_header *)(dmar + 1);
460 while (((unsigned long)entry_header) < (((unsigned long)dmar) + size)) {
461 acpi_table_print_dmar_entry(entry_header);
463 switch (entry_header->type) {
464 case ACPI_DMAR_DRHD:
465 printk (KERN_INFO PREFIX "found ACPI_DMAR_DRHD\n");
466 ret = acpi_parse_one_drhd(entry_header);
467 break;
468 case ACPI_DMAR_RMRR:
469 printk (KERN_INFO PREFIX "found ACPI_DMAR_RMRR\n");
470 ret = acpi_parse_one_rmrr(entry_header);
471 break;
472 case ACPI_DMAR_ATSR:
473 printk (KERN_INFO PREFIX "found ACPI_DMAR_RMRR\n");
474 ret = acpi_parse_one_atsr(entry_header);
475 break;
476 default:
477 printk(KERN_WARNING PREFIX "Unknown DMAR structure type\n");
478 ret = -EINVAL;
479 break;
480 }
481 if (ret)
482 break;
484 entry_header = ((void *)entry_header + entry_header->length);
485 }
487 /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */
488 dmar->header.signature[0] = '\0';
490 return ret;
491 }
493 int acpi_dmar_init(void)
494 {
495 extern int ioapic_ack_new;
496 int rc;
498 if (!vtd_enabled)
499 return -ENODEV;
501 if ((rc = vtd_hw_check()) != 0)
502 return rc;
504 acpi_table_parse(ACPI_DMAR, acpi_parse_dmar);
506 if (list_empty(&acpi_drhd_units)) {
507 printk(KERN_ERR PREFIX "No DMAR devices found\n");
508 vtd_enabled = 0;
509 return -ENODEV;
510 }
512 /* Use fake-vector style of IOAPIC acknowledgement. */
513 ioapic_ack_new = 0;
515 return 0;
516 }