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