xref: /netbsd-src/sys/dev/fdt/fdt_iommu.c (revision f509ff83368414361efd0231ad4f77b7f408b793)
1*f509ff83Sjmcneill /* $NetBSD: fdt_iommu.c,v 1.1 2021/09/04 12:34:39 jmcneill Exp $ */
2*f509ff83Sjmcneill 
3*f509ff83Sjmcneill /*-
4*f509ff83Sjmcneill  * Copyright (c) 2021 Jared McNeill <jmcneill@invisible.ca>
5*f509ff83Sjmcneill  * All rights reserved.
6*f509ff83Sjmcneill  *
7*f509ff83Sjmcneill  * Redistribution and use in source and binary forms, with or without
8*f509ff83Sjmcneill  * modification, are permitted provided that the following conditions
9*f509ff83Sjmcneill  * are met:
10*f509ff83Sjmcneill  * 1. Redistributions of source code must retain the above copyright
11*f509ff83Sjmcneill  *    notice, this list of conditions and the following disclaimer.
12*f509ff83Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13*f509ff83Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
14*f509ff83Sjmcneill  *    documentation and/or other materials provided with the distribution.
15*f509ff83Sjmcneill  *
16*f509ff83Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17*f509ff83Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*f509ff83Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19*f509ff83Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20*f509ff83Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*f509ff83Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22*f509ff83Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*f509ff83Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*f509ff83Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*f509ff83Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*f509ff83Sjmcneill  * SUCH DAMAGE.
27*f509ff83Sjmcneill  */
28*f509ff83Sjmcneill 
29*f509ff83Sjmcneill #include <sys/cdefs.h>
30*f509ff83Sjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_iommu.c,v 1.1 2021/09/04 12:34:39 jmcneill Exp $");
31*f509ff83Sjmcneill 
32*f509ff83Sjmcneill #include <sys/param.h>
33*f509ff83Sjmcneill #include <sys/bus.h>
34*f509ff83Sjmcneill #include <sys/kmem.h>
35*f509ff83Sjmcneill #include <sys/queue.h>
36*f509ff83Sjmcneill 
37*f509ff83Sjmcneill #include <libfdt.h>
38*f509ff83Sjmcneill #include <dev/fdt/fdtvar.h>
39*f509ff83Sjmcneill 
40*f509ff83Sjmcneill struct fdtbus_iommu {
41*f509ff83Sjmcneill 	device_t			iommu_dev;
42*f509ff83Sjmcneill 	int				iommu_phandle;
43*f509ff83Sjmcneill 	const struct fdtbus_iommu_func	*iommu_funcs;
44*f509ff83Sjmcneill 	u_int				iommu_cells;
45*f509ff83Sjmcneill 	LIST_ENTRY(fdtbus_iommu)	iommu_next;
46*f509ff83Sjmcneill };
47*f509ff83Sjmcneill 
48*f509ff83Sjmcneill static LIST_HEAD(, fdtbus_iommu) fdtbus_iommus =
49*f509ff83Sjmcneill     LIST_HEAD_INITIALIZER(fdtbus_iommus);
50*f509ff83Sjmcneill 
51*f509ff83Sjmcneill /*
52*f509ff83Sjmcneill  * fdtbus_get_iommu --
53*f509ff83Sjmcneill  *
54*f509ff83Sjmcneill  *	Return the iommu registered with the specified node, or NULL if
55*f509ff83Sjmcneill  *	not found.
56*f509ff83Sjmcneill  */
57*f509ff83Sjmcneill static struct fdtbus_iommu *
fdtbus_get_iommu(int phandle)58*f509ff83Sjmcneill fdtbus_get_iommu(int phandle)
59*f509ff83Sjmcneill {
60*f509ff83Sjmcneill 	struct fdtbus_iommu *iommu;
61*f509ff83Sjmcneill 
62*f509ff83Sjmcneill 	LIST_FOREACH(iommu, &fdtbus_iommus, iommu_next) {
63*f509ff83Sjmcneill 		if (iommu->iommu_phandle == phandle) {
64*f509ff83Sjmcneill 			return iommu;
65*f509ff83Sjmcneill 		}
66*f509ff83Sjmcneill 	}
67*f509ff83Sjmcneill 
68*f509ff83Sjmcneill 	return NULL;
69*f509ff83Sjmcneill }
70*f509ff83Sjmcneill 
71*f509ff83Sjmcneill /*
72*f509ff83Sjmcneill  * fdtbus_register_iommu --
73*f509ff83Sjmcneill  *
74*f509ff83Sjmcneill  *	Register an IOMMU on the specified node.
75*f509ff83Sjmcneill  */
76*f509ff83Sjmcneill int
fdtbus_register_iommu(device_t dev,int phandle,const struct fdtbus_iommu_func * funcs)77*f509ff83Sjmcneill fdtbus_register_iommu(device_t dev, int phandle,
78*f509ff83Sjmcneill     const struct fdtbus_iommu_func *funcs)
79*f509ff83Sjmcneill {
80*f509ff83Sjmcneill 	struct fdtbus_iommu *iommu;
81*f509ff83Sjmcneill 	u_int cells;
82*f509ff83Sjmcneill 
83*f509ff83Sjmcneill 	if (funcs == NULL || funcs->map == NULL) {
84*f509ff83Sjmcneill 		return EINVAL;
85*f509ff83Sjmcneill 	}
86*f509ff83Sjmcneill 
87*f509ff83Sjmcneill 	if (of_getprop_uint32(phandle, "#iommu-cells", &cells) != 0) {
88*f509ff83Sjmcneill 		return ENXIO;
89*f509ff83Sjmcneill 	}
90*f509ff83Sjmcneill 
91*f509ff83Sjmcneill 	if (fdtbus_get_iommu(phandle) != NULL) {
92*f509ff83Sjmcneill 		return EEXIST;
93*f509ff83Sjmcneill 	}
94*f509ff83Sjmcneill 
95*f509ff83Sjmcneill 	iommu = kmem_alloc(sizeof(*iommu), KM_SLEEP);
96*f509ff83Sjmcneill 	iommu->iommu_dev = dev;
97*f509ff83Sjmcneill 	iommu->iommu_phandle = phandle;
98*f509ff83Sjmcneill 	iommu->iommu_funcs = funcs;
99*f509ff83Sjmcneill 	iommu->iommu_cells = cells;
100*f509ff83Sjmcneill 
101*f509ff83Sjmcneill 	LIST_INSERT_HEAD(&fdtbus_iommus, iommu, iommu_next);
102*f509ff83Sjmcneill 
103*f509ff83Sjmcneill 	return 0;
104*f509ff83Sjmcneill }
105*f509ff83Sjmcneill 
106*f509ff83Sjmcneill /*
107*f509ff83Sjmcneill  * fdtbus_iommu_map --
108*f509ff83Sjmcneill  *
109*f509ff83Sjmcneill  *	Get a bus dma tag that is translated by any iommus specified by
110*f509ff83Sjmcneill  *	the device tree. The `index` property refers to the N-th item
111*f509ff83Sjmcneill  *	in the list of IOMMUs specified in the "iommus" property. If an
112*f509ff83Sjmcneill  *	IOMMU is not found, the original bus dma tag is returned.
113*f509ff83Sjmcneill  */
114*f509ff83Sjmcneill bus_dma_tag_t
fdtbus_iommu_map(int phandle,u_int index,bus_dma_tag_t dmat)115*f509ff83Sjmcneill fdtbus_iommu_map(int phandle, u_int index, bus_dma_tag_t dmat)
116*f509ff83Sjmcneill {
117*f509ff83Sjmcneill 	struct fdtbus_iommu *iommu;
118*f509ff83Sjmcneill 	const u_int *p;
119*f509ff83Sjmcneill 	u_int n, cells;
120*f509ff83Sjmcneill 	int len, resid;
121*f509ff83Sjmcneill 
122*f509ff83Sjmcneill 	p = fdtbus_get_prop(phandle, "iommus", &len);
123*f509ff83Sjmcneill 	if (p == NULL) {
124*f509ff83Sjmcneill 		return dmat;
125*f509ff83Sjmcneill 	}
126*f509ff83Sjmcneill 
127*f509ff83Sjmcneill 	for (n = 0, resid = len; resid > 0; n++) {
128*f509ff83Sjmcneill 		const int iommu_phandle =
129*f509ff83Sjmcneill 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
130*f509ff83Sjmcneill 		if (of_getprop_uint32(iommu_phandle, "#iommu-cells", &cells)) {
131*f509ff83Sjmcneill 			break;
132*f509ff83Sjmcneill 		}
133*f509ff83Sjmcneill 		if (n == index) {
134*f509ff83Sjmcneill 			iommu = fdtbus_get_iommu(iommu_phandle);
135*f509ff83Sjmcneill 			if (iommu == NULL) {
136*f509ff83Sjmcneill 				break;
137*f509ff83Sjmcneill 			}
138*f509ff83Sjmcneill 			return iommu->iommu_funcs->map(iommu->iommu_dev,
139*f509ff83Sjmcneill 			    cells > 0 ? &p[1] : NULL, dmat);
140*f509ff83Sjmcneill 		}
141*f509ff83Sjmcneill 		resid -= (cells + 1) * 4;
142*f509ff83Sjmcneill 		p += cells + 1;
143*f509ff83Sjmcneill 	}
144*f509ff83Sjmcneill 
145*f509ff83Sjmcneill 	return dmat;
146*f509ff83Sjmcneill }
147*f509ff83Sjmcneill 
148*f509ff83Sjmcneill /*
149*f509ff83Sjmcneill  * fdtbus_iommu_map_pci --
150*f509ff83Sjmcneill  *
151*f509ff83Sjmcneill  *	Get a bus dma tag that is translated by any iommus specified by
152*f509ff83Sjmcneill  *	the device tree for PCI devices. The `rid` param is the requester
153*f509ff83Sjmcneill  *	ID. Returns a (maybe translated) dma tag.
154*f509ff83Sjmcneill  */
155*f509ff83Sjmcneill bus_dma_tag_t
fdtbus_iommu_map_pci(int phandle,uint32_t rid,bus_dma_tag_t dmat)156*f509ff83Sjmcneill fdtbus_iommu_map_pci(int phandle, uint32_t rid, bus_dma_tag_t dmat)
157*f509ff83Sjmcneill {
158*f509ff83Sjmcneill 	struct fdtbus_iommu *iommu;
159*f509ff83Sjmcneill 	uint32_t map_mask;
160*f509ff83Sjmcneill 	const u_int *p;
161*f509ff83Sjmcneill 	u_int cells;
162*f509ff83Sjmcneill 	int len;
163*f509ff83Sjmcneill 
164*f509ff83Sjmcneill 	len = 0;
165*f509ff83Sjmcneill 	p = fdtbus_get_prop(phandle, "iommu-map", &len);
166*f509ff83Sjmcneill 	KASSERT(p != NULL || len == 0);
167*f509ff83Sjmcneill 
168*f509ff83Sjmcneill 	if (of_getprop_uint32(phandle, "iommu-map-mask", &map_mask) == 0) {
169*f509ff83Sjmcneill 		rid &= map_mask;
170*f509ff83Sjmcneill 	}
171*f509ff83Sjmcneill 
172*f509ff83Sjmcneill 	while (len >= 4) {
173*f509ff83Sjmcneill 		const u_int rid_base = be32toh(*p++);
174*f509ff83Sjmcneill 		const int iommu_phandle =
175*f509ff83Sjmcneill 		    fdtbus_get_phandle_from_native(be32toh(*p++));
176*f509ff83Sjmcneill 		const u_int iommu_base = be32toh(*p++);
177*f509ff83Sjmcneill 		const u_int length = be32toh(*p++);
178*f509ff83Sjmcneill 		len -= 4;
179*f509ff83Sjmcneill 
180*f509ff83Sjmcneill 		if (iommu_phandle <= 0) {
181*f509ff83Sjmcneill 			continue;
182*f509ff83Sjmcneill 		}
183*f509ff83Sjmcneill 		if (of_getprop_uint32(iommu_phandle, "#iommu-cells", &cells)) {
184*f509ff83Sjmcneill 			continue;
185*f509ff83Sjmcneill 		}
186*f509ff83Sjmcneill 		if (cells != 1) {
187*f509ff83Sjmcneill 			/*
188*f509ff83Sjmcneill 			 * The pci-iommu bindings expect iommu references with
189*f509ff83Sjmcneill 			 * exactly one specifier cell.
190*f509ff83Sjmcneill 			 */
191*f509ff83Sjmcneill 			continue;
192*f509ff83Sjmcneill 		}
193*f509ff83Sjmcneill 		iommu = fdtbus_get_iommu(iommu_phandle);
194*f509ff83Sjmcneill 		if (iommu == NULL) {
195*f509ff83Sjmcneill 			continue;
196*f509ff83Sjmcneill 		}
197*f509ff83Sjmcneill 
198*f509ff83Sjmcneill 		if (rid >= rid_base && rid < rid_base + length) {
199*f509ff83Sjmcneill 			const uint32_t sid = rid - rid_base + iommu_base;
200*f509ff83Sjmcneill 			return iommu->iommu_funcs->map(iommu->iommu_dev,
201*f509ff83Sjmcneill 			    &sid, dmat);
202*f509ff83Sjmcneill 		}
203*f509ff83Sjmcneill 	}
204*f509ff83Sjmcneill 
205*f509ff83Sjmcneill 	return dmat;
206*f509ff83Sjmcneill }
207