xref: /freebsd/sys/arm64/acpica/acpi_iort.c (revision a0b9e2e854027e6ff61fb075a1309dbc71c42b54)
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 /*
81  * IORT node. Each node has some device specific data depending on the
82  * type of the node. The node can also have a set of mappings, OR in
83  * case of ITS group nodes a set of ITS entries.
84  * The nodes are kept in a TAILQ by type.
85  */
86 struct iort_node {
87 	TAILQ_ENTRY(iort_node)	next;		/* next entry with same type */
88 	enum AcpiIortNodeType	type;		/* ACPI type */
89 	u_int			node_offset;	/* offset in IORT - node ID */
90 	u_int			nentries;	/* items in array below */
91 	u_int			usecount;	/* for bookkeeping */
92 	u_int			revision;	/* node revision */
93 	union {
94 		ACPI_IORT_ROOT_COMPLEX	pci_rc;		/* PCI root complex */
95 		ACPI_IORT_SMMU		smmu;
96 		ACPI_IORT_SMMU_V3	smmu_v3;
97 	} data;
98 	union {
99 		struct iort_map_entry	*mappings;	/* node mappings  */
100 		struct iort_its_entry	*its;		/* ITS IDs array */
101 	} entries;
102 };
103 
104 /* Lists for each of the types. */
105 static TAILQ_HEAD(, iort_node) pci_nodes = TAILQ_HEAD_INITIALIZER(pci_nodes);
106 static TAILQ_HEAD(, iort_node) smmu_nodes = TAILQ_HEAD_INITIALIZER(smmu_nodes);
107 static TAILQ_HEAD(, iort_node) its_groups = TAILQ_HEAD_INITIALIZER(its_groups);
108 
109 static int
110 iort_entry_get_id_mapping_index(struct iort_node *node)
111 {
112 
113 	switch(node->type) {
114 	case ACPI_IORT_NODE_SMMU_V3:
115 		/* The ID mapping field was added in version 1 */
116 		if (node->revision < 1)
117 			return (-1);
118 
119 		/*
120 		 * If all the control interrupts are GISCV based the ID
121 		 * mapping field is ignored.
122 		 */
123 		if (node->data.smmu_v3.EventGsiv != 0 &&
124 		    node->data.smmu_v3.PriGsiv != 0 &&
125 		    node->data.smmu_v3.GerrGsiv != 0 &&
126 		    node->data.smmu_v3.SyncGsiv != 0)
127 			return (-1);
128 
129 		if (node->data.smmu_v3.IdMappingIndex >= node->nentries)
130 			return (-1);
131 
132 		return (node->data.smmu_v3.IdMappingIndex);
133 	case ACPI_IORT_NODE_PMCG:
134 		return (0);
135 	default:
136 		break;
137 	}
138 
139 	return (-1);
140 }
141 
142 /*
143  * Lookup an ID in the mappings array. If successful, map the input ID
144  * to the output ID and return the output node found.
145  */
146 static struct iort_node *
147 iort_entry_lookup(struct iort_node *node, u_int id, u_int *outid)
148 {
149 	struct iort_map_entry *entry;
150 	int i, id_map;
151 
152 	id_map = iort_entry_get_id_mapping_index(node);
153 	entry = node->entries.mappings;
154 	for (i = 0; i < node->nentries; i++, entry++) {
155 		if (i == id_map)
156 			continue;
157 		if (entry->base <= id && id <= entry->end)
158 			break;
159 	}
160 	if (i == node->nentries)
161 		return (NULL);
162 	if ((entry->flags & ACPI_IORT_ID_SINGLE_MAPPING) == 0)
163 		*outid = entry->outbase + (id - entry->base);
164 	else
165 		*outid = entry->outbase;
166 	return (entry->out_node);
167 }
168 
169 /*
170  * Map a PCI RID to a SMMU node or an ITS node, based on outtype.
171  */
172 static struct iort_node *
173 iort_pci_rc_map(u_int seg, u_int rid, u_int outtype, u_int *outid)
174 {
175 	struct iort_node *node, *out_node;
176 	u_int nxtid;
177 
178 	out_node = NULL;
179 	TAILQ_FOREACH(node, &pci_nodes, next) {
180 		if (node->data.pci_rc.PciSegmentNumber != seg)
181 			continue;
182 		out_node = iort_entry_lookup(node, rid, &nxtid);
183 		if (out_node != NULL)
184 			break;
185 	}
186 
187 	/* Could not find a PCI RC node with segment and device ID. */
188 	if (out_node == NULL)
189 		return (NULL);
190 
191 	/* Node can be SMMU or ITS. If SMMU, we need another lookup. */
192 	if (outtype == ACPI_IORT_NODE_ITS_GROUP &&
193 	    (out_node->type == ACPI_IORT_NODE_SMMU_V3 ||
194 	    out_node->type == ACPI_IORT_NODE_SMMU)) {
195 		out_node = iort_entry_lookup(out_node, nxtid, &nxtid);
196 		if (out_node == NULL)
197 			return (NULL);
198 	}
199 
200 	KASSERT(out_node->type == outtype, ("mapping fail"));
201 	*outid = nxtid;
202 	return (out_node);
203 }
204 
205 #ifdef notyet
206 /*
207  * Not implemented, map a PCIe device to the SMMU it is associated with.
208  */
209 int
210 acpi_iort_map_smmu(u_int seg, u_int devid, void **smmu, u_int *sid)
211 {
212 	/* XXX: convert oref to SMMU device */
213 	return (ENXIO);
214 }
215 #endif
216 
217 /*
218  * Allocate memory for a node, initialize and copy mappings. 'start'
219  * argument provides the table start used to calculate the node offset.
220  */
221 static void
222 iort_copy_data(struct iort_node *node, ACPI_IORT_NODE *node_entry)
223 {
224 	ACPI_IORT_ID_MAPPING *map_entry;
225 	struct iort_map_entry *mapping;
226 	int i;
227 
228 	map_entry = ACPI_ADD_PTR(ACPI_IORT_ID_MAPPING, node_entry,
229 	    node_entry->MappingOffset);
230 	node->nentries = node_entry->MappingCount;
231 	node->usecount = 0;
232 	mapping = malloc(sizeof(*mapping) * node->nentries, M_DEVBUF,
233 	    M_WAITOK | M_ZERO);
234 	node->entries.mappings = mapping;
235 	for (i = 0; i < node->nentries; i++, mapping++, map_entry++) {
236 		mapping->base = map_entry->InputBase;
237 		/*
238 		 * IdCount means "The number of IDs in the range minus one" (ARM DEN 0049D).
239 		 * We use <= for comparison against this field, so don't add one here.
240 		 */
241 		mapping->end = map_entry->InputBase + map_entry->IdCount;
242 		mapping->outbase = map_entry->OutputBase;
243 		mapping->out_node_offset = map_entry->OutputReference;
244 		mapping->flags = map_entry->Flags;
245 		mapping->out_node = NULL;
246 	}
247 }
248 
249 /*
250  * Allocate and copy an ITS group.
251  */
252 static void
253 iort_copy_its(struct iort_node *node, ACPI_IORT_NODE *node_entry)
254 {
255 	struct iort_its_entry *its;
256 	ACPI_IORT_ITS_GROUP *itsg_entry;
257 	UINT32 *id;
258 	int i;
259 
260 	itsg_entry = (ACPI_IORT_ITS_GROUP *)node_entry->NodeData;
261 	node->nentries = itsg_entry->ItsCount;
262 	node->usecount = 0;
263 	its = malloc(sizeof(*its) * node->nentries, M_DEVBUF, M_WAITOK | M_ZERO);
264 	node->entries.its = its;
265 	id = &itsg_entry->Identifiers[0];
266 	for (i = 0; i < node->nentries; i++, its++, id++) {
267 		its->its_id = *id;
268 		its->pxm = -1;
269 		its->xref = 0;
270 	}
271 }
272 
273 /*
274  * Walk the IORT table and add nodes to corresponding list.
275  */
276 static void
277 iort_add_nodes(ACPI_IORT_NODE *node_entry, u_int node_offset)
278 {
279 	ACPI_IORT_ROOT_COMPLEX *pci_rc;
280 	ACPI_IORT_SMMU *smmu;
281 	ACPI_IORT_SMMU_V3 *smmu_v3;
282 	struct iort_node *node;
283 
284 	node = malloc(sizeof(*node), M_DEVBUF, M_WAITOK | M_ZERO);
285 	node->type =  node_entry->Type;
286 	node->node_offset = node_offset;
287 	node->revision = node_entry->Revision;
288 
289 	/* copy nodes depending on type */
290 	switch(node_entry->Type) {
291 	case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
292 		pci_rc = (ACPI_IORT_ROOT_COMPLEX *)node_entry->NodeData;
293 		memcpy(&node->data.pci_rc, pci_rc, sizeof(*pci_rc));
294 		iort_copy_data(node, node_entry);
295 		TAILQ_INSERT_TAIL(&pci_nodes, node, next);
296 		break;
297 	case ACPI_IORT_NODE_SMMU:
298 		smmu = (ACPI_IORT_SMMU *)node_entry->NodeData;
299 		memcpy(&node->data.smmu, smmu, sizeof(*smmu));
300 		iort_copy_data(node, node_entry);
301 		TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
302 		break;
303 	case ACPI_IORT_NODE_SMMU_V3:
304 		smmu_v3 = (ACPI_IORT_SMMU_V3 *)node_entry->NodeData;
305 		memcpy(&node->data.smmu_v3, smmu_v3, sizeof(*smmu_v3));
306 		iort_copy_data(node, node_entry);
307 		TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
308 		break;
309 	case ACPI_IORT_NODE_ITS_GROUP:
310 		iort_copy_its(node, node_entry);
311 		TAILQ_INSERT_TAIL(&its_groups, node, next);
312 		break;
313 	default:
314 		printf("ACPI: IORT: Dropping unhandled type %u\n",
315 		    node_entry->Type);
316 		free(node, M_DEVBUF);
317 		break;
318 	}
319 }
320 
321 /*
322  * For the mapping entry given, walk thru all the possible destination
323  * nodes and resolve the output reference.
324  */
325 static void
326 iort_resolve_node(struct iort_map_entry *entry, int check_smmu)
327 {
328 	struct iort_node *node, *np;
329 
330 	node = NULL;
331 	if (check_smmu) {
332 		TAILQ_FOREACH(np, &smmu_nodes, next) {
333 			if (entry->out_node_offset == np->node_offset) {
334 				node = np;
335 				break;
336 			}
337 		}
338 	}
339 	if (node == NULL) {
340 		TAILQ_FOREACH(np, &its_groups, next) {
341 			if (entry->out_node_offset == np->node_offset) {
342 				node = np;
343 				break;
344 			}
345 		}
346 	}
347 	if (node != NULL) {
348 		node->usecount++;
349 		entry->out_node = node;
350 	} else {
351 		printf("ACPI: IORT: Firmware Bug: no mapping for node %u\n",
352 		    entry->out_node_offset);
353 	}
354 }
355 
356 /*
357  * Resolve all output node references to node pointers.
358  */
359 static void
360 iort_post_process_mappings(void)
361 {
362 	struct iort_node *node;
363 	int i;
364 
365 	TAILQ_FOREACH(node, &pci_nodes, next)
366 		for (i = 0; i < node->nentries; i++)
367 			iort_resolve_node(&node->entries.mappings[i], TRUE);
368 	TAILQ_FOREACH(node, &smmu_nodes, next)
369 		for (i = 0; i < node->nentries; i++)
370 			iort_resolve_node(&node->entries.mappings[i], FALSE);
371 	/* TODO: named nodes */
372 }
373 
374 /*
375  * Walk MADT table, assign PIC xrefs to all ITS entries.
376  */
377 static void
378 madt_resolve_its_xref(ACPI_SUBTABLE_HEADER *entry, void *arg)
379 {
380 	ACPI_MADT_GENERIC_TRANSLATOR *gict;
381 	struct iort_node *its_node;
382 	struct iort_its_entry *its_entry;
383 	u_int xref;
384 	int i, matches;
385 
386         if (entry->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
387 		return;
388 
389 	gict = (ACPI_MADT_GENERIC_TRANSLATOR *)entry;
390 	matches = 0;
391 	xref = acpi_its_xref++;
392 	TAILQ_FOREACH(its_node, &its_groups, next) {
393 		its_entry = its_node->entries.its;
394 		for (i = 0; i < its_node->nentries; i++, its_entry++) {
395 			if (its_entry->its_id == gict->TranslationId) {
396 				its_entry->xref = xref;
397 				matches++;
398 			}
399 		}
400 	}
401 	if (matches == 0)
402 		printf("ACPI: IORT: Unused ITS block, ID %u\n",
403 		    gict->TranslationId);
404 }
405 
406 /*
407  * Walk SRAT, assign proximity to all ITS entries.
408  */
409 static void
410 srat_resolve_its_pxm(ACPI_SUBTABLE_HEADER *entry, void *arg)
411 {
412 	ACPI_SRAT_GIC_ITS_AFFINITY *gicits;
413 	struct iort_node *its_node;
414 	struct iort_its_entry *its_entry;
415 	int *map_counts;
416 	int i, matches, dom;
417 
418 	if (entry->Type != ACPI_SRAT_TYPE_GIC_ITS_AFFINITY)
419 		return;
420 
421 	matches = 0;
422 	map_counts = arg;
423 	gicits = (ACPI_SRAT_GIC_ITS_AFFINITY *)entry;
424 	dom = acpi_map_pxm_to_vm_domainid(gicits->ProximityDomain);
425 
426 	/*
427 	 * Catch firmware and config errors. map_counts keeps a
428 	 * count of ProximityDomain values mapping to a domain ID
429 	 */
430 #if MAXMEMDOM > 1
431 	if (dom == -1)
432 		printf("Firmware Error: Proximity Domain %d could not be"
433 		    " mapped for GIC ITS ID %d!\n",
434 		    gicits->ProximityDomain, gicits->ItsId);
435 #endif
436 	/* use dom + 1 as index to handle the case where dom == -1 */
437 	i = ++map_counts[dom + 1];
438 	if (i > 1) {
439 #ifdef NUMA
440 		if (dom != -1)
441 			printf("ERROR: Multiple Proximity Domains map to the"
442 			    " same NUMA domain %d!\n", dom);
443 #else
444 		printf("WARNING: multiple Proximity Domains in SRAT but NUMA"
445 		    " NOT enabled!\n");
446 #endif
447 	}
448 	TAILQ_FOREACH(its_node, &its_groups, next) {
449 		its_entry = its_node->entries.its;
450 		for (i = 0; i < its_node->nentries; i++, its_entry++) {
451 			if (its_entry->its_id == gicits->ItsId) {
452 				its_entry->pxm = dom;
453 				matches++;
454 			}
455 		}
456 	}
457 	if (matches == 0)
458 		printf("ACPI: IORT: ITS block %u in SRAT not found in IORT!\n",
459 		    gicits->ItsId);
460 }
461 
462 /*
463  * Cross check the ITS Id with MADT and (if available) SRAT.
464  */
465 static int
466 iort_post_process_its(void)
467 {
468 	ACPI_TABLE_MADT *madt;
469 	ACPI_TABLE_SRAT *srat;
470 	vm_paddr_t madt_pa, srat_pa;
471 	int map_counts[MAXMEMDOM + 1] = { 0 };
472 
473 	/* Check ITS block in MADT */
474 	madt_pa = acpi_find_table(ACPI_SIG_MADT);
475 	KASSERT(madt_pa != 0, ("no MADT!"));
476 	madt = acpi_map_table(madt_pa, ACPI_SIG_MADT);
477 	KASSERT(madt != NULL, ("can't map MADT!"));
478 	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
479 	    madt_resolve_its_xref, NULL);
480 	acpi_unmap_table(madt);
481 
482 	/* Get proximtiy if available */
483 	srat_pa = acpi_find_table(ACPI_SIG_SRAT);
484 	if (srat_pa != 0) {
485 		srat = acpi_map_table(srat_pa, ACPI_SIG_SRAT);
486 		KASSERT(srat != NULL, ("can't map SRAT!"));
487 		acpi_walk_subtables(srat + 1, (char *)srat + srat->Header.Length,
488 		    srat_resolve_its_pxm, map_counts);
489 		acpi_unmap_table(srat);
490 	}
491 	return (0);
492 }
493 
494 /*
495  * Find, parse, and save IO Remapping Table ("IORT").
496  */
497 static int
498 acpi_parse_iort(void *dummy __unused)
499 {
500 	ACPI_TABLE_IORT *iort;
501 	ACPI_IORT_NODE *node_entry;
502 	vm_paddr_t iort_pa;
503 	u_int node_offset;
504 
505 	iort_pa = acpi_find_table(ACPI_SIG_IORT);
506 	if (iort_pa == 0)
507 		return (ENXIO);
508 
509 	iort = acpi_map_table(iort_pa, ACPI_SIG_IORT);
510 	if (iort == NULL) {
511 		printf("ACPI: Unable to map the IORT table!\n");
512 		return (ENXIO);
513 	}
514 	for (node_offset = iort->NodeOffset;
515 	    node_offset < iort->Header.Length;
516 	    node_offset += node_entry->Length) {
517 		node_entry = ACPI_ADD_PTR(ACPI_IORT_NODE, iort, node_offset);
518 		iort_add_nodes(node_entry, node_offset);
519 	}
520 	acpi_unmap_table(iort);
521 	iort_post_process_mappings();
522 	iort_post_process_its();
523 	return (0);
524 }
525 SYSINIT(acpi_parse_iort, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_parse_iort, NULL);
526 
527 /*
528  * Provide ITS ID to PIC xref mapping.
529  */
530 int
531 acpi_iort_its_lookup(u_int its_id, u_int *xref, int *pxm)
532 {
533 	struct iort_node *its_node;
534 	struct iort_its_entry *its_entry;
535 	int i;
536 
537 	TAILQ_FOREACH(its_node, &its_groups, next) {
538 		its_entry = its_node->entries.its;
539 		for  (i = 0; i < its_node->nentries; i++, its_entry++) {
540 			if (its_entry->its_id == its_id) {
541 				*xref = its_entry->xref;
542 				*pxm = its_entry->pxm;
543 				return (0);
544 			}
545 		}
546 	}
547 	return (ENOENT);
548 }
549 
550 /*
551  * Find mapping for a PCIe device given segment and device ID
552  * returns the XREF for MSI interrupt setup and the device ID to
553  * use for the interrupt setup
554  */
555 int
556 acpi_iort_map_pci_msi(u_int seg, u_int rid, u_int *xref, u_int *devid)
557 {
558 	struct iort_node *node;
559 
560 	node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_ITS_GROUP, devid);
561 	if (node == NULL)
562 		return (ENOENT);
563 
564 	/* This should be an ITS node */
565 	KASSERT(node->type == ACPI_IORT_NODE_ITS_GROUP, ("bad group"));
566 
567 	/* return first node, we don't handle more than that now. */
568 	*xref = node->entries.its[0].xref;
569 	return (0);
570 }
571 
572 int
573 acpi_iort_map_pci_smmuv3(u_int seg, u_int rid, u_int *xref, u_int *sid)
574 {
575 	ACPI_IORT_SMMU_V3 *smmu;
576 	struct iort_node *node;
577 
578 	node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_SMMU_V3, sid);
579 	if (node == NULL)
580 		return (ENOENT);
581 
582 	/* This should be an SMMU node. */
583 	KASSERT(node->type == ACPI_IORT_NODE_SMMU_V3, ("bad node"));
584 
585 	smmu = (ACPI_IORT_SMMU_V3 *)&node->data.smmu_v3;
586 	*xref = smmu->BaseAddress;
587 
588 	return (0);
589 }
590