xref: /freebsd/sys/arm64/acpica/acpi_iort.c (revision a91a246563dffa876a52f53a98de4af9fa364c52)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2018 Marvell International Ltd.
5  *
6  * Author: Jayachandran C Nair <jchandra@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include "opt_acpi.h"
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 
37 #include <machine/intr.h>
38 
39 #include <contrib/dev/acpica/include/acpi.h>
40 #include <contrib/dev/acpica/include/accommon.h>
41 #include <contrib/dev/acpica/include/actables.h>
42 
43 #include <dev/acpica/acpivar.h>
44 
45 /*
46  * Track next XREF available for ITS groups.
47  */
48 static u_int acpi_its_xref = ACPI_MSI_XREF;
49 
50 /*
51  * Some types of IORT nodes have a set of mappings.  Each of them map
52  * a range of device IDs [base..end] from the current node to another
53  * node. The corresponding device IDs on destination node starts at
54  * outbase.
55  */
56 struct iort_map_entry {
57 	u_int			base;
58 	u_int			end;
59 	u_int			outbase;
60 	u_int			flags;
61 	u_int			out_node_offset;
62 	struct iort_node	*out_node;
63 };
64 
65 /*
66  * The ITS group node does not have any outgoing mappings. It has a
67  * of a list of GIC ITS blocks which can handle the device ID. We
68  * will store the PIC XREF used by the block and the blocks proximity
69  * data here, so that it can be retrieved together.
70  */
71 struct iort_its_entry {
72 	u_int			its_id;
73 	u_int			xref;
74 	int			pxm;
75 };
76 
77 struct iort_named_component
78 {
79 	UINT32                  NodeFlags;
80 	UINT64                  MemoryProperties;
81 	UINT8                   MemoryAddressLimit;
82 	char                    DeviceName[32]; /* Path of namespace object */
83 };
84 
85 /*
86  * IORT node. Each node has some device specific data depending on the
87  * type of the node. The node can also have a set of mappings, OR in
88  * case of ITS group nodes a set of ITS entries.
89  * The nodes are kept in a TAILQ by type.
90  */
91 struct iort_node {
92 	TAILQ_ENTRY(iort_node)	next;		/* next entry with same type */
93 	enum AcpiIortNodeType	type;		/* ACPI type */
94 	u_int			node_offset;	/* offset in IORT - node ID */
95 	u_int			nentries;	/* items in array below */
96 	u_int			usecount;	/* for bookkeeping */
97 	u_int			revision;	/* node revision */
98 	union {
99 		struct iort_map_entry	*mappings;	/* node mappings  */
100 		struct iort_its_entry	*its;		/* ITS IDs array */
101 	} entries;
102 	union {
103 		ACPI_IORT_ROOT_COMPLEX		pci_rc;	/* PCI root complex */
104 		ACPI_IORT_SMMU			smmu;
105 		ACPI_IORT_SMMU_V3		smmu_v3;
106 		struct iort_named_component	named_comp;
107 	} data;
108 };
109 
110 /* Lists for each of the types. */
111 static TAILQ_HEAD(, iort_node) pci_nodes = TAILQ_HEAD_INITIALIZER(pci_nodes);
112 static TAILQ_HEAD(, iort_node) smmu_nodes = TAILQ_HEAD_INITIALIZER(smmu_nodes);
113 static TAILQ_HEAD(, iort_node) its_groups = TAILQ_HEAD_INITIALIZER(its_groups);
114 static TAILQ_HEAD(, iort_node) named_nodes = TAILQ_HEAD_INITIALIZER(named_nodes);
115 
116 static int
117 iort_entry_get_id_mapping_index(struct iort_node *node)
118 {
119 
120 	switch(node->type) {
121 	case ACPI_IORT_NODE_SMMU_V3:
122 		/* The ID mapping field was added in version 1 */
123 		if (node->revision < 1)
124 			return (-1);
125 
126 		/*
127 		 * If all the control interrupts are GISCV based the ID
128 		 * mapping field is ignored.
129 		 */
130 		if (node->data.smmu_v3.EventGsiv != 0 &&
131 		    node->data.smmu_v3.PriGsiv != 0 &&
132 		    node->data.smmu_v3.GerrGsiv != 0 &&
133 		    node->data.smmu_v3.SyncGsiv != 0)
134 			return (-1);
135 
136 		if (node->data.smmu_v3.IdMappingIndex >= node->nentries)
137 			return (-1);
138 
139 		return (node->data.smmu_v3.IdMappingIndex);
140 	case ACPI_IORT_NODE_PMCG:
141 		return (0);
142 	default:
143 		break;
144 	}
145 
146 	return (-1);
147 }
148 
149 /*
150  * Lookup an ID in the mappings array. If successful, map the input ID
151  * to the output ID and return the output node found.
152  */
153 static struct iort_node *
154 iort_entry_lookup(struct iort_node *node, u_int id, u_int *outid)
155 {
156 	struct iort_map_entry *entry;
157 	int i, id_map;
158 
159 	id_map = iort_entry_get_id_mapping_index(node);
160 	entry = node->entries.mappings;
161 	for (i = 0; i < node->nentries; i++, entry++) {
162 		if (i == id_map)
163 			continue;
164 		if (entry->base <= id && id <= entry->end)
165 			break;
166 	}
167 	if (i == node->nentries)
168 		return (NULL);
169 	if ((entry->flags & ACPI_IORT_ID_SINGLE_MAPPING) == 0)
170 		*outid = entry->outbase + (id - entry->base);
171 	else
172 		*outid = entry->outbase;
173 	return (entry->out_node);
174 }
175 
176 /*
177  * Perform an additional lookup in case of SMMU node and ITS outtype.
178  */
179 static struct iort_node *
180 iort_smmu_trymap(struct iort_node *node, u_int outtype, u_int *outid)
181 {
182 	/* Original node can be not found. */
183 	if (!node)
184 		return (NULL);
185 
186 	/* Node can be SMMU or ITS. If SMMU, we need another lookup. */
187 	if (outtype == ACPI_IORT_NODE_ITS_GROUP &&
188 	    (node->type == ACPI_IORT_NODE_SMMU_V3 ||
189 	     node->type == ACPI_IORT_NODE_SMMU)) {
190 		node = iort_entry_lookup(node, *outid, outid);
191 		if (node == NULL)
192 			return (NULL);
193 	}
194 
195 	KASSERT(node->type == outtype, ("mapping fail"));
196 	return (node);
197 }
198 
199 /*
200  * Map a PCI RID to a SMMU node or an ITS node, based on outtype.
201  */
202 static struct iort_node *
203 iort_pci_rc_map(u_int seg, u_int rid, u_int outtype, u_int *outid)
204 {
205 	struct iort_node *node, *out_node;
206 	u_int nxtid;
207 
208 	out_node = NULL;
209 	TAILQ_FOREACH(node, &pci_nodes, next) {
210 		if (node->data.pci_rc.PciSegmentNumber != seg)
211 			continue;
212 		out_node = iort_entry_lookup(node, rid, &nxtid);
213 		if (out_node != NULL)
214 			break;
215 	}
216 
217 	out_node = iort_smmu_trymap(out_node, outtype, &nxtid);
218 	if (out_node)
219 		*outid = nxtid;
220 
221 	return (out_node);
222 }
223 
224 /*
225  * Map a named component node to a SMMU node or an ITS node, based on outtype.
226  */
227 static struct iort_node *
228 iort_named_comp_map(const char *devname, u_int rid, u_int outtype, u_int *outid)
229 {
230 	struct iort_node *node, *out_node;
231 	u_int nxtid;
232 
233 	out_node = NULL;
234 	TAILQ_FOREACH(node, &named_nodes, next) {
235 		if (strstr(node->data.named_comp.DeviceName, devname) == NULL)
236 			continue;
237 		out_node = iort_entry_lookup(node, rid, &nxtid);
238 		if (out_node != NULL)
239 			break;
240 	}
241 
242 	out_node = iort_smmu_trymap(out_node, outtype, &nxtid);
243 	if (out_node)
244 		*outid = nxtid;
245 
246 	return (out_node);
247 }
248 
249 #ifdef notyet
250 /*
251  * Not implemented, map a PCIe device to the SMMU it is associated with.
252  */
253 int
254 acpi_iort_map_smmu(u_int seg, u_int devid, void **smmu, u_int *sid)
255 {
256 	/* XXX: convert oref to SMMU device */
257 	return (ENXIO);
258 }
259 #endif
260 
261 /*
262  * Allocate memory for a node, initialize and copy mappings. 'start'
263  * argument provides the table start used to calculate the node offset.
264  */
265 static void
266 iort_copy_data(struct iort_node *node, ACPI_IORT_NODE *node_entry)
267 {
268 	ACPI_IORT_ID_MAPPING *map_entry;
269 	struct iort_map_entry *mapping;
270 	int i;
271 
272 	map_entry = ACPI_ADD_PTR(ACPI_IORT_ID_MAPPING, node_entry,
273 	    node_entry->MappingOffset);
274 	node->nentries = node_entry->MappingCount;
275 	node->usecount = 0;
276 	mapping = malloc(sizeof(*mapping) * node->nentries, M_DEVBUF,
277 	    M_WAITOK | M_ZERO);
278 	node->entries.mappings = mapping;
279 	for (i = 0; i < node->nentries; i++, mapping++, map_entry++) {
280 		mapping->base = map_entry->InputBase;
281 		/*
282 		 * IdCount means "The number of IDs in the range minus one" (ARM DEN 0049D).
283 		 * We use <= for comparison against this field, so don't add one here.
284 		 */
285 		mapping->end = map_entry->InputBase + map_entry->IdCount;
286 		mapping->outbase = map_entry->OutputBase;
287 		mapping->out_node_offset = map_entry->OutputReference;
288 		mapping->flags = map_entry->Flags;
289 		mapping->out_node = NULL;
290 	}
291 }
292 
293 /*
294  * Allocate and copy an ITS group.
295  */
296 static void
297 iort_copy_its(struct iort_node *node, ACPI_IORT_NODE *node_entry)
298 {
299 	struct iort_its_entry *its;
300 	ACPI_IORT_ITS_GROUP *itsg_entry;
301 	UINT32 *id;
302 	int i;
303 
304 	itsg_entry = (ACPI_IORT_ITS_GROUP *)node_entry->NodeData;
305 	node->nentries = itsg_entry->ItsCount;
306 	node->usecount = 0;
307 	its = malloc(sizeof(*its) * node->nentries, M_DEVBUF, M_WAITOK | M_ZERO);
308 	node->entries.its = its;
309 	id = &itsg_entry->Identifiers[0];
310 	for (i = 0; i < node->nentries; i++, its++, id++) {
311 		its->its_id = *id;
312 		its->pxm = -1;
313 		its->xref = 0;
314 	}
315 }
316 
317 /*
318  * Walk the IORT table and add nodes to corresponding list.
319  */
320 static void
321 iort_add_nodes(ACPI_IORT_NODE *node_entry, u_int node_offset)
322 {
323 	ACPI_IORT_ROOT_COMPLEX *pci_rc;
324 	ACPI_IORT_SMMU *smmu;
325 	ACPI_IORT_SMMU_V3 *smmu_v3;
326 	ACPI_IORT_NAMED_COMPONENT *named_comp;
327 	struct iort_node *node;
328 
329 	node = malloc(sizeof(*node), M_DEVBUF, M_WAITOK | M_ZERO);
330 	node->type =  node_entry->Type;
331 	node->node_offset = node_offset;
332 	node->revision = node_entry->Revision;
333 
334 	/* copy nodes depending on type */
335 	switch(node_entry->Type) {
336 	case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
337 		pci_rc = (ACPI_IORT_ROOT_COMPLEX *)node_entry->NodeData;
338 		memcpy(&node->data.pci_rc, pci_rc, sizeof(*pci_rc));
339 		iort_copy_data(node, node_entry);
340 		TAILQ_INSERT_TAIL(&pci_nodes, node, next);
341 		break;
342 	case ACPI_IORT_NODE_SMMU:
343 		smmu = (ACPI_IORT_SMMU *)node_entry->NodeData;
344 		memcpy(&node->data.smmu, smmu, sizeof(*smmu));
345 		iort_copy_data(node, node_entry);
346 		TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
347 		break;
348 	case ACPI_IORT_NODE_SMMU_V3:
349 		smmu_v3 = (ACPI_IORT_SMMU_V3 *)node_entry->NodeData;
350 		memcpy(&node->data.smmu_v3, smmu_v3, sizeof(*smmu_v3));
351 		iort_copy_data(node, node_entry);
352 		TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
353 		break;
354 	case ACPI_IORT_NODE_ITS_GROUP:
355 		iort_copy_its(node, node_entry);
356 		TAILQ_INSERT_TAIL(&its_groups, node, next);
357 		break;
358 	case ACPI_IORT_NODE_NAMED_COMPONENT:
359 		named_comp = (ACPI_IORT_NAMED_COMPONENT *)node_entry->NodeData;
360 		memcpy(&node->data.named_comp, named_comp, sizeof(*named_comp));
361 
362 		/* Copy name of the node separately. */
363 		strncpy(node->data.named_comp.DeviceName,
364 		    named_comp->DeviceName,
365 		    sizeof(node->data.named_comp.DeviceName));
366 		node->data.named_comp.DeviceName[31] = 0;
367 
368 		iort_copy_data(node, node_entry);
369 		TAILQ_INSERT_TAIL(&named_nodes, node, next);
370 		break;
371 	default:
372 		printf("ACPI: IORT: Dropping unhandled type %u\n",
373 		    node_entry->Type);
374 		free(node, M_DEVBUF);
375 		break;
376 	}
377 }
378 
379 /*
380  * For the mapping entry given, walk thru all the possible destination
381  * nodes and resolve the output reference.
382  */
383 static void
384 iort_resolve_node(struct iort_map_entry *entry, int check_smmu)
385 {
386 	struct iort_node *node, *np;
387 
388 	node = NULL;
389 	if (check_smmu) {
390 		TAILQ_FOREACH(np, &smmu_nodes, next) {
391 			if (entry->out_node_offset == np->node_offset) {
392 				node = np;
393 				break;
394 			}
395 		}
396 	}
397 	if (node == NULL) {
398 		TAILQ_FOREACH(np, &its_groups, next) {
399 			if (entry->out_node_offset == np->node_offset) {
400 				node = np;
401 				break;
402 			}
403 		}
404 	}
405 	if (node != NULL) {
406 		node->usecount++;
407 		entry->out_node = node;
408 	} else {
409 		printf("ACPI: IORT: Firmware Bug: no mapping for node %u\n",
410 		    entry->out_node_offset);
411 	}
412 }
413 
414 /*
415  * Resolve all output node references to node pointers.
416  */
417 static void
418 iort_post_process_mappings(void)
419 {
420 	struct iort_node *node;
421 	int i;
422 
423 	TAILQ_FOREACH(node, &pci_nodes, next)
424 		for (i = 0; i < node->nentries; i++)
425 			iort_resolve_node(&node->entries.mappings[i], TRUE);
426 	TAILQ_FOREACH(node, &smmu_nodes, next)
427 		for (i = 0; i < node->nentries; i++)
428 			iort_resolve_node(&node->entries.mappings[i], FALSE);
429 	TAILQ_FOREACH(node, &named_nodes, next)
430 		for (i = 0; i < node->nentries; i++)
431 			iort_resolve_node(&node->entries.mappings[i], TRUE);
432 }
433 
434 /*
435  * Walk MADT table, assign PIC xrefs to all ITS entries.
436  */
437 static void
438 madt_resolve_its_xref(ACPI_SUBTABLE_HEADER *entry, void *arg)
439 {
440 	ACPI_MADT_GENERIC_TRANSLATOR *gict;
441 	struct iort_node *its_node;
442 	struct iort_its_entry *its_entry;
443 	u_int xref;
444 	int i, matches;
445 
446         if (entry->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
447 		return;
448 
449 	gict = (ACPI_MADT_GENERIC_TRANSLATOR *)entry;
450 	matches = 0;
451 	xref = acpi_its_xref++;
452 	TAILQ_FOREACH(its_node, &its_groups, next) {
453 		its_entry = its_node->entries.its;
454 		for (i = 0; i < its_node->nentries; i++, its_entry++) {
455 			if (its_entry->its_id == gict->TranslationId) {
456 				its_entry->xref = xref;
457 				matches++;
458 			}
459 		}
460 	}
461 	if (matches == 0)
462 		printf("ACPI: IORT: Unused ITS block, ID %u\n",
463 		    gict->TranslationId);
464 }
465 
466 /*
467  * Walk SRAT, assign proximity to all ITS entries.
468  */
469 static void
470 srat_resolve_its_pxm(ACPI_SUBTABLE_HEADER *entry, void *arg)
471 {
472 	ACPI_SRAT_GIC_ITS_AFFINITY *gicits;
473 	struct iort_node *its_node;
474 	struct iort_its_entry *its_entry;
475 	int *map_counts;
476 	int i, matches, dom;
477 
478 	if (entry->Type != ACPI_SRAT_TYPE_GIC_ITS_AFFINITY)
479 		return;
480 
481 	matches = 0;
482 	map_counts = arg;
483 	gicits = (ACPI_SRAT_GIC_ITS_AFFINITY *)entry;
484 	dom = acpi_map_pxm_to_vm_domainid(gicits->ProximityDomain);
485 
486 	/*
487 	 * Catch firmware and config errors. map_counts keeps a
488 	 * count of ProximityDomain values mapping to a domain ID
489 	 */
490 #if MAXMEMDOM > 1
491 	if (dom == -1)
492 		printf("Firmware Error: Proximity Domain %d could not be"
493 		    " mapped for GIC ITS ID %d!\n",
494 		    gicits->ProximityDomain, gicits->ItsId);
495 #endif
496 	/* use dom + 1 as index to handle the case where dom == -1 */
497 	i = ++map_counts[dom + 1];
498 	if (i > 1) {
499 #ifdef NUMA
500 		if (dom != -1)
501 			printf("ERROR: Multiple Proximity Domains map to the"
502 			    " same NUMA domain %d!\n", dom);
503 #else
504 		printf("WARNING: multiple Proximity Domains in SRAT but NUMA"
505 		    " NOT enabled!\n");
506 #endif
507 	}
508 	TAILQ_FOREACH(its_node, &its_groups, next) {
509 		its_entry = its_node->entries.its;
510 		for (i = 0; i < its_node->nentries; i++, its_entry++) {
511 			if (its_entry->its_id == gicits->ItsId) {
512 				its_entry->pxm = dom;
513 				matches++;
514 			}
515 		}
516 	}
517 	if (matches == 0)
518 		printf("ACPI: IORT: ITS block %u in SRAT not found in IORT!\n",
519 		    gicits->ItsId);
520 }
521 
522 /*
523  * Cross check the ITS Id with MADT and (if available) SRAT.
524  */
525 static int
526 iort_post_process_its(void)
527 {
528 	ACPI_TABLE_MADT *madt;
529 	ACPI_TABLE_SRAT *srat;
530 	vm_paddr_t madt_pa, srat_pa;
531 	int map_counts[MAXMEMDOM + 1] = { 0 };
532 
533 	/* Check ITS block in MADT */
534 	madt_pa = acpi_find_table(ACPI_SIG_MADT);
535 	KASSERT(madt_pa != 0, ("no MADT!"));
536 	madt = acpi_map_table(madt_pa, ACPI_SIG_MADT);
537 	KASSERT(madt != NULL, ("can't map MADT!"));
538 	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
539 	    madt_resolve_its_xref, NULL);
540 	acpi_unmap_table(madt);
541 
542 	/* Get proximtiy if available */
543 	srat_pa = acpi_find_table(ACPI_SIG_SRAT);
544 	if (srat_pa != 0) {
545 		srat = acpi_map_table(srat_pa, ACPI_SIG_SRAT);
546 		KASSERT(srat != NULL, ("can't map SRAT!"));
547 		acpi_walk_subtables(srat + 1, (char *)srat + srat->Header.Length,
548 		    srat_resolve_its_pxm, map_counts);
549 		acpi_unmap_table(srat);
550 	}
551 	return (0);
552 }
553 
554 /*
555  * Find, parse, and save IO Remapping Table ("IORT").
556  */
557 static int
558 acpi_parse_iort(void *dummy __unused)
559 {
560 	ACPI_TABLE_IORT *iort;
561 	ACPI_IORT_NODE *node_entry;
562 	vm_paddr_t iort_pa;
563 	u_int node_offset;
564 
565 	iort_pa = acpi_find_table(ACPI_SIG_IORT);
566 	if (iort_pa == 0)
567 		return (ENXIO);
568 
569 	iort = acpi_map_table(iort_pa, ACPI_SIG_IORT);
570 	if (iort == NULL) {
571 		printf("ACPI: Unable to map the IORT table!\n");
572 		return (ENXIO);
573 	}
574 	for (node_offset = iort->NodeOffset;
575 	    node_offset < iort->Header.Length;
576 	    node_offset += node_entry->Length) {
577 		node_entry = ACPI_ADD_PTR(ACPI_IORT_NODE, iort, node_offset);
578 		iort_add_nodes(node_entry, node_offset);
579 	}
580 	acpi_unmap_table(iort);
581 	iort_post_process_mappings();
582 	iort_post_process_its();
583 	return (0);
584 }
585 SYSINIT(acpi_parse_iort, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_parse_iort, NULL);
586 
587 /*
588  * Provide ITS ID to PIC xref mapping.
589  */
590 int
591 acpi_iort_its_lookup(u_int its_id, u_int *xref, int *pxm)
592 {
593 	struct iort_node *its_node;
594 	struct iort_its_entry *its_entry;
595 	int i;
596 
597 	TAILQ_FOREACH(its_node, &its_groups, next) {
598 		its_entry = its_node->entries.its;
599 		for  (i = 0; i < its_node->nentries; i++, its_entry++) {
600 			if (its_entry->its_id == its_id) {
601 				*xref = its_entry->xref;
602 				*pxm = its_entry->pxm;
603 				return (0);
604 			}
605 		}
606 	}
607 	return (ENOENT);
608 }
609 
610 /*
611  * Find mapping for a PCIe device given segment and device ID
612  * returns the XREF for MSI interrupt setup and the device ID to
613  * use for the interrupt setup
614  */
615 int
616 acpi_iort_map_pci_msi(u_int seg, u_int rid, u_int *xref, u_int *devid)
617 {
618 	struct iort_node *node;
619 
620 	node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_ITS_GROUP, devid);
621 	if (node == NULL)
622 		return (ENOENT);
623 
624 	/* This should be an ITS node */
625 	KASSERT(node->type == ACPI_IORT_NODE_ITS_GROUP, ("bad group"));
626 
627 	/* return first node, we don't handle more than that now. */
628 	*xref = node->entries.its[0].xref;
629 	return (0);
630 }
631 
632 int
633 acpi_iort_map_pci_smmuv3(u_int seg, u_int rid, u_int *xref, u_int *sid)
634 {
635 	ACPI_IORT_SMMU_V3 *smmu;
636 	struct iort_node *node;
637 
638 	node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_SMMU_V3, sid);
639 	if (node == NULL)
640 		return (ENOENT);
641 
642 	/* This should be an SMMU node. */
643 	KASSERT(node->type == ACPI_IORT_NODE_SMMU_V3, ("bad node"));
644 
645 	smmu = (ACPI_IORT_SMMU_V3 *)&node->data.smmu_v3;
646 	*xref = smmu->BaseAddress;
647 
648 	return (0);
649 }
650 
651 /*
652  * Finds mapping for a named node given name and resource ID and returns the
653  * XREF for MSI interrupt setup and the device ID to use for the interrupt setup.
654  */
655 int
656 acpi_iort_map_named_msi(const char *devname, u_int rid, u_int *xref,
657     u_int *devid)
658 {
659 	struct iort_node *node;
660 
661 	node = iort_named_comp_map(devname, rid, ACPI_IORT_NODE_ITS_GROUP,
662 	    devid);
663 	if (node == NULL)
664 		return (ENOENT);
665 
666 	/* This should be an ITS node */
667 	KASSERT(node->type == ACPI_IORT_NODE_ITS_GROUP, ("bad group"));
668 
669 	/* Return first node, we don't handle more than that now. */
670 	*xref = node->entries.its[0].xref;
671 	return (0);
672 }
673 
674 int
675 acpi_iort_map_named_smmuv3(const char *devname, u_int rid, u_int *xref,
676     u_int *devid)
677 {
678 	ACPI_IORT_SMMU_V3 *smmu;
679 	struct iort_node *node;
680 
681 	node = iort_named_comp_map(devname, rid, ACPI_IORT_NODE_SMMU_V3, devid);
682 	if (node == NULL)
683 		return (ENOENT);
684 
685 	/* This should be an SMMU node. */
686 	KASSERT(node->type == ACPI_IORT_NODE_SMMU_V3, ("bad node"));
687 
688 	smmu = (ACPI_IORT_SMMU_V3 *)&node->data.smmu_v3;
689 	*xref = smmu->BaseAddress;
690 
691 	return (0);
692 }
693