2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (C) 2018 Marvell International Ltd.
6 * Author: Jayachandran C Nair <jchandra@freebsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
40 #include <machine/intr.h>
42 #include <contrib/dev/acpica/include/acpi.h>
43 #include <contrib/dev/acpica/include/accommon.h>
44 #include <contrib/dev/acpica/include/actables.h>
46 #include <dev/acpica/acpivar.h>
49 * Track next XREF available for ITS groups.
51 static u_int acpi_its_xref = ACPI_MSI_XREF;
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
59 struct iort_map_entry {
64 u_int out_node_offset;
65 struct iort_node *out_node;
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.
74 struct iort_its_entry {
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.
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 */
93 ACPI_IORT_ROOT_COMPLEX pci_rc; /* PCI root complex */
95 ACPI_IORT_SMMU_V3 smmu_v3;
98 struct iort_map_entry *mappings; /* node mappings */
99 struct iort_its_entry *its; /* ITS IDs array */
103 /* Lists for each of the types. */
104 static TAILQ_HEAD(, iort_node) pci_nodes = TAILQ_HEAD_INITIALIZER(pci_nodes);
105 static TAILQ_HEAD(, iort_node) smmu_nodes = TAILQ_HEAD_INITIALIZER(smmu_nodes);
106 static TAILQ_HEAD(, iort_node) its_groups = TAILQ_HEAD_INITIALIZER(its_groups);
109 * Lookup an ID in the mappings array. If successful, map the input ID
110 * to the output ID and return the output node found.
112 static struct iort_node *
113 iort_entry_lookup(struct iort_node *node, u_int id, u_int *outid)
115 struct iort_map_entry *entry;
118 entry = node->entries.mappings;
119 for (i = 0; i < node->nentries; i++, entry++) {
120 if (entry->base <= id && id <= entry->end)
123 if (i == node->nentries)
125 if ((entry->flags & ACPI_IORT_ID_SINGLE_MAPPING) == 0)
126 *outid = entry->outbase + (id - entry->base);
128 *outid = entry->outbase;
129 return (entry->out_node);
133 * Map a PCI RID to a SMMU node or an ITS node, based on outtype.
135 static struct iort_node *
136 iort_pci_rc_map(u_int seg, u_int rid, u_int outtype, u_int *outid)
138 struct iort_node *node, *out_node;
142 TAILQ_FOREACH(node, &pci_nodes, next) {
143 if (node->data.pci_rc.PciSegmentNumber != seg)
145 out_node = iort_entry_lookup(node, rid, &nxtid);
146 if (out_node != NULL)
150 /* Could not find a PCI RC node with segment and device ID. */
151 if (out_node == NULL)
154 /* Node can be SMMU or ITS. If SMMU, we need another lookup. */
155 if (outtype == ACPI_IORT_NODE_ITS_GROUP &&
156 (out_node->type == ACPI_IORT_NODE_SMMU_V3 ||
157 out_node->type == ACPI_IORT_NODE_SMMU)) {
158 out_node = iort_entry_lookup(out_node, nxtid, &nxtid);
159 if (out_node == NULL)
163 KASSERT(out_node->type == outtype, ("mapping fail"));
170 * Not implemented, map a PCIe device to the SMMU it is associated with.
173 acpi_iort_map_smmu(u_int seg, u_int devid, void **smmu, u_int *sid)
175 /* XXX: convert oref to SMMU device */
181 * Allocate memory for a node, initialize and copy mappings. 'start'
182 * argument provides the table start used to calculate the node offset.
185 iort_copy_data(struct iort_node *node, ACPI_IORT_NODE *node_entry)
187 ACPI_IORT_ID_MAPPING *map_entry;
188 struct iort_map_entry *mapping;
191 map_entry = ACPI_ADD_PTR(ACPI_IORT_ID_MAPPING, node_entry,
192 node_entry->MappingOffset);
193 node->nentries = node_entry->MappingCount;
195 mapping = malloc(sizeof(*mapping) * node->nentries, M_DEVBUF,
197 node->entries.mappings = mapping;
198 for (i = 0; i < node->nentries; i++, mapping++, map_entry++) {
199 mapping->base = map_entry->InputBase;
200 mapping->end = map_entry->InputBase + map_entry->IdCount - 1;
201 mapping->outbase = map_entry->OutputBase;
202 mapping->out_node_offset = map_entry->OutputReference;
203 mapping->flags = map_entry->Flags;
204 mapping->out_node = NULL;
209 * Allocate and copy an ITS group.
212 iort_copy_its(struct iort_node *node, ACPI_IORT_NODE *node_entry)
214 struct iort_its_entry *its;
215 ACPI_IORT_ITS_GROUP *itsg_entry;
219 itsg_entry = (ACPI_IORT_ITS_GROUP *)node_entry->NodeData;
220 node->nentries = itsg_entry->ItsCount;
222 its = malloc(sizeof(*its) * node->nentries, M_DEVBUF, M_WAITOK | M_ZERO);
223 node->entries.its = its;
224 id = &itsg_entry->Identifiers[0];
225 for (i = 0; i < node->nentries; i++, its++, id++) {
233 * Walk the IORT table and add nodes to corresponding list.
236 iort_add_nodes(ACPI_IORT_NODE *node_entry, u_int node_offset)
238 ACPI_IORT_ROOT_COMPLEX *pci_rc;
239 ACPI_IORT_SMMU *smmu;
240 ACPI_IORT_SMMU_V3 *smmu_v3;
241 struct iort_node *node;
243 node = malloc(sizeof(*node), M_DEVBUF, M_WAITOK | M_ZERO);
244 node->type = node_entry->Type;
245 node->node_offset = node_offset;
247 /* copy nodes depending on type */
248 switch(node_entry->Type) {
249 case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
250 pci_rc = (ACPI_IORT_ROOT_COMPLEX *)node_entry->NodeData;
251 memcpy(&node->data.pci_rc, pci_rc, sizeof(*pci_rc));
252 iort_copy_data(node, node_entry);
253 TAILQ_INSERT_TAIL(&pci_nodes, node, next);
255 case ACPI_IORT_NODE_SMMU:
256 smmu = (ACPI_IORT_SMMU *)node_entry->NodeData;
257 memcpy(&node->data.smmu, smmu, sizeof(*smmu));
258 iort_copy_data(node, node_entry);
259 TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
261 case ACPI_IORT_NODE_SMMU_V3:
262 smmu_v3 = (ACPI_IORT_SMMU_V3 *)node_entry->NodeData;
263 memcpy(&node->data.smmu_v3, smmu_v3, sizeof(*smmu_v3));
264 iort_copy_data(node, node_entry);
265 TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
267 case ACPI_IORT_NODE_ITS_GROUP:
268 iort_copy_its(node, node_entry);
269 TAILQ_INSERT_TAIL(&its_groups, node, next);
272 printf("ACPI: IORT: Dropping unhandled type %u\n",
274 free(node, M_DEVBUF);
280 * For the mapping entry given, walk thru all the possible destination
281 * nodes and resolve the output reference.
284 iort_resolve_node(struct iort_map_entry *entry, int check_smmu)
286 struct iort_node *node, *np;
290 TAILQ_FOREACH(np, &smmu_nodes, next) {
291 if (entry->out_node_offset == np->node_offset) {
298 TAILQ_FOREACH(np, &its_groups, next) {
299 if (entry->out_node_offset == np->node_offset) {
307 entry->out_node = node;
309 printf("ACPI: IORT: Firmware Bug: no mapping for node %u\n",
310 entry->out_node_offset);
315 * Resolve all output node references to node pointers.
318 iort_post_process_mappings(void)
320 struct iort_node *node;
323 TAILQ_FOREACH(node, &pci_nodes, next)
324 for (i = 0; i < node->nentries; i++)
325 iort_resolve_node(&node->entries.mappings[i], TRUE);
326 TAILQ_FOREACH(node, &smmu_nodes, next)
327 for (i = 0; i < node->nentries; i++)
328 iort_resolve_node(&node->entries.mappings[i], FALSE);
329 /* TODO: named nodes */
333 * Walk MADT table, assign PIC xrefs to all ITS entries.
336 madt_resolve_its_xref(ACPI_SUBTABLE_HEADER *entry, void *arg)
338 ACPI_MADT_GENERIC_TRANSLATOR *gict;
339 struct iort_node *its_node;
340 struct iort_its_entry *its_entry;
344 if (entry->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
347 gict = (ACPI_MADT_GENERIC_TRANSLATOR *)entry;
349 xref = acpi_its_xref++;
350 TAILQ_FOREACH(its_node, &its_groups, next) {
351 its_entry = its_node->entries.its;
352 for (i = 0; i < its_node->nentries; i++, its_entry++) {
353 if (its_entry->its_id == gict->TranslationId) {
354 its_entry->xref = xref;
360 printf("ACPI: IORT: Unused ITS block, ID %u\n",
361 gict->TranslationId);
365 * Walk SRAT, assign proximity to all ITS entries.
368 srat_resolve_its_pxm(ACPI_SUBTABLE_HEADER *entry, void *arg)
370 ACPI_SRAT_GIC_ITS_AFFINITY *gicits;
371 struct iort_node *its_node;
372 struct iort_its_entry *its_entry;
376 if (entry->Type != ACPI_SRAT_TYPE_GIC_ITS_AFFINITY)
381 gicits = (ACPI_SRAT_GIC_ITS_AFFINITY *)entry;
382 dom = acpi_map_pxm_to_vm_domainid(gicits->ProximityDomain);
385 * Catch firmware and config errors. map_counts keeps a
386 * count of ProximityDomain values mapping to a domain ID
390 printf("Firmware Error: Proximity Domain %d could not be"
391 " mapped for GIC ITS ID %d!\n",
392 gicits->ProximityDomain, gicits->ItsId);
394 /* use dom + 1 as index to handle the case where dom == -1 */
395 i = ++map_counts[dom + 1];
399 printf("ERROR: Multiple Proximity Domains map to the"
400 " same NUMA domain %d!\n", dom);
402 printf("WARNING: multiple Proximity Domains in SRAT but NUMA"
406 TAILQ_FOREACH(its_node, &its_groups, next) {
407 its_entry = its_node->entries.its;
408 for (i = 0; i < its_node->nentries; i++, its_entry++) {
409 if (its_entry->its_id == gicits->ItsId) {
410 its_entry->pxm = dom;
416 printf("ACPI: IORT: ITS block %u in SRAT not found in IORT!\n",
421 * Cross check the ITS Id with MADT and (if available) SRAT.
424 iort_post_process_its(void)
426 ACPI_TABLE_MADT *madt;
427 ACPI_TABLE_SRAT *srat;
428 vm_paddr_t madt_pa, srat_pa;
429 int map_counts[MAXMEMDOM + 1] = { 0 };
431 /* Check ITS block in MADT */
432 madt_pa = acpi_find_table(ACPI_SIG_MADT);
433 KASSERT(madt_pa != 0, ("no MADT!"));
434 madt = acpi_map_table(madt_pa, ACPI_SIG_MADT);
435 KASSERT(madt != NULL, ("can't map MADT!"));
436 acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
437 madt_resolve_its_xref, NULL);
438 acpi_unmap_table(madt);
440 /* Get proximtiy if available */
441 srat_pa = acpi_find_table(ACPI_SIG_SRAT);
443 srat = acpi_map_table(srat_pa, ACPI_SIG_SRAT);
444 KASSERT(srat != NULL, ("can't map SRAT!"));
445 acpi_walk_subtables(srat + 1, (char *)srat + srat->Header.Length,
446 srat_resolve_its_pxm, map_counts);
447 acpi_unmap_table(srat);
453 * Find, parse, and save IO Remapping Table ("IORT").
456 acpi_parse_iort(void *dummy __unused)
458 ACPI_TABLE_IORT *iort;
459 ACPI_IORT_NODE *node_entry;
463 iort_pa = acpi_find_table(ACPI_SIG_IORT);
467 iort = acpi_map_table(iort_pa, ACPI_SIG_IORT);
469 printf("ACPI: Unable to map the IORT table!\n");
472 for (node_offset = iort->NodeOffset;
473 node_offset < iort->Header.Length;
474 node_offset += node_entry->Length) {
475 node_entry = ACPI_ADD_PTR(ACPI_IORT_NODE, iort, node_offset);
476 iort_add_nodes(node_entry, node_offset);
478 acpi_unmap_table(iort);
479 iort_post_process_mappings();
480 iort_post_process_its();
483 SYSINIT(acpi_parse_iort, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_parse_iort, NULL);
486 * Provide ITS ID to PIC xref mapping.
489 acpi_iort_its_lookup(u_int its_id, u_int *xref, int *pxm)
491 struct iort_node *its_node;
492 struct iort_its_entry *its_entry;
495 TAILQ_FOREACH(its_node, &its_groups, next) {
496 its_entry = its_node->entries.its;
497 for (i = 0; i < its_node->nentries; i++, its_entry++) {
498 if (its_entry->its_id == its_id) {
499 *xref = its_entry->xref;
500 *pxm = its_entry->pxm;
509 * Find mapping for a PCIe device given segment and device ID
510 * returns the XREF for MSI interrupt setup and the device ID to
511 * use for the interrupt setup
514 acpi_iort_map_pci_msi(u_int seg, u_int rid, u_int *xref, u_int *devid)
516 struct iort_node *node;
518 node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_ITS_GROUP, devid);
522 /* This should be an ITS node */
523 KASSERT(node->type == ACPI_IORT_NODE_ITS_GROUP, ("bad group"));
525 /* return first node, we don't handle more than that now. */
526 *xref = node->entries.its[0].xref;