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