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