xref: /illumos-gate/usr/src/uts/i86pc/io/immu_dmar.c (revision 8fc99e42676a23421c75e76660640f9765d693b1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Portions Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright (c) 2009, Intel Corporation.
28  * All rights reserved.
29  */
30 
31 
32 #include <sys/debug.h>
33 #include <sys/sysmacros.h>
34 #include <sys/types.h>
35 #include <sys/kmem.h>
36 #include <sys/sunddi.h>
37 #include <sys/list.h>
38 #include <sys/pci.h>
39 #include <sys/pci_cfgspace.h>
40 #include <sys/pci_impl.h>
41 #include <sys/sunndi.h>
42 #include <sys/ksynch.h>
43 #include <sys/cmn_err.h>
44 #include <sys/bootconf.h>
45 #include <sys/int_fmtio.h>
46 #include <sys/smbios.h>
47 #include <sys/acpi/acpi.h>
48 #include <sys/acpica.h>
49 #include <sys/iommulib.h>
50 #include <sys/immu.h>
51 
52 static void dmar_table_destroy(dmar_table_t *tbl);
53 
54 /*
55  * internal global variables
56  */
57 static char	*dmar_raw;		/* raw DMAR ACPI table */
58 static dmar_table_t *dmar_table;	/* converted form of DMAR table */
59 
60 /*
61  * global variables exported outside this file
62  */
63 boolean_t dmar_print = B_FALSE;
64 kmutex_t ioapic_drhd_lock;
65 list_t ioapic_drhd_list;
66 
67 /* ######################################################################### */
68 
69 /*
70  * helper functions to read the "raw" DMAR table
71  */
72 
73 static uint8_t
74 get_uint8(char *cp)
75 {
76 	uint8_t val = *((uint8_t *)cp);
77 	return (val);
78 }
79 
80 static uint16_t
81 get_uint16(char *cp)
82 {
83 	uint16_t val = *((uint16_t *)cp);
84 	return (val);
85 }
86 
87 static uint32_t
88 get_uint32(char *cp)
89 {
90 	uint32_t val = *((uint32_t *)cp);
91 	return (val);
92 }
93 
94 static uint64_t
95 get_uint64(char *cp)
96 {
97 	uint64_t val = *((uint64_t *)cp);
98 	return (val);
99 }
100 
101 static char *
102 get_str(char *cp, uint_t len)
103 {
104 	char *str = kmem_alloc(len + 1, KM_SLEEP);
105 
106 	(void) strlcpy(str, cp, len + 1);
107 
108 	return (str);
109 }
110 
111 static void
112 scope_list_free(list_t *scope_list)
113 {
114 	scope_t *scope;
115 
116 	if (list_is_empty(scope_list)) {
117 		list_destroy(scope_list);
118 		return;
119 	}
120 
121 	while ((scope = list_remove_head(scope_list)) != NULL) {
122 		kmem_free(scope, sizeof (scope_t));
123 	}
124 
125 	ASSERT(list_is_empty(scope_list));
126 	list_destroy(scope_list);
127 }
128 
129 static void
130 drhd_list_destroy(list_t *drhd_list)
131 {
132 	drhd_t *drhd;
133 
134 	ASSERT(drhd_list);
135 
136 	if (list_is_empty(drhd_list)) {
137 		list_destroy(drhd_list);
138 		return;
139 	}
140 
141 	while ((drhd = list_remove_head(drhd_list)) != NULL) {
142 		scope_list_free(&(drhd->dr_scope_list));
143 		kmem_free(drhd, sizeof (drhd_t));
144 	}
145 
146 	ASSERT(list_is_empty(drhd_list));
147 	list_destroy(drhd_list);
148 }
149 
150 static void
151 rmrr_list_destroy(list_t *rmrr_list)
152 {
153 	rmrr_t *rmrr;
154 
155 	ASSERT(rmrr_list);
156 
157 	if (list_is_empty(rmrr_list)) {
158 		list_destroy(rmrr_list);
159 		return;
160 	}
161 
162 	while ((rmrr = list_remove_head(rmrr_list)) != NULL) {
163 		scope_list_free(&(rmrr->rm_scope_list));
164 		kmem_free(rmrr, sizeof (rmrr_t));
165 	}
166 
167 	ASSERT(list_is_empty(rmrr_list));
168 	list_destroy(rmrr_list);
169 }
170 
171 /*
172  * parse_scope()
173  *      parse a scope structure in the "raw" table
174  */
175 static scope_t *
176 parse_scope(char *shead)
177 {
178 	scope_t *scope;
179 	char *phead;
180 	int bus, dev, func;
181 	uint8_t startbus;
182 	uint8_t len;
183 	int depth;
184 
185 	ASSERT(shead);
186 
187 	scope = kmem_zalloc(sizeof (scope_t), KM_SLEEP);
188 	scope->scp_type = get_uint8(&shead[0]);
189 	scope->scp_enumid = get_uint8(&shead[4]);
190 
191 	len = get_uint8(&shead[1]);
192 	startbus = get_uint8(&shead[5]);
193 	depth = (len - 6)/2;
194 	ASSERT(depth >= 1);
195 
196 	phead = &shead[6];
197 
198 	bus = startbus;
199 	dev = get_uint8(phead++);
200 	func = get_uint8(phead++);
201 
202 	for (depth--; depth > 0; depth--) {
203 		bus = pci_getb_func(bus, dev, func, PCI_BCNF_SECBUS);
204 		dev = get_uint8(phead++);
205 		func = get_uint8(phead++);
206 	}
207 
208 	ASSERT(bus >= 0 && bus < 256);
209 	ASSERT(dev >= 0 && dev < 32);
210 	ASSERT(func >= 0 && func < 8);
211 
212 	/* ok we got the device BDF */
213 	scope->scp_bus = bus;
214 	scope->scp_dev = dev;
215 	scope->scp_func = func;
216 
217 	return (scope);
218 }
219 
220 
221 /* setup the ioapic_drhd structure */
222 static void
223 ioapic_drhd_setup(void)
224 {
225 	mutex_init(&(ioapic_drhd_lock), NULL, MUTEX_DEFAULT, NULL);
226 
227 	mutex_enter(&(ioapic_drhd_lock));
228 	list_create(&(ioapic_drhd_list), sizeof (ioapic_drhd_t),
229 	    offsetof(ioapic_drhd_t, ioapic_node));
230 	mutex_exit(&(ioapic_drhd_lock));
231 }
232 
233 /* get ioapic source id for interrupt remapping */
234 static void
235 ioapic_drhd_insert(scope_t *scope, drhd_t *drhd)
236 {
237 	ioapic_drhd_t *idt;
238 
239 	idt = kmem_zalloc(sizeof (ioapic_drhd_t), KM_SLEEP);
240 	idt->ioapic_ioapicid = scope->scp_enumid;
241 	idt->ioapic_sid = ((scope->scp_bus << 8) | (scope->scp_dev << 3) |
242 	    (scope->scp_func));
243 	idt->ioapic_drhd = drhd;
244 
245 	mutex_enter(&ioapic_drhd_lock);
246 	list_insert_tail(&ioapic_drhd_list, idt);
247 	mutex_exit(&ioapic_drhd_lock);
248 }
249 
250 static ioapic_drhd_t *
251 ioapic_drhd_lookup(int ioapicid)
252 {
253 	ioapic_drhd_t *idt;
254 
255 	mutex_enter(&ioapic_drhd_lock);
256 	idt = list_head(&ioapic_drhd_list);
257 	for (; idt; idt = list_next(&ioapic_drhd_list, idt)) {
258 		if (idt->ioapic_ioapicid == ioapicid) {
259 			break;
260 		}
261 	}
262 	mutex_exit(&ioapic_drhd_lock);
263 
264 	return (idt);
265 }
266 
267 static void
268 ioapic_drhd_destroy(void)
269 {
270 	ioapic_drhd_t *idt;
271 
272 	mutex_enter(&ioapic_drhd_lock);
273 	while (idt = list_remove_head(&ioapic_drhd_list)) {
274 		kmem_free(idt, sizeof (ioapic_drhd_t));
275 	}
276 	list_destroy(&ioapic_drhd_list);
277 	mutex_exit(&(ioapic_drhd_lock));
278 
279 	mutex_destroy(&(ioapic_drhd_lock));
280 }
281 
282 /*
283  * parse_drhd()
284  *   parse the drhd uints in dmar table
285  */
286 static int
287 parse_drhd(char *uhead, dmar_table_t *tbl)
288 {
289 	drhd_t *drhd;
290 	int seg;
291 	int len;
292 	char *shead;
293 	scope_t *scope;
294 
295 	ASSERT(uhead);
296 	ASSERT(tbl);
297 	ASSERT(get_uint16(&uhead[0]) == DMAR_DRHD);
298 
299 	seg = get_uint16(&uhead[6]);
300 	if (seg < 0 || seg >= IMMU_MAXSEG) {
301 		ddi_err(DER_WARN, NULL, "invalid segment# <%d>"
302 		    "in DRHD unit in ACPI DMAR table", seg);
303 		return (DDI_FAILURE);
304 	}
305 
306 	drhd = kmem_zalloc(sizeof (drhd_t), KM_SLEEP);
307 	mutex_init(&(drhd->dr_lock), NULL, MUTEX_DEFAULT, NULL);
308 	list_create(&(drhd->dr_scope_list), sizeof (scope_t),
309 	    offsetof(scope_t, scp_node));
310 
311 	len = get_uint16(&uhead[2]);
312 	drhd->dr_include_all =
313 	    (get_uint8(&uhead[4]) & DMAR_INCLUDE_ALL) ? B_TRUE : B_FALSE;
314 	drhd->dr_seg = seg;
315 	drhd->dr_regs = get_uint64(&uhead[8]);
316 
317 	/*
318 	 * parse each scope.
319 	 */
320 	shead = &uhead[16];
321 	while (shead < &uhead[len - 1]) {
322 		scope = parse_scope(shead);
323 		if (scope == NULL) {
324 			return (DDI_FAILURE);
325 		}
326 
327 		if (scope->scp_type == DMAR_IOAPIC)  {
328 			ioapic_drhd_insert(scope, drhd);
329 		}
330 
331 		list_insert_tail(&(drhd->dr_scope_list), scope);
332 		shead += get_uint8(&shead[1]);
333 	}
334 
335 	list_insert_tail(&(tbl->tbl_drhd_list[drhd->dr_seg]), drhd);
336 
337 	return (DDI_SUCCESS);
338 }
339 
340 /*
341  * parse_rmrr()
342  *   parse the rmrr units in dmar table
343  */
344 static int
345 parse_rmrr(char *uhead, dmar_table_t *tbl)
346 {
347 	rmrr_t *rmrr;
348 	int seg;
349 	int len;
350 	char *shead;
351 	scope_t *scope;
352 
353 	ASSERT(uhead);
354 	ASSERT(tbl);
355 	ASSERT(get_uint16(&uhead[0]) == DMAR_RMRR);
356 
357 	seg = get_uint16(&uhead[6]);
358 	if (seg < 0 || seg >= IMMU_MAXSEG) {
359 		ddi_err(DER_WARN, NULL, "invalid segment# <%d>"
360 		    "in RMRR unit in ACPI DMAR table", seg);
361 		return (DDI_FAILURE);
362 	}
363 
364 	rmrr = kmem_zalloc(sizeof (rmrr_t), KM_SLEEP);
365 	mutex_init(&(rmrr->rm_lock), NULL, MUTEX_DEFAULT, NULL);
366 	list_create(&(rmrr->rm_scope_list), sizeof (scope_t),
367 	    offsetof(scope_t, scp_node));
368 
369 	/* RMRR region is [base,limit] */
370 	len = get_uint16(&uhead[2]);
371 	rmrr->rm_seg = get_uint16(&uhead[6]);
372 	rmrr->rm_base = get_uint64(&uhead[8]);
373 	rmrr->rm_limit = get_uint64(&uhead[16]);
374 
375 	if (rmrr->rm_base > rmrr->rm_limit) {
376 		ddi_err(DER_WARN, NULL, "IMMU: BIOS bug detected: "
377 		    "RMRR: base (%lx) > limit (%lx)",
378 		    rmrr->rm_base, rmrr->rm_limit);
379 		list_destroy(&(rmrr->rm_scope_list));
380 		mutex_destroy(&(rmrr->rm_lock));
381 		kmem_free(rmrr, sizeof (rmrr_t));
382 		return (DDI_SUCCESS);
383 	}
384 
385 	/*
386 	 * parse each scope in RMRR
387 	 */
388 	shead = &uhead[24];
389 	while (shead < &uhead[len - 1]) {
390 		scope = parse_scope(shead);
391 		if (scope == NULL) {
392 			return (DDI_FAILURE);
393 		}
394 		list_insert_tail(&(rmrr->rm_scope_list), scope);
395 		shead += get_uint8(&shead[1]);
396 	}
397 
398 	list_insert_tail(&(tbl->tbl_rmrr_list[rmrr->rm_seg]), rmrr);
399 
400 	return (DDI_SUCCESS);
401 }
402 
403 #define	TBL_OEM_ID_SZ		(6)
404 #define	TBL_OEM_TBLID_SZ	(8)
405 
406 /*
407  * parse the "raw" DMAR table and convert it
408  * into a useful form.
409  */
410 static int
411 dmar_parse(dmar_table_t **tblpp, char *raw)
412 {
413 	char *uhead;
414 	dmar_table_t *tbl;
415 	int i;
416 	char *unmstr;
417 
418 	ASSERT(raw);
419 	ASSERT(tblpp);
420 
421 	*tblpp = NULL;
422 
423 	/*
424 	 * do a sanity check. make sure the raw table
425 	 * has the right signature
426 	 */
427 	if (raw[0] != 'D' || raw[1] != 'M' ||
428 	    raw[2] != 'A' || raw[3] != 'R') {
429 		ddi_err(DER_WARN, NULL, "IOMMU ACPI "
430 		    "signature != \"DMAR\"");
431 		return (DDI_FAILURE);
432 	}
433 
434 	/*
435 	 * the platform has intel iommu, create processed ACPI struct
436 	 */
437 	tbl = kmem_zalloc(sizeof (dmar_table_t), KM_SLEEP);
438 	mutex_init(&(tbl->tbl_lock), NULL, MUTEX_DEFAULT, NULL);
439 
440 	tbl->tbl_raw = raw;
441 
442 	/*
443 	 * Note we explicitly show offsets for clarity
444 	 */
445 	tbl->tbl_rawlen = get_uint32(&raw[4]);
446 
447 	/* XXX TO DO verify checksum of table */
448 	tbl->tbl_oem_id = get_str(&raw[10], TBL_OEM_ID_SZ);
449 	tbl->tbl_oem_tblid = get_str(&raw[16], TBL_OEM_TBLID_SZ);
450 	tbl->tbl_oem_rev = get_uint32(&raw[24]);
451 	tbl->tbl_haw = get_uint8(&raw[36]) + 1;
452 	tbl->tbl_intrmap = (get_uint8(&raw[37]) & DMAR_INTRMAP_SUPPORT)
453 	    ? B_TRUE : B_FALSE;
454 
455 	/* create lists for DRHD and RMRR */
456 	for (i = 0; i < IMMU_MAXSEG; i++) {
457 		list_create(&(tbl->tbl_drhd_list[i]), sizeof (drhd_t),
458 		    offsetof(drhd_t, dr_node));
459 		list_create(&(tbl->tbl_rmrr_list[i]), sizeof (rmrr_t),
460 		    offsetof(rmrr_t, rm_node));
461 	}
462 
463 	ioapic_drhd_setup();
464 
465 	/*
466 	 * parse each unit. Currently only DRHD and RMRR types
467 	 * are parsed. We ignore all other types of units.
468 	 */
469 	uhead = &raw[48];
470 	while (uhead < &raw[tbl->tbl_rawlen - 1]) {
471 		unmstr = NULL;
472 		switch (get_uint16(uhead)) {
473 		case DMAR_DRHD:
474 			if (parse_drhd(uhead, tbl) != DDI_SUCCESS) {
475 				goto failed;
476 			}
477 			break;
478 		case DMAR_RMRR:
479 			if (parse_rmrr(uhead, tbl) != DDI_SUCCESS) {
480 				goto failed;
481 			}
482 			break;
483 		case DMAR_ATSR:
484 			unmstr = "ATSR";
485 			break;
486 		case DMAR_RHSA:
487 			unmstr = "RHSA";
488 			break;
489 		default:
490 			unmstr = "unknown unity type";
491 			break;
492 		}
493 		if (unmstr) {
494 			ddi_err(DER_NOTE, NULL, "DMAR ACPI table: "
495 			    "skipping unsupported unit type %s", unmstr);
496 		}
497 		uhead += get_uint16(&uhead[2]);
498 	}
499 
500 	*tblpp = tbl;
501 	return (DDI_SUCCESS);
502 
503 failed:
504 	dmar_table_destroy(tbl);
505 	return (DDI_FAILURE);
506 }
507 
508 static char *
509 scope_type(int devtype)
510 {
511 	char *typestr;
512 
513 	switch (devtype) {
514 	case DMAR_ENDPOINT:
515 		typestr = "endpoint-device";
516 		break;
517 	case DMAR_SUBTREE:
518 		typestr = "subtree-device";
519 		break;
520 	case DMAR_IOAPIC:
521 		typestr = "IOAPIC";
522 		break;
523 	case DMAR_HPET:
524 		typestr = "HPET";
525 		break;
526 	default:
527 		typestr = "Unknown device";
528 		break;
529 	}
530 
531 	return (typestr);
532 }
533 
534 static void
535 print_scope_list(list_t *scope_list)
536 {
537 	scope_t *scope;
538 
539 	if (list_is_empty(scope_list))
540 		return;
541 
542 	ddi_err(DER_CONT, NULL, "\tdevice list:\n");
543 
544 	for (scope = list_head(scope_list); scope;
545 	    scope = list_next(scope_list, scope)) {
546 		ddi_err(DER_CONT, NULL, "\t\ttype = %s\n",
547 		    scope_type(scope->scp_type));
548 		ddi_err(DER_CONT, NULL, "\n\t\tbus = %d\n",
549 		    scope->scp_bus);
550 		ddi_err(DER_CONT, NULL, "\t\tdev = %d\n",
551 		    scope->scp_dev);
552 		ddi_err(DER_CONT, NULL, "\t\tfunc = %d\n",
553 		    scope->scp_func);
554 	}
555 }
556 
557 static void
558 print_drhd_list(list_t *drhd_list)
559 {
560 	drhd_t *drhd;
561 
562 	if (list_is_empty(drhd_list))
563 		return;
564 
565 	ddi_err(DER_CONT, NULL, "\ndrhd list:\n");
566 
567 	for (drhd = list_head(drhd_list); drhd;
568 	    drhd = list_next(drhd_list, drhd)) {
569 
570 		ddi_err(DER_CONT, NULL, "\n\tsegment = %d\n",
571 		    drhd->dr_seg);
572 		ddi_err(DER_CONT, NULL, "\treg_base = 0x%" PRIx64 "\n",
573 		    drhd->dr_regs);
574 		ddi_err(DER_CONT, NULL, "\tinclude_all = %s\n",
575 		    drhd->dr_include_all == B_TRUE ? "TRUE" : "FALSE");
576 		ddi_err(DER_CONT, NULL, "\tdip = 0x%p\n",
577 		    (void *)drhd->dr_dip);
578 
579 		print_scope_list(&(drhd->dr_scope_list));
580 	}
581 }
582 
583 
584 static void
585 print_rmrr_list(list_t *rmrr_list)
586 {
587 	rmrr_t *rmrr;
588 
589 	if (list_is_empty(rmrr_list))
590 		return;
591 
592 	ddi_err(DER_CONT, NULL, "\nrmrr list:\n");
593 
594 	for (rmrr = list_head(rmrr_list); rmrr;
595 	    rmrr = list_next(rmrr_list, rmrr)) {
596 
597 		ddi_err(DER_CONT, NULL, "\n\tsegment = %d\n",
598 		    rmrr->rm_seg);
599 		ddi_err(DER_CONT, NULL, "\tbase = 0x%lx\n",
600 		    rmrr->rm_base);
601 		ddi_err(DER_CONT, NULL, "\tlimit = 0x%lx\n",
602 		    rmrr->rm_limit);
603 
604 		print_scope_list(&(rmrr->rm_scope_list));
605 	}
606 }
607 
608 /*
609  * print DMAR table
610  */
611 static void
612 dmar_table_print(dmar_table_t *tbl)
613 {
614 	int i;
615 
616 	if (dmar_print == B_FALSE) {
617 		return;
618 	}
619 
620 	/* print the title */
621 	ddi_err(DER_CONT, NULL, "#### Start of dmar_table ####\n");
622 	ddi_err(DER_CONT, NULL, "\thaw = %d\n", tbl->tbl_haw);
623 	ddi_err(DER_CONT, NULL, "\tintr_remap = %s\n",
624 	    tbl->tbl_intrmap == B_TRUE ? "<true>" : "<false>");
625 
626 	/* print drhd list */
627 	for (i = 0; i < IMMU_MAXSEG; i++) {
628 		print_drhd_list(&(tbl->tbl_drhd_list[i]));
629 	}
630 
631 
632 	/* print rmrr list */
633 	for (i = 0; i < IMMU_MAXSEG; i++) {
634 		print_rmrr_list(&(tbl->tbl_rmrr_list[i]));
635 	}
636 
637 	ddi_err(DER_CONT, NULL, "#### END of dmar_table ####\n");
638 }
639 
640 static void
641 drhd_devi_create(drhd_t *drhd, char *name)
642 {
643 	struct ddi_parent_private_data *pdptr;
644 	struct regspec reg;
645 	dev_info_t *dip;
646 
647 	ndi_devi_alloc_sleep(root_devinfo, name,
648 	    DEVI_SID_NODEID, &dip);
649 
650 	drhd->dr_dip = dip;
651 
652 	reg.regspec_bustype = 0;
653 	reg.regspec_addr = drhd->dr_regs;
654 	reg.regspec_size = IMMU_REGSZ;
655 
656 	/*
657 	 * update the reg properties
658 	 *
659 	 *   reg property will be used for register
660 	 *   set access
661 	 *
662 	 * refer to the bus_map of root nexus driver
663 	 * I/O or memory mapping:
664 	 *
665 	 * <bustype=0, addr=x, len=x>: memory
666 	 * <bustype=1, addr=x, len=x>: i/o
667 	 * <bustype>1, addr=0, len=x>: x86-compatibility i/o
668 	 */
669 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE,
670 	    dip, "reg", (int *)&reg,
671 	    sizeof (struct regspec) / sizeof (int));
672 
673 
674 	pdptr = kmem_zalloc(sizeof (struct ddi_parent_private_data)
675 	    + sizeof (struct regspec), KM_SLEEP);
676 	pdptr->par_nreg = 1;
677 	pdptr->par_reg = (struct regspec *)(pdptr + 1);
678 	pdptr->par_reg->regspec_bustype = 0;
679 	pdptr->par_reg->regspec_addr = drhd->dr_regs;
680 	pdptr->par_reg->regspec_size = IMMU_REGSZ;
681 	ddi_set_parent_data(dip, pdptr);
682 }
683 
684 /*
685  * dmar_devinfos_create()
686  *
687  *   create the dev_info node in the device tree,
688  *   the info node is a nuxus child of the root
689  *   nexus
690  */
691 static void
692 dmar_devinfos_create(dmar_table_t *tbl)
693 {
694 	list_t *drhd_list;
695 	drhd_t *drhd;
696 	char name[IMMU_MAXNAMELEN];
697 	int i, unit;
698 
699 	for (i = 0; i < IMMU_MAXSEG; i++) {
700 
701 		drhd_list = &(tbl->tbl_drhd_list[i]);
702 
703 		if (list_is_empty(drhd_list))
704 			continue;
705 
706 		drhd = list_head(drhd_list);
707 		for (unit = 0; drhd;
708 		    drhd = list_next(drhd_list, drhd), unit++) {
709 			(void) snprintf(name, sizeof (name),
710 			    "drhd%d,%d", i, unit);
711 			drhd_devi_create(drhd, name);
712 		}
713 	}
714 }
715 
716 static void
717 drhd_devi_destroy(drhd_t *drhd)
718 {
719 	dev_info_t *dip;
720 	int count;
721 
722 	dip = drhd->dr_dip;
723 	ASSERT(dip);
724 
725 	ndi_devi_enter(root_devinfo, &count);
726 	if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != DDI_SUCCESS) {
727 		ddi_err(DER_WARN, dip, "Failed to destroy");
728 	}
729 	ndi_devi_exit(root_devinfo, count);
730 	drhd->dr_dip = NULL;
731 }
732 
733 /*
734  * dmar_devi_destroy()
735  *
736  * destroy dev_info nodes for all drhd units
737  */
738 static void
739 dmar_devi_destroy(dmar_table_t *tbl)
740 {
741 	drhd_t *drhd;
742 	list_t *drhd_list;
743 	int i;
744 
745 	for (i = 0; i < IMMU_MAXSEG; i++) {
746 		drhd_list = &(tbl->tbl_drhd_list[i]);
747 		if (list_is_empty(drhd_list))
748 			continue;
749 
750 		drhd = list_head(drhd_list);
751 		for (; drhd; drhd = list_next(drhd_list, drhd)) {
752 			drhd_devi_destroy(drhd);
753 		}
754 	}
755 }
756 
757 static int
758 match_bdf(dev_info_t *ddip, void *arg)
759 {
760 	immu_arg_t *imarg = (immu_arg_t *)arg;
761 	immu_devi_t *immu_devi;
762 
763 	ASSERT(ddip);
764 	ASSERT(imarg);
765 	ASSERT(imarg->ima_seg == 0);
766 	ASSERT(imarg->ima_bus >= 0);
767 	ASSERT(imarg->ima_devfunc >= 0);
768 	ASSERT(imarg->ima_ddip == NULL);
769 
770 	/* rdip can be NULL */
771 
772 	mutex_enter(&(DEVI(ddip)->devi_lock));
773 
774 	immu_devi = IMMU_DEVI(ddip);
775 	ASSERT(immu_devi);
776 
777 	if (immu_devi->imd_seg == imarg->ima_seg &&
778 	    immu_devi->imd_bus == imarg->ima_bus &&
779 	    immu_devi->imd_devfunc == imarg->ima_devfunc) {
780 		imarg->ima_ddip = ddip;
781 	}
782 
783 	mutex_exit(&(DEVI(ddip)->devi_lock));
784 
785 	return (imarg->ima_ddip ? DDI_WALK_TERMINATE : DDI_WALK_CONTINUE);
786 }
787 static void
788 dmar_table_destroy(dmar_table_t *tbl)
789 {
790 	int i;
791 
792 	ASSERT(tbl);
793 
794 	/* destroy lists for DRHD and RMRR */
795 	for (i = 0; i < IMMU_MAXSEG; i++) {
796 		rmrr_list_destroy(&(tbl->tbl_rmrr_list[i]));
797 		drhd_list_destroy(&(tbl->tbl_drhd_list[i]));
798 	}
799 
800 	/* free strings */
801 	kmem_free(tbl->tbl_oem_tblid, TBL_OEM_ID_SZ + 1);
802 	kmem_free(tbl->tbl_oem_id, TBL_OEM_TBLID_SZ + 1);
803 	tbl->tbl_raw = NULL; /* raw ACPI table doesn't have to be freed */
804 	mutex_destroy(&(tbl->tbl_lock));
805 	kmem_free(tbl, sizeof (dmar_table_t));
806 }
807 
808 /*
809  * #########################################################################
810  * Functions exported by dmar.c
811  * This file deals with reading and processing the DMAR ACPI table
812  * #########################################################################
813  */
814 
815 /*
816  * immu_dmar_setup()
817  *	Check if the system has a DMAR ACPI table. If yes, the system
818  *	has Intel IOMMU hardware
819  */
820 int
821 immu_dmar_setup(void)
822 {
823 	if (AcpiGetTable("DMAR", 1, (ACPI_TABLE_HEADER **)&dmar_raw) != AE_OK) {
824 		ddi_err(DER_LOG, NULL,
825 		    "No DMAR ACPI table. No Intel IOMMU present\n");
826 		dmar_raw = NULL;
827 		return (DDI_FAILURE);
828 	}
829 	ASSERT(dmar_raw);
830 	return (DDI_SUCCESS);
831 }
832 
833 /*
834  * immu_dmar_parse()
835  *  Called by immu.c to parse and convert "raw" ACPI DMAR table
836  */
837 int
838 immu_dmar_parse(void)
839 {
840 	dmar_table_t *tbl = NULL;
841 
842 	/* we should already have found the "raw" table */
843 	ASSERT(dmar_raw);
844 
845 	ddi_err(DER_CONT, NULL, "?Processing DMAR ACPI table\n");
846 
847 	dmar_table = NULL;
848 
849 	/*
850 	 * parse DMAR ACPI table
851 	 */
852 	if (dmar_parse(&tbl, dmar_raw) != DDI_SUCCESS) {
853 		ASSERT(tbl == NULL);
854 		return (DDI_FAILURE);
855 	}
856 
857 	ASSERT(tbl);
858 
859 	/*
860 	 * create one devinfo for every drhd unit
861 	 * in the DMAR table
862 	 */
863 	dmar_devinfos_create(tbl);
864 
865 	/*
866 	 * print the dmar table if the debug option is set
867 	 */
868 	dmar_table_print(tbl);
869 
870 	dmar_table = tbl;
871 
872 	return (DDI_SUCCESS);
873 }
874 
875 void
876 immu_dmar_startup(void)
877 {
878 	/* nothing to do */
879 }
880 
881 void
882 immu_dmar_shutdown(void)
883 {
884 	/* nothing to do */
885 }
886 
887 void
888 immu_dmar_destroy(void)
889 {
890 	dmar_devi_destroy(dmar_table);
891 	dmar_table_destroy(dmar_table);
892 	ioapic_drhd_destroy();
893 	dmar_table = NULL;
894 	dmar_raw = NULL;
895 }
896 
897 boolean_t
898 immu_dmar_blacklisted(char **strptr, uint_t nstrs)
899 {
900 	dmar_table_t *tbl = dmar_table;
901 	int i;
902 	char oem_rev[IMMU_MAXNAMELEN];
903 
904 	ASSERT(tbl);
905 
906 	ASSERT((strptr == NULL) ^ (nstrs != 0));
907 
908 	/*
909 	 * Must be a minimum of 4
910 	 */
911 	if (nstrs < 4) {
912 		return (B_FALSE);
913 	}
914 
915 	ddi_err(DER_CONT, NULL, "?System DMAR ACPI table information:\n");
916 	ddi_err(DER_CONT, NULL, "?OEM-ID = <%s>\n", tbl->tbl_oem_id);
917 	ddi_err(DER_CONT, NULL, "?Table-ID = <%s>\n", tbl->tbl_oem_tblid);
918 	(void) snprintf(oem_rev, sizeof (oem_rev), "%d", tbl->tbl_oem_rev);
919 	ddi_err(DER_CONT, NULL, "?Revision = <%s>\n", oem_rev);
920 
921 	for (i = 0; nstrs - i >= 4; i++) {
922 		if (strcmp(*strptr++, "DMAR") == 0) {
923 			if (strcmp(*strptr++, tbl->tbl_oem_id) == 0 &&
924 			    ((char *)strptr == '\0' ||
925 			    strcmp(*strptr++, tbl->tbl_oem_tblid) == 0) &&
926 			    ((char *)strptr == '\0' ||
927 			    strcmp(*strptr++, oem_rev) == 0)) {
928 				return (B_TRUE);
929 			}
930 			i += 3; /* for loops adds 1 as well, so only 3 here */
931 		}
932 	}
933 	return (B_FALSE);
934 }
935 
936 void
937 immu_dmar_rmrr_map(void)
938 {
939 	int seg;
940 	int e;
941 	int count;
942 	dev_info_t *rdip;
943 	scope_t *scope;
944 	rmrr_t *rmrr;
945 	dmar_table_t *tbl;
946 
947 	ASSERT(dmar_table);
948 
949 	tbl = dmar_table;
950 
951 	/* called during boot, when kernel is single threaded. No lock */
952 
953 	/*
954 	 * for each segment, walk the rmrr list looking for an exact match
955 	 */
956 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
957 		rmrr = list_head(&(tbl->tbl_rmrr_list)[seg]);
958 		for (; rmrr; rmrr = list_next(&(tbl->tbl_rmrr_list)[seg],
959 		    rmrr)) {
960 
961 			/*
962 			 * try to match BDF *exactly* to a device scope.
963 			 */
964 			scope = list_head(&(rmrr->rm_scope_list));
965 			for (; scope;
966 			    scope = list_next(&(rmrr->rm_scope_list), scope)) {
967 				immu_arg_t imarg = {0};
968 				memrng_t mrng = {0};
969 
970 				/* PCI endpoint devices only */
971 				if (scope->scp_type != DMAR_ENDPOINT)
972 					continue;
973 
974 				imarg.ima_seg = seg;
975 				imarg.ima_bus = scope->scp_bus;
976 				imarg.ima_devfunc =
977 				    IMMU_PCI_DEVFUNC(scope->scp_dev,
978 				    scope->scp_func);
979 				imarg.ima_ddip = NULL;
980 				imarg.ima_rdip = NULL;
981 
982 				ASSERT(root_devinfo);
983 				/* XXX should be optimized */
984 				ndi_devi_enter(root_devinfo, &count);
985 				ddi_walk_devs(ddi_get_child(root_devinfo),
986 				    match_bdf, &imarg);
987 				ndi_devi_exit(root_devinfo, count);
988 
989 				if (imarg.ima_ddip == NULL) {
990 					ddi_err(DER_WARN, NULL,
991 					    "No dip found for "
992 					    "bus=0x%x, dev=0x%x, func= 0x%x",
993 					    scope->scp_bus, scope->scp_dev,
994 					    scope->scp_func);
995 					continue;
996 				}
997 
998 				rdip = imarg.ima_ddip;
999 				/*
1000 				 * This address must be in the BIOS reserved
1001 				 * map
1002 				 */
1003 				if (!address_in_memlist(bios_rsvd,
1004 				    (uint64_t)rmrr->rm_base, rmrr->rm_limit -
1005 				    rmrr->rm_base + 1)) {
1006 					ddi_err(DER_WARN, rdip, "RMRR range "
1007 					    " [0x%" PRIx64 " - 0x%" PRIx64 "]"
1008 					    " not in BIOS reserved map",
1009 					    rmrr->rm_base, rmrr->rm_limit);
1010 				}
1011 
1012 				/* XXX could be more efficient */
1013 				memlist_read_lock();
1014 				if (address_in_memlist(phys_install,
1015 				    (uint64_t)rmrr->rm_base, rmrr->rm_limit -
1016 				    rmrr->rm_base + 1)) {
1017 					ddi_err(DER_WARN, rdip, "RMRR range "
1018 					    " [0x%" PRIx64 " - 0x%" PRIx64 "]"
1019 					    " is in physinstall map",
1020 					    rmrr->rm_base, rmrr->rm_limit);
1021 				}
1022 				memlist_read_unlock();
1023 
1024 
1025 				ddi_err(DER_LOG, rdip,
1026 				    "IMMU: Mapping RMRR range "
1027 				    "[0x%" PRIx64 " - 0x%"PRIx64 "]",
1028 				    rmrr->rm_base, rmrr->rm_limit);
1029 
1030 				mrng.mrng_start =
1031 				    IMMU_ROUNDOWN((uintptr_t)rmrr->rm_base);
1032 				mrng.mrng_npages =
1033 				    IMMU_ROUNDUP((uintptr_t)rmrr->rm_limit -
1034 				    (uintptr_t)rmrr->rm_base + 1) /
1035 				    IMMU_PAGESIZE;
1036 				e = immu_dvma_map(NULL, NULL, &mrng, 0, rdip,
1037 				    IMMU_FLAGS_READ | IMMU_FLAGS_WRITE |
1038 				    IMMU_FLAGS_MEMRNG);
1039 				/*
1040 				 * dip may have unity domain or xlate domain
1041 				 * If the former, PHYSICAL is returned else
1042 				 * MAPPED is returned.
1043 				 */
1044 				ASSERT(e == DDI_DMA_MAPPED ||
1045 				    e == DDI_DMA_USE_PHYSICAL);
1046 			}
1047 		}
1048 	}
1049 
1050 }
1051 
1052 immu_t *
1053 immu_dmar_get_immu(dev_info_t *rdip)
1054 {
1055 	int seg;
1056 	int tlevel;
1057 	int level;
1058 	drhd_t *drhd;
1059 	drhd_t *tdrhd;
1060 	scope_t *scope;
1061 	dmar_table_t *tbl;
1062 
1063 	ASSERT(dmar_table);
1064 
1065 	tbl = dmar_table;
1066 
1067 	mutex_enter(&(tbl->tbl_lock));
1068 
1069 	/*
1070 	 * for each segment, walk the drhd list looking for an exact match
1071 	 */
1072 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
1073 		drhd = list_head(&(tbl->tbl_drhd_list)[seg]);
1074 		for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg],
1075 		    drhd)) {
1076 
1077 			/*
1078 			 * we are currently searching for exact matches so
1079 			 * skip "include all" (catchall) and subtree matches
1080 			 */
1081 			if (drhd->dr_include_all == B_TRUE)
1082 				continue;
1083 
1084 			/*
1085 			 * try to match BDF *exactly* to a device scope.
1086 			 */
1087 			scope = list_head(&(drhd->dr_scope_list));
1088 			for (; scope;
1089 			    scope = list_next(&(drhd->dr_scope_list), scope)) {
1090 				immu_arg_t imarg = {0};
1091 
1092 				/* PCI endpoint devices only */
1093 				if (scope->scp_type != DMAR_ENDPOINT)
1094 					continue;
1095 
1096 				imarg.ima_seg = seg;
1097 				imarg.ima_bus = scope->scp_bus;
1098 				imarg.ima_devfunc =
1099 				    IMMU_PCI_DEVFUNC(scope->scp_dev,
1100 				    scope->scp_func);
1101 				imarg.ima_ddip = NULL;
1102 				imarg.ima_rdip = rdip;
1103 				level = 0;
1104 				if (immu_walk_ancestor(rdip, NULL, match_bdf,
1105 				    &imarg, &level, IMMU_FLAGS_DONTPASS)
1106 				    != DDI_SUCCESS) {
1107 					/* skip - nothing else we can do */
1108 					continue;
1109 				}
1110 
1111 				/* Should have walked only 1 level i.e. rdip */
1112 				ASSERT(level == 1);
1113 
1114 				if (imarg.ima_ddip) {
1115 					ASSERT(imarg.ima_ddip == rdip);
1116 					goto found;
1117 				}
1118 			}
1119 		}
1120 	}
1121 
1122 	/*
1123 	 * walk the drhd list looking for subtree match
1124 	 * i.e. is the device a descendant of a devscope BDF.
1125 	 * We want the lowest subtree.
1126 	 */
1127 	tdrhd = NULL;
1128 	tlevel = 0;
1129 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
1130 		drhd = list_head(&(tbl->tbl_drhd_list)[seg]);
1131 		for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg],
1132 		    drhd)) {
1133 
1134 			/* looking for subtree match */
1135 			if (drhd->dr_include_all == B_TRUE)
1136 				continue;
1137 
1138 			/*
1139 			 * try to match the device scope
1140 			 */
1141 			scope = list_head(&(drhd->dr_scope_list));
1142 			for (; scope;
1143 			    scope = list_next(&(drhd->dr_scope_list), scope)) {
1144 				immu_arg_t imarg = {0};
1145 
1146 				/* PCI subtree only */
1147 				if (scope->scp_type != DMAR_SUBTREE)
1148 					continue;
1149 
1150 				imarg.ima_seg = seg;
1151 				imarg.ima_bus = scope->scp_bus;
1152 				imarg.ima_devfunc =
1153 				    IMMU_PCI_DEVFUNC(scope->scp_dev,
1154 				    scope->scp_func);
1155 
1156 				imarg.ima_ddip = NULL;
1157 				imarg.ima_rdip = rdip;
1158 				level = 0;
1159 				if (immu_walk_ancestor(rdip, NULL, match_bdf,
1160 				    &imarg, &level, 0) != DDI_SUCCESS) {
1161 					/* skip - nothing else we can do */
1162 					continue;
1163 				}
1164 
1165 				/* should have walked 1 level i.e. rdip */
1166 				ASSERT(level > 0);
1167 
1168 				/* look for lowest ancestor matching drhd */
1169 				if (imarg.ima_ddip && (tdrhd == NULL ||
1170 				    level < tlevel)) {
1171 					tdrhd = drhd;
1172 					tlevel = level;
1173 				}
1174 			}
1175 		}
1176 	}
1177 
1178 	if ((drhd = tdrhd) != NULL) {
1179 		goto found;
1180 	}
1181 
1182 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
1183 		drhd = list_head(&(tbl->tbl_drhd_list[seg]));
1184 		for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg],
1185 		    drhd)) {
1186 			/* Look for include all */
1187 			if (drhd->dr_include_all == B_TRUE) {
1188 				break;
1189 			}
1190 		}
1191 	}
1192 
1193 	/*FALLTHRU*/
1194 
1195 found:
1196 	mutex_exit(&(tbl->tbl_lock));
1197 
1198 	/*
1199 	 * No drhd (dmar unit) found for this device in the ACPI DMAR tables.
1200 	 * This may happen with buggy versions of BIOSes. Just warn instead
1201 	 * of panic as we don't want whole system to go down because of one
1202 	 * device.
1203 	 */
1204 	if (drhd == NULL) {
1205 		ddi_err(DER_WARN, rdip, "can't find Intel IOMMU unit for "
1206 		    "device in ACPI DMAR table.");
1207 		return (NULL);
1208 	}
1209 
1210 	return (drhd->dr_immu);
1211 }
1212 
1213 char *
1214 immu_dmar_unit_name(void *dmar_unit)
1215 {
1216 	drhd_t *drhd = (drhd_t *)dmar_unit;
1217 
1218 	ASSERT(drhd->dr_dip);
1219 	return (ddi_node_name(drhd->dr_dip));
1220 }
1221 
1222 dev_info_t *
1223 immu_dmar_unit_dip(void *dmar_unit)
1224 {
1225 	drhd_t *drhd = (drhd_t *)dmar_unit;
1226 	return (drhd->dr_dip);
1227 }
1228 
1229 void *
1230 immu_dmar_walk_units(int seg, void *dmar_unit)
1231 {
1232 	list_t *drhd_list;
1233 	drhd_t *drhd = (drhd_t *)dmar_unit;
1234 
1235 	drhd_list = &(dmar_table->tbl_drhd_list[seg]);
1236 
1237 	if (drhd == NULL) {
1238 		return ((void *)list_head(drhd_list));
1239 	} else {
1240 		return ((void *)list_next(drhd_list, drhd));
1241 	}
1242 }
1243 
1244 void
1245 immu_dmar_set_immu(void *dmar_unit, immu_t *immu)
1246 {
1247 	drhd_t *drhd = (drhd_t *)dmar_unit;
1248 
1249 	ASSERT(drhd);
1250 	ASSERT(immu);
1251 
1252 	drhd->dr_immu = immu;
1253 }
1254 
1255 boolean_t
1256 immu_dmar_intrmap_supported(void)
1257 {
1258 	ASSERT(dmar_table);
1259 	return (dmar_table->tbl_intrmap);
1260 }
1261 
1262 /* for a given ioapicid, find the source id and immu */
1263 uint16_t
1264 immu_dmar_ioapic_sid(int ioapicid)
1265 {
1266 	ioapic_drhd_t *idt;
1267 
1268 	idt = ioapic_drhd_lookup(ioapicid);
1269 	if (idt == NULL) {
1270 		ddi_err(DER_PANIC, NULL, "cannot determine source-id for "
1271 		    "IOAPIC (id = %d)", ioapicid);
1272 		/*NOTREACHED*/
1273 	}
1274 
1275 	return (idt->ioapic_sid);
1276 }
1277 
1278 /* for a given ioapicid, find the source id and immu */
1279 immu_t *
1280 immu_dmar_ioapic_immu(int ioapicid)
1281 {
1282 	ioapic_drhd_t *idt;
1283 
1284 	idt = ioapic_drhd_lookup(ioapicid);
1285 	if (idt) {
1286 		return (idt->ioapic_drhd ? idt->ioapic_drhd->dr_immu : NULL);
1287 	}
1288 	return (NULL);
1289 }
1290