11558Srgrimes/*-
21558Srgrimes * SPDX-License-Identifier: BSD-2-Clause
31558Srgrimes *
41558Srgrimes * Copyright (C) 2018 Marvell International Ltd.
51558Srgrimes *
61558Srgrimes * Author: Jayachandran C Nair <jchandra@freebsd.org>
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30114589Sobrien#include "opt_acpi.h"
311558Srgrimes
3238040Scharnier#include <sys/param.h>
331558Srgrimes#include <sys/bus.h>
341558Srgrimes#include <sys/kernel.h>
351558Srgrimes#include <sys/malloc.h>
361558Srgrimes
371558Srgrimes#include <machine/intr.h>
381558Srgrimes
39114589Sobrien#include <contrib/dev/acpica/include/acpi.h>
4038040Scharnier#include <contrib/dev/acpica/include/accommon.h>
41114589Sobrien#include <contrib/dev/acpica/include/actables.h>
42114589Sobrien
431558Srgrimes#include <dev/acpica/acpivar.h>
441558Srgrimes
45102231Strhodes/*
461558Srgrimes * Track next XREF available for ITS groups.
471558Srgrimes */
4842873Sluoqistatic u_int acpi_its_xref = ACPI_MSI_XREF;
4996478Sphk
501558Srgrimes/*
511558Srgrimes * Some types of IORT nodes have a set of mappings.  Each of them map
5298542Smckusick * a range of device IDs [base..end] from the current node to another
5398542Smckusick * node. The corresponding device IDs on destination node starts at
541558Srgrimes * outbase.
55207141Sjeff */
561558Srgrimesstruct iort_map_entry {
57110174Sgordon	u_int			base;
581558Srgrimes	u_int			end;
591558Srgrimes	u_int			outbase;
601558Srgrimes	u_int			flags;
61109597Sjmallett	u_int			out_node_offset;
6238040Scharnier	struct iort_node	*out_node;
631558Srgrimes};
641558Srgrimes
65207141Sjeff/*
6658047Ssheldonh * The ITS group node does not have any outgoing mappings. It has a
67226266Smckusick * of a list of GIC ITS blocks which can handle the device ID. We
681558Srgrimes * will store the PIC XREF used by the block and the blocks proximity
691558Srgrimes * data here, so that it can be retrieved together.
701558Srgrimes */
711558Srgrimesstruct iort_its_entry {
721558Srgrimes	u_int			its_id;
73227081Sed	u_int			xref;
74109597Sjmallett	int			pxm;
751558Srgrimes};
7692883Simp
7792883Simpstruct iort_named_component
78207141Sjeff{
79207141Sjeff	UINT32                  NodeFlags;
80207141Sjeff	UINT64                  MemoryProperties;
811558Srgrimes	UINT8                   MemoryAddressLimit;
821558Srgrimes	char                    DeviceName[32]; /* Path of namespace object */
83109597Sjmallett};
841558Srgrimes
85207141Sjeff/*
86216798Skib * IORT node. Each node has some device specific data depending on the
87109963Sjmallett * type of the node. The node can also have a set of mappings, OR in
8879750Sdd * case of ITS group nodes a set of ITS entries.
89127455Sbde * The nodes are kept in a TAILQ by type.
90207141Sjeff */
91207141Sjeffstruct iort_node {
92216798Skib	TAILQ_ENTRY(iort_node)	next;		/* next entry with same type */
93207141Sjeff	enum AcpiIortNodeType	type;		/* ACPI type */
94127441Sbde	u_int			node_offset;	/* offset in IORT - node ID */
9579750Sdd	u_int			nentries;	/* items in array below */
9642873Sluoqi	u_int			usecount;	/* for bookkeeping */
9742873Sluoqi	u_int			revision;	/* node revision */
981558Srgrimes	union {
99127441Sbde		struct iort_map_entry	*mappings;	/* node mappings  */
100127441Sbde		struct iort_its_entry	*its;		/* ITS IDs array */
101207141Sjeff	} entries;
102216798Skib	union {
103207141Sjeff		ACPI_IORT_ROOT_COMPLEX		pci_rc;	/* PCI root complex */
104207141Sjeff		ACPI_IORT_SMMU			smmu;
105127455Sbde		ACPI_IORT_SMMU_V3		smmu_v3;
106127441Sbde		struct iort_named_component	named_comp;
107216798Skib	} data;
108216798Skib};
109127441Sbde
110127441Sbde/* Lists for each of the types. */
111127441Sbdestatic TAILQ_HEAD(, iort_node) pci_nodes = TAILQ_HEAD_INITIALIZER(pci_nodes);
112127441Sbdestatic TAILQ_HEAD(, iort_node) smmu_nodes = TAILQ_HEAD_INITIALIZER(smmu_nodes);
113127441Sbdestatic TAILQ_HEAD(, iort_node) its_groups = TAILQ_HEAD_INITIALIZER(its_groups);
114127441Sbdestatic TAILQ_HEAD(, iort_node) named_nodes = TAILQ_HEAD_INITIALIZER(named_nodes);
115127441Sbde
116127441Sbdestatic int
117127441Sbdeiort_entry_get_id_mapping_index(struct iort_node *node)
118200796Strasz{
119127441Sbde
120127441Sbde	switch(node->type) {
121127441Sbde	case ACPI_IORT_NODE_SMMU_V3:
122127441Sbde		/* The ID mapping field was added in version 1 */
123127441Sbde		if (node->revision < 1)
124127441Sbde			return (-1);
125127441Sbde
126127441Sbde		/*
127127441Sbde		 * If all the control interrupts are GISCV based the ID
128127441Sbde		 * mapping field is ignored.
129127441Sbde		 */
130127441Sbde		if (node->data.smmu_v3.EventGsiv != 0 &&
131127441Sbde		    node->data.smmu_v3.PriGsiv != 0 &&
132127441Sbde		    node->data.smmu_v3.GerrGsiv != 0 &&
133127441Sbde		    node->data.smmu_v3.SyncGsiv != 0)
134127441Sbde			return (-1);
135127441Sbde
136127441Sbde		if (node->data.smmu_v3.IdMappingIndex >= node->nentries)
137127441Sbde			return (-1);
138127441Sbde
139127441Sbde		return (node->data.smmu_v3.IdMappingIndex);
140127441Sbde	case ACPI_IORT_NODE_PMCG:
141127441Sbde		return (0);
142127441Sbde	default:
143127441Sbde		break;
144127441Sbde	}
145127441Sbde
146127441Sbde	return (-1);
147127441Sbde}
148207141Sjeff
149207141Sjeff/*
150207141Sjeff * Lookup an ID in the mappings array. If successful, map the input ID
151207141Sjeff * to the output ID and return the output node found.
152207141Sjeff */
153207141Sjeffstatic struct iort_node *
154207141Sjeffiort_entry_lookup(struct iort_node *node, u_int id, u_int *outid)
155207141Sjeff{
156207141Sjeff	struct iort_map_entry *entry;
157207141Sjeff	int i, id_map;
158207141Sjeff
159207141Sjeff	id_map = iort_entry_get_id_mapping_index(node);
160163842Spjd	entry = node->entries.mappings;
161163842Spjd	for (i = 0; i < node->nentries; i++, entry++) {
162163842Spjd		if (i == id_map)
163163842Spjd			continue;
164163842Spjd		if (entry->base <= id && id <= entry->end)
165163842Spjd			break;
166163842Spjd	}
167163842Spjd	if (i == node->nentries)
168163842Spjd		return (NULL);
169163842Spjd	if ((entry->flags & ACPI_IORT_ID_SINGLE_MAPPING) == 0)
170163842Spjd		*outid = entry->outbase + (id - entry->base);
171163842Spjd	else
172163842Spjd		*outid = entry->outbase;
173127441Sbde	return (entry->out_node);
174127441Sbde}
175127441Sbde
176127441Sbde/*
177127441Sbde * Perform an additional lookup in case of SMMU node and ITS outtype.
178127441Sbde */
179127441Sbdestatic struct iort_node *
180127441Sbdeiort_smmu_trymap(struct iort_node *node, u_int outtype, u_int *outid)
181127441Sbde{
182127441Sbde	/* Original node can be not found. */
183127441Sbde	if (!node)
184127441Sbde		return (NULL);
185127441Sbde
186127441Sbde	/* Node can be SMMU or ITS. If SMMU, we need another lookup. */
187127441Sbde	if (outtype == ACPI_IORT_NODE_ITS_GROUP &&
188127441Sbde	    (node->type == ACPI_IORT_NODE_SMMU_V3 ||
189127441Sbde	     node->type == ACPI_IORT_NODE_SMMU)) {
190127441Sbde		node = iort_entry_lookup(node, *outid, outid);
191127441Sbde		if (node == NULL)
192127441Sbde			return (NULL);
193127441Sbde	}
194127441Sbde
195127441Sbde	KASSERT(node->type == outtype, ("mapping fail"));
196127441Sbde	return (node);
197127441Sbde}
198127441Sbde
199127441Sbde/*
200127441Sbde * Map a PCI RID to a SMMU node or an ITS node, based on outtype.
201127441Sbde */
202127441Sbdestatic struct iort_node *
203127441Sbdeiort_pci_rc_map(u_int seg, u_int rid, u_int outtype, u_int *outid)
204127441Sbde{
205127441Sbde	struct iort_node *node, *out_node;
206127441Sbde	u_int nxtid;
207127441Sbde
208127441Sbde	out_node = NULL;
209127441Sbde	TAILQ_FOREACH(node, &pci_nodes, next) {
210127441Sbde		if (node->data.pci_rc.PciSegmentNumber != seg)
211127441Sbde			continue;
212200796Strasz		out_node = iort_entry_lookup(node, rid, &nxtid);
213200796Strasz		if (out_node != NULL)
214200796Strasz			break;
215200796Strasz	}
216200796Strasz
217200796Strasz	out_node = iort_smmu_trymap(out_node, outtype, &nxtid);
218200796Strasz	if (out_node)
219200796Strasz		*outid = nxtid;
220200796Strasz
221200796Strasz	return (out_node);
222200796Strasz}
223200796Strasz
224127441Sbde/*
225127441Sbde * Map a named component node to a SMMU node or an ITS node, based on outtype.
226127441Sbde */
227127441Sbdestatic struct iort_node *
228127455Sbdeiort_named_comp_map(const char *devname, u_int rid, u_int outtype, u_int *outid)
229127455Sbde{
230127441Sbde	struct iort_node *node, *out_node;
231127441Sbde	u_int nxtid;
232127441Sbde
233127441Sbde	out_node = NULL;
234127441Sbde	TAILQ_FOREACH(node, &named_nodes, next) {
235127441Sbde		if (strstr(node->data.named_comp.DeviceName, devname) == NULL)
236127441Sbde			continue;
237127441Sbde		out_node = iort_entry_lookup(node, rid, &nxtid);
238127441Sbde		if (out_node != NULL)
239127455Sbde			break;
240127441Sbde	}
241127455Sbde
242127441Sbde	out_node = iort_smmu_trymap(out_node, outtype, &nxtid);
243127441Sbde	if (out_node)
244127441Sbde		*outid = nxtid;
245127441Sbde
246127441Sbde	return (out_node);
247127441Sbde}
248127441Sbde
249127441Sbde#ifdef notyet
250127441Sbde/*
251127441Sbde * Not implemented, map a PCIe device to the SMMU it is associated with.
252127441Sbde */
253127441Sbdeint
254127441Sbdeacpi_iort_map_smmu(u_int seg, u_int devid, void **smmu, u_int *sid)
255127441Sbde{
256127441Sbde	/* XXX: convert oref to SMMU device */
257127441Sbde	return (ENXIO);
258127441Sbde}
259127441Sbde#endif
260127441Sbde
261127441Sbde/*
262127441Sbde * Allocate memory for a node, initialize and copy mappings. 'start'
263127441Sbde * argument provides the table start used to calculate the node offset.
264127441Sbde */
265207141Sjeffstatic void
266207141Sjeffiort_copy_data(struct iort_node *node, ACPI_IORT_NODE *node_entry)
267207141Sjeff{
268207141Sjeff	ACPI_IORT_ID_MAPPING *map_entry;
269207141Sjeff	struct iort_map_entry *mapping;
270207141Sjeff	int i;
271207141Sjeff
272207141Sjeff	map_entry = ACPI_ADD_PTR(ACPI_IORT_ID_MAPPING, node_entry,
273207141Sjeff	    node_entry->MappingOffset);
274207141Sjeff	node->nentries = node_entry->MappingCount;
275216798Skib	node->usecount = 0;
276216798Skib	mapping = malloc(sizeof(*mapping) * node->nentries, M_DEVBUF,
277216798Skib	    M_WAITOK | M_ZERO);
278216798Skib	node->entries.mappings = mapping;
279216798Skib	for (i = 0; i < node->nentries; i++, mapping++, map_entry++) {
280216798Skib		mapping->base = map_entry->InputBase;
281216798Skib		/*
282216798Skib		 * IdCount means "The number of IDs in the range minus one" (ARM DEN 0049D).
283216798Skib		 * We use <= for comparison against this field, so don't add one here.
284216798Skib		 */
285216798Skib		mapping->end = map_entry->InputBase + map_entry->IdCount;
286216798Skib		mapping->outbase = map_entry->OutputBase;
287127441Sbde		mapping->out_node_offset = map_entry->OutputReference;
288127441Sbde		mapping->flags = map_entry->Flags;
289105120Srwatson		mapping->out_node = NULL;
29069314Scharnier	}
29169314Scharnier}
29269314Scharnier
293127441Sbde/*
29469314Scharnier * Allocate and copy an ITS group.
295109963Sjmallett */
296109963Sjmallettstatic void
297109963Sjmallettiort_copy_its(struct iort_node *node, ACPI_IORT_NODE *node_entry)
298109963Sjmallett{
299207421Sjeff	struct iort_its_entry *its;
300207421Sjeff	ACPI_IORT_ITS_GROUP *itsg_entry;
301207421Sjeff	UINT32 *id;
30269829Scharnier	int i;
30369829Scharnier
30469829Scharnier	itsg_entry = (ACPI_IORT_ITS_GROUP *)node_entry->NodeData;
30569829Scharnier	node->nentries = itsg_entry->ItsCount;
30669829Scharnier	node->usecount = 0;
30769829Scharnier	its = malloc(sizeof(*its) * node->nentries, M_DEVBUF, M_WAITOK | M_ZERO);
30869829Scharnier	node->entries.its = its;
309110174Sgordon	id = &itsg_entry->Identifiers[0];
310110174Sgordon	for (i = 0; i < node->nentries; i++, its++, id++) {
311110174Sgordon		its->its_id = *id;
312110174Sgordon		its->pxm = -1;
313105120Srwatson		its->xref = 0;
314200796Strasz	}
315105120Srwatson}
316105120Srwatson
317105120Srwatson/*
318200796Strasz * Walk the IORT table and add nodes to corresponding list.
319200796Strasz */
320200796Straszstatic void
321105120Srwatsoniort_add_nodes(ACPI_IORT_NODE *node_entry, u_int node_offset)
322105120Srwatson{
323105120Srwatson	ACPI_IORT_ROOT_COMPLEX *pci_rc;
324105120Srwatson	ACPI_IORT_SMMU *smmu;
325105120Srwatson	ACPI_IORT_SMMU_V3 *smmu_v3;
326105120Srwatson	ACPI_IORT_NAMED_COMPONENT *named_comp;
327105120Srwatson	struct iort_node *node;
328105120Srwatson
329105120Srwatson	node = malloc(sizeof(*node), M_DEVBUF, M_WAITOK | M_ZERO);
330105120Srwatson	node->type =  node_entry->Type;
331105120Srwatson	node->node_offset = node_offset;
332105206Srwatson	node->revision = node_entry->Revision;
333105120Srwatson
334105120Srwatson	/* copy nodes depending on type */
335105120Srwatson	switch(node_entry->Type) {
33669829Scharnier	case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
33769829Scharnier		pci_rc = (ACPI_IORT_ROOT_COMPLEX *)node_entry->NodeData;
338127455Sbde		memcpy(&node->data.pci_rc, pci_rc, sizeof(*pci_rc));
33969829Scharnier		iort_copy_data(node, node_entry);
34069829Scharnier		TAILQ_INSERT_TAIL(&pci_nodes, node, next);
34169829Scharnier		break;
342127455Sbde	case ACPI_IORT_NODE_SMMU:
34369829Scharnier		smmu = (ACPI_IORT_SMMU *)node_entry->NodeData;
34469829Scharnier		memcpy(&node->data.smmu, smmu, sizeof(*smmu));
34569829Scharnier		iort_copy_data(node, node_entry);
34675377Smckusick		TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
34775377Smckusick		break;
348203769Smckusick	case ACPI_IORT_NODE_SMMU_V3:
34975377Smckusick		smmu_v3 = (ACPI_IORT_SMMU_V3 *)node_entry->NodeData;
35075377Smckusick		memcpy(&node->data.smmu_v3, smmu_v3, sizeof(*smmu_v3));
35175377Smckusick		iort_copy_data(node, node_entry);
35275377Smckusick		TAILQ_INSERT_TAIL(&smmu_nodes, node, next);
35375377Smckusick		break;
35475377Smckusick	case ACPI_IORT_NODE_ITS_GROUP:
35575377Smckusick		iort_copy_its(node, node_entry);
35675377Smckusick		TAILQ_INSERT_TAIL(&its_groups, node, next);
357207141Sjeff		break;
358207141Sjeff	case ACPI_IORT_NODE_NAMED_COMPONENT:
359207141Sjeff		named_comp = (ACPI_IORT_NAMED_COMPONENT *)node_entry->NodeData;
360207141Sjeff		memcpy(&node->data.named_comp, named_comp, sizeof(*named_comp));
361207141Sjeff
362207141Sjeff		/* Copy name of the node separately. */
363207141Sjeff		strncpy(node->data.named_comp.DeviceName,
364207141Sjeff		    named_comp->DeviceName,
365207141Sjeff		    sizeof(node->data.named_comp.DeviceName));
366207141Sjeff		node->data.named_comp.DeviceName[31] = 0;
367207141Sjeff
368207141Sjeff		iort_copy_data(node, node_entry);
369207141Sjeff		TAILQ_INSERT_TAIL(&named_nodes, node, next);
370207141Sjeff		break;
371207141Sjeff	default:
372207141Sjeff		printf("ACPI: IORT: Dropping unhandled type %u\n",
373207141Sjeff		    node_entry->Type);
374207141Sjeff		free(node, M_DEVBUF);
375207141Sjeff		break;
376207141Sjeff	}
377208241Sjeff}
378207141Sjeff
379208241Sjeff/*
380208241Sjeff * For the mapping entry given, walk thru all the possible destination
381208241Sjeff * nodes and resolve the output reference.
382208241Sjeff */
383207141Sjeffstatic void
384207141Sjeffiort_resolve_node(struct iort_map_entry *entry, int check_smmu)
385207141Sjeff{
386163842Spjd	struct iort_node *node, *np;
387163842Spjd
388163842Spjd	node = NULL;
389163842Spjd	if (check_smmu) {
390163842Spjd		TAILQ_FOREACH(np, &smmu_nodes, next) {
391163842Spjd			if (entry->out_node_offset == np->node_offset) {
392163842Spjd				node = np;
393163842Spjd				break;
394163842Spjd			}
395163842Spjd		}
396163842Spjd	}
397163842Spjd	if (node == NULL) {
398163842Spjd		TAILQ_FOREACH(np, &its_groups, next) {
399163842Spjd			if (entry->out_node_offset == np->node_offset) {
400163842Spjd				node = np;
401163842Spjd				break;
402163842Spjd			}
403163842Spjd		}
404163842Spjd	}
405163842Spjd	if (node != NULL) {
406105120Srwatson		node->usecount++;
407105120Srwatson		entry->out_node = node;
408105120Srwatson	} else {
409105120Srwatson		printf("ACPI: IORT: Firmware Bug: no mapping for node %u\n",
410105120Srwatson		    entry->out_node_offset);
411105120Srwatson	}
412105120Srwatson}
413105120Srwatson
414105120Srwatson/*
415105120Srwatson * Resolve all output node references to node pointers.
416105120Srwatson */
417105120Srwatsonstatic void
418105120Srwatsoniort_post_process_mappings(void)
419105120Srwatson{
420105120Srwatson	struct iort_node *node;
421105120Srwatson	int i;
422105206Srwatson
423105120Srwatson	TAILQ_FOREACH(node, &pci_nodes, next)
424105120Srwatson		for (i = 0; i < node->nentries; i++)
425105120Srwatson			iort_resolve_node(&node->entries.mappings[i], TRUE);
42669829Scharnier	TAILQ_FOREACH(node, &smmu_nodes, next)
42769829Scharnier		for (i = 0; i < node->nentries; i++)
428127455Sbde			iort_resolve_node(&node->entries.mappings[i], FALSE);
42969829Scharnier	TAILQ_FOREACH(node, &named_nodes, next)
43069829Scharnier		for (i = 0; i < node->nentries; i++)
43169829Scharnier			iort_resolve_node(&node->entries.mappings[i], TRUE);
43269829Scharnier}
43369829Scharnier
43469829Scharnier/*
43569829Scharnier * Walk MADT table, assign PIC xrefs to all ITS entries.
43669829Scharnier */
43769829Scharnierstatic void
43869829Scharniermadt_resolve_its_xref(ACPI_SUBTABLE_HEADER *entry, void *arg)
43969829Scharnier{
440200796Strasz	ACPI_MADT_GENERIC_TRANSLATOR *gict;
441200796Strasz	struct iort_node *its_node;
442200796Strasz	struct iort_its_entry *its_entry;
443200796Strasz	u_int xref;
444200796Strasz	int i, matches;
445200796Strasz
446200796Strasz        if (entry->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
447200796Strasz		return;
448200796Strasz
449200796Strasz	gict = (ACPI_MADT_GENERIC_TRANSLATOR *)entry;
450200796Strasz	matches = 0;
451200796Strasz	xref = acpi_its_xref++;
452200796Strasz	TAILQ_FOREACH(its_node, &its_groups, next) {
453200796Strasz		its_entry = its_node->entries.its;
454200796Strasz		for (i = 0; i < its_node->nentries; i++, its_entry++) {
455200796Strasz			if (its_entry->its_id == gict->TranslationId) {
456200796Strasz				its_entry->xref = xref;
457200796Strasz				matches++;
458200796Strasz			}
459200796Strasz		}
460200796Strasz	}
461200796Strasz	if (matches == 0)
462200796Strasz		printf("ACPI: IORT: Unused ITS block, ID %u\n",
46369829Scharnier		    gict->TranslationId);
46469829Scharnier}
46569829Scharnier
466127455Sbde/*
46769829Scharnier * Walk SRAT, assign proximity to all ITS entries.
468127455Sbde */
46975498Smckusickstatic void
47075498Smckusicksrat_resolve_its_pxm(ACPI_SUBTABLE_HEADER *entry, void *arg)
47169829Scharnier{
47269829Scharnier	ACPI_SRAT_GIC_ITS_AFFINITY *gicits;
47369829Scharnier	struct iort_node *its_node;
47469829Scharnier	struct iort_its_entry *its_entry;
47569829Scharnier	int *map_counts;
476127455Sbde	int i, matches, dom;
47769829Scharnier
478127455Sbde	if (entry->Type != ACPI_SRAT_TYPE_GIC_ITS_AFFINITY)
47969829Scharnier		return;
48069829Scharnier
48169829Scharnier	matches = 0;
48269829Scharnier	map_counts = arg;
48369829Scharnier	gicits = (ACPI_SRAT_GIC_ITS_AFFINITY *)entry;
48469829Scharnier	dom = acpi_map_pxm_to_vm_domainid(gicits->ProximityDomain);
48569829Scharnier
48669829Scharnier	/*
48769829Scharnier	 * Catch firmware and config errors. map_counts keeps a
488127455Sbde	 * count of ProximityDomain values mapping to a domain ID
48969829Scharnier	 */
49069829Scharnier#if MAXMEMDOM > 1
49169829Scharnier	if (dom == -1)
49269829Scharnier		printf("Firmware Error: Proximity Domain %d could not be"
49369829Scharnier		    " mapped for GIC ITS ID %d!\n",
49469829Scharnier		    gicits->ProximityDomain, gicits->ItsId);
495127455Sbde#endif
49669829Scharnier	/* use dom + 1 as index to handle the case where dom == -1 */
497127455Sbde	i = ++map_counts[dom + 1];
49869829Scharnier	if (i > 1) {
49969829Scharnier#ifdef NUMA
50069829Scharnier		if (dom != -1)
50175377Smckusick			printf("ERROR: Multiple Proximity Domains map to the"
50275377Smckusick			    " same NUMA domain %d!\n", dom);
503203769Smckusick#else
50475377Smckusick		printf("WARNING: multiple Proximity Domains in SRAT but NUMA"
50575377Smckusick		    " NOT enabled!\n");
50675377Smckusick#endif
50775377Smckusick	}
50875377Smckusick	TAILQ_FOREACH(its_node, &its_groups, next) {
50975377Smckusick		its_entry = its_node->entries.its;
51075377Smckusick		for (i = 0; i < its_node->nentries; i++, its_entry++) {
51175377Smckusick			if (its_entry->its_id == gicits->ItsId) {
512216798Skib				its_entry->pxm = dom;
513216798Skib				matches++;
514216798Skib			}
515216798Skib		}
516216798Skib	}
517216798Skib	if (matches == 0)
518216798Skib		printf("ACPI: IORT: ITS block %u in SRAT not found in IORT!\n",
519216798Skib		    gicits->ItsId);
520216798Skib}
521216798Skib
522216798Skib/*
523216798Skib * Cross check the ITS Id with MADT and (if available) SRAT.
524216798Skib */
525216798Skibstatic int
526216798Skibiort_post_process_its(void)
527216798Skib{
528216798Skib	ACPI_TABLE_MADT *madt;
529216798Skib	ACPI_TABLE_SRAT *srat;
53069829Scharnier	vm_paddr_t madt_pa, srat_pa;
531109963Sjmallett	int map_counts[MAXMEMDOM + 1] = { 0 };
532109963Sjmallett
533109963Sjmallett	/* Check ITS block in MADT */
53442873Sluoqi	madt_pa = acpi_find_table(ACPI_SIG_MADT);
53542873Sluoqi	KASSERT(madt_pa != 0, ("no MADT!"));
536109963Sjmallett	madt = acpi_map_table(madt_pa, ACPI_SIG_MADT);
53742873Sluoqi	KASSERT(madt != NULL, ("can't map MADT!"));
53842873Sluoqi	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
539102231Strhodes	    madt_resolve_its_xref, NULL);
54042873Sluoqi	acpi_unmap_table(madt);
5411558Srgrimes
542109963Sjmallett	/* Get proximtiy if available */
543109963Sjmallett	srat_pa = acpi_find_table(ACPI_SIG_SRAT);
544109963Sjmallett	if (srat_pa != 0) {
545109963Sjmallett		srat = acpi_map_table(srat_pa, ACPI_SIG_SRAT);
546109963Sjmallett		KASSERT(srat != NULL, ("can't map SRAT!"));
5471558Srgrimes		acpi_walk_subtables(srat + 1, (char *)srat + srat->Header.Length,
5481558Srgrimes		    srat_resolve_its_pxm, map_counts);
5491558Srgrimes		acpi_unmap_table(srat);
550207141Sjeff	}
551207141Sjeff	return (0);
552207141Sjeff}
553207141Sjeff
554207141Sjeff/*
555207141Sjeff * Find, parse, and save IO Remapping Table ("IORT").
556227081Sed */
557207141Sjeffstatic int
558207141Sjeffacpi_parse_iort(void *dummy __unused)
559207141Sjeff{
560207141Sjeff	ACPI_TABLE_IORT *iort;
561207141Sjeff	ACPI_IORT_NODE *node_entry;
562207141Sjeff	vm_paddr_t iort_pa;
563207141Sjeff	u_int node_offset;
564207141Sjeff
565207141Sjeff	iort_pa = acpi_find_table(ACPI_SIG_IORT);
566207141Sjeff	if (iort_pa == 0)
567207141Sjeff		return (ENXIO);
568207141Sjeff
569207141Sjeff	iort = acpi_map_table(iort_pa, ACPI_SIG_IORT);
570207141Sjeff	if (iort == NULL) {
571207141Sjeff		printf("ACPI: Unable to map the IORT table!\n");
572207141Sjeff		return (ENXIO);
573207141Sjeff	}
574207141Sjeff	for (node_offset = iort->NodeOffset;
575207141Sjeff	    node_offset < iort->Header.Length;
576207141Sjeff	    node_offset += node_entry->Length) {
577207141Sjeff		node_entry = ACPI_ADD_PTR(ACPI_IORT_NODE, iort, node_offset);
578207141Sjeff		iort_add_nodes(node_entry, node_offset);
579207141Sjeff	}
580207141Sjeff	acpi_unmap_table(iort);
581207141Sjeff	iort_post_process_mappings();
582207141Sjeff	iort_post_process_its();
583207141Sjeff	return (0);
584207141Sjeff}
585208241SjeffSYSINIT(acpi_parse_iort, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_parse_iort, NULL);
586207141Sjeff
587207141Sjeff/*
588207141Sjeff * Provide ITS ID to PIC xref mapping.
589207141Sjeff */
590207141Sjeffint
591207141Sjeffacpi_iort_its_lookup(u_int its_id, u_int *xref, int *pxm)
592207141Sjeff{
593207141Sjeff	struct iort_node *its_node;
594207141Sjeff	struct iort_its_entry *its_entry;
595207141Sjeff	int i;
596207141Sjeff
597207141Sjeff	TAILQ_FOREACH(its_node, &its_groups, next) {
598207141Sjeff		its_entry = its_node->entries.its;
599207141Sjeff		for  (i = 0; i < its_node->nentries; i++, its_entry++) {
600207141Sjeff			if (its_entry->its_id == its_id) {
601207141Sjeff				*xref = its_entry->xref;
602207141Sjeff				*pxm = its_entry->pxm;
603207141Sjeff				return (0);
604207141Sjeff			}
605207141Sjeff		}
606207141Sjeff	}
607207141Sjeff	return (ENOENT);
608207141Sjeff}
609207141Sjeff
610207141Sjeff/*
611207141Sjeff * Find mapping for a PCIe device given segment and device ID
612207141Sjeff * returns the XREF for MSI interrupt setup and the device ID to
613207141Sjeff * use for the interrupt setup
614207141Sjeff */
615207141Sjeffint
616207141Sjeffacpi_iort_map_pci_msi(u_int seg, u_int rid, u_int *xref, u_int *devid)
617207141Sjeff{
618207141Sjeff	struct iort_node *node;
619207141Sjeff
620207141Sjeff	node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_ITS_GROUP, devid);
621207141Sjeff	if (node == NULL)
622207141Sjeff		return (ENOENT);
623207141Sjeff
624207141Sjeff	/* This should be an ITS node */
625207141Sjeff	KASSERT(node->type == ACPI_IORT_NODE_ITS_GROUP, ("bad group"));
626207141Sjeff
627207141Sjeff	/* return first node, we don't handle more than that now. */
628207141Sjeff	*xref = node->entries.its[0].xref;
629207141Sjeff	return (0);
630207141Sjeff}
631207141Sjeff
632207141Sjeffint
633207141Sjeffacpi_iort_map_pci_smmuv3(u_int seg, u_int rid, u_int *xref, u_int *sid)
634207141Sjeff{
635207141Sjeff	ACPI_IORT_SMMU_V3 *smmu;
636207141Sjeff	struct iort_node *node;
637207141Sjeff
638207141Sjeff	node = iort_pci_rc_map(seg, rid, ACPI_IORT_NODE_SMMU_V3, sid);
639207141Sjeff	if (node == NULL)
640207141Sjeff		return (ENOENT);
641207141Sjeff
642207141Sjeff	/* This should be an SMMU node. */
643207141Sjeff	KASSERT(node->type == ACPI_IORT_NODE_SMMU_V3, ("bad node"));
644207141Sjeff
645207141Sjeff	smmu = (ACPI_IORT_SMMU_V3 *)&node->data.smmu_v3;
646207141Sjeff	*xref = smmu->BaseAddress;
647207141Sjeff
648207141Sjeff	return (0);
649207141Sjeff}
650207141Sjeff
651207141Sjeff/*
652207141Sjeff * Finds mapping for a named node given name and resource ID and returns the
653207141Sjeff * XREF for MSI interrupt setup and the device ID to use for the interrupt setup.
654207141Sjeff */
655207141Sjeffint
656207141Sjeffacpi_iort_map_named_msi(const char *devname, u_int rid, u_int *xref,
657207141Sjeff    u_int *devid)
658207141Sjeff{
659207141Sjeff	struct iort_node *node;
660207141Sjeff
661207141Sjeff	node = iort_named_comp_map(devname, rid, ACPI_IORT_NODE_ITS_GROUP,
662207141Sjeff	    devid);
663207141Sjeff	if (node == NULL)
664207141Sjeff		return (ENOENT);
665207141Sjeff
666207141Sjeff	/* This should be an ITS node */
667207141Sjeff	KASSERT(node->type == ACPI_IORT_NODE_ITS_GROUP, ("bad group"));
668207141Sjeff
669207141Sjeff	/* Return first node, we don't handle more than that now. */
670207141Sjeff	*xref = node->entries.its[0].xref;
671207141Sjeff	return (0);
672207141Sjeff}
673207141Sjeff
674207141Sjeffint
675207141Sjeffacpi_iort_map_named_smmuv3(const char *devname, u_int rid, u_int *xref,
676207141Sjeff    u_int *devid)
677207141Sjeff{
678207141Sjeff	ACPI_IORT_SMMU_V3 *smmu;
679207141Sjeff	struct iort_node *node;
680207141Sjeff
681207141Sjeff	node = iort_named_comp_map(devname, rid, ACPI_IORT_NODE_SMMU_V3, devid);
682207141Sjeff	if (node == NULL)
683207141Sjeff		return (ENOENT);
684207141Sjeff
685207141Sjeff	/* This should be an SMMU node. */
686207141Sjeff	KASSERT(node->type == ACPI_IORT_NODE_SMMU_V3, ("bad node"));
687207141Sjeff
688207141Sjeff	smmu = (ACPI_IORT_SMMU_V3 *)&node->data.smmu_v3;
689207141Sjeff	*xref = smmu->BaseAddress;
690207141Sjeff
691207141Sjeff	return (0);
692218603Skib}
693218603Skib