1 /*
2 * Code to passthrough a device tree node to a guest
3 *
4 * Julien Grall <julien.grall@linaro.org>
5 * Copyright (c) 2014 Linaro Limited.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18 #include <xen/device_tree.h>
19 #include <xen/guest_access.h>
20 #include <xen/iommu.h>
21 #include <xen/lib.h>
22 #include <xen/sched.h>
23 #include <xsm/xsm.h>
24
25 #include <asm/iommu_fwspec.h>
26
27 static spinlock_t dtdevs_lock = SPIN_LOCK_UNLOCKED;
28
iommu_assign_dt_device(struct domain * d,struct dt_device_node * dev)29 int iommu_assign_dt_device(struct domain *d, struct dt_device_node *dev)
30 {
31 int rc = -EBUSY;
32 struct domain_iommu *hd = dom_iommu(d);
33
34 if ( !is_iommu_enabled(d) )
35 return -EINVAL;
36
37 if ( !dt_device_is_protected(dev) )
38 return -EINVAL;
39
40 spin_lock(&dtdevs_lock);
41
42 if ( !list_empty(&dev->domain_list) )
43 goto fail;
44
45 /* The flag field doesn't matter to DT device. */
46 rc = hd->platform_ops->assign_device(d, 0, dt_to_dev(dev), 0);
47
48 if ( rc )
49 goto fail;
50
51 list_add(&dev->domain_list, &hd->dt_devices);
52 dt_device_set_used_by(dev, d->domain_id);
53
54 fail:
55 spin_unlock(&dtdevs_lock);
56
57 return rc;
58 }
59
iommu_deassign_dt_device(struct domain * d,struct dt_device_node * dev)60 int iommu_deassign_dt_device(struct domain *d, struct dt_device_node *dev)
61 {
62 const struct domain_iommu *hd = dom_iommu(d);
63 int rc;
64
65 if ( !is_iommu_enabled(d) )
66 return -EINVAL;
67
68 if ( !dt_device_is_protected(dev) )
69 return -EINVAL;
70
71 spin_lock(&dtdevs_lock);
72
73 rc = hd->platform_ops->reassign_device(d, NULL, 0, dt_to_dev(dev));
74 if ( rc )
75 goto fail;
76
77 list_del_init(&dev->domain_list);
78 dt_device_set_used_by(dev, DOMID_IO);
79
80 fail:
81 spin_unlock(&dtdevs_lock);
82
83 return rc;
84 }
85
iommu_dt_device_is_assigned(const struct dt_device_node * dev)86 static bool_t iommu_dt_device_is_assigned(const struct dt_device_node *dev)
87 {
88 bool_t assigned = 0;
89
90 if ( !dt_device_is_protected(dev) )
91 return 0;
92
93 spin_lock(&dtdevs_lock);
94 assigned = !list_empty(&dev->domain_list);
95 spin_unlock(&dtdevs_lock);
96
97 return assigned;
98 }
99
iommu_dt_domain_init(struct domain * d)100 int iommu_dt_domain_init(struct domain *d)
101 {
102 INIT_LIST_HEAD(&dom_iommu(d)->dt_devices);
103
104 return 0;
105 }
106
iommu_release_dt_devices(struct domain * d)107 int iommu_release_dt_devices(struct domain *d)
108 {
109 const struct domain_iommu *hd = dom_iommu(d);
110 struct dt_device_node *dev, *_dev;
111 int rc;
112
113 if ( !is_iommu_enabled(d) )
114 return 0;
115
116 list_for_each_entry_safe(dev, _dev, &hd->dt_devices, domain_list)
117 {
118 rc = iommu_deassign_dt_device(d, dev);
119 if ( rc )
120 {
121 dprintk(XENLOG_ERR, "Failed to deassign %s in domain %u\n",
122 dt_node_full_name(dev), d->domain_id);
123 return rc;
124 }
125 }
126
127 return 0;
128 }
129
iommu_add_dt_device(struct dt_device_node * np)130 int iommu_add_dt_device(struct dt_device_node *np)
131 {
132 const struct iommu_ops *ops = iommu_get_ops();
133 struct dt_phandle_args iommu_spec;
134 struct device *dev = dt_to_dev(np);
135 int rc = 1, index = 0;
136
137 if ( !iommu_enabled )
138 return 1;
139
140 if ( !ops )
141 return -EINVAL;
142
143 if ( dev_iommu_fwspec_get(dev) )
144 return -EEXIST;
145
146 /*
147 * According to the Documentation/devicetree/bindings/iommu/iommu.txt
148 * from Linux.
149 */
150 while ( !dt_parse_phandle_with_args(np, "iommus", "#iommu-cells",
151 index, &iommu_spec) )
152 {
153 /*
154 * The driver which supports generic IOMMU DT bindings must have
155 * these callback implemented.
156 */
157 if ( !ops->add_device || !ops->dt_xlate )
158 {
159 /*
160 * Some Device Trees may expose both legacy SMMU and generic
161 * IOMMU bindings together. However, the SMMU driver is only
162 * supporting the former and will protect them during the
163 * initialization. So we need to skip them and not return
164 * error here.
165 *
166 * XXX: This can be dropped when the SMMU is able to deal
167 * with generic bindings.
168 */
169 if ( dt_device_is_protected(np) )
170 return 0;
171 else
172 return -EINVAL;
173 }
174
175 if ( !dt_device_is_available(iommu_spec.np) )
176 break;
177
178 rc = iommu_fwspec_init(dev, &iommu_spec.np->dev);
179 if ( rc )
180 break;
181
182 /*
183 * Provide DT IOMMU specifier which describes the IOMMU master
184 * interfaces of that device (device IDs, etc) to the driver.
185 * The driver is responsible to decide how to interpret them.
186 */
187 rc = ops->dt_xlate(dev, &iommu_spec);
188 if ( rc )
189 break;
190
191 index++;
192 }
193
194 /*
195 * Add master device to the IOMMU if latter is present and available.
196 * The driver is responsible to mark that device as protected.
197 */
198 if ( !rc )
199 rc = ops->add_device(0, dev);
200
201 if ( rc < 0 )
202 iommu_fwspec_free(dev);
203
204 return rc;
205 }
206
iommu_do_dt_domctl(struct xen_domctl * domctl,struct domain * d,XEN_GUEST_HANDLE_PARAM (xen_domctl_t)u_domctl)207 int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
208 XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
209 {
210 int ret;
211 struct dt_device_node *dev;
212
213 switch ( domctl->cmd )
214 {
215 case XEN_DOMCTL_assign_device:
216 ASSERT(d);
217 /* fall through */
218 case XEN_DOMCTL_test_assign_device:
219 ret = -ENODEV;
220 if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
221 break;
222
223 ret = -EINVAL;
224 if ( (d && d->is_dying) || domctl->u.assign_device.flags )
225 break;
226
227 ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
228 domctl->u.assign_device.u.dt.size,
229 &dev);
230 if ( ret )
231 break;
232
233 ret = xsm_assign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev));
234 if ( ret )
235 break;
236
237 if ( domctl->cmd == XEN_DOMCTL_test_assign_device )
238 {
239 if ( iommu_dt_device_is_assigned(dev) )
240 {
241 printk(XENLOG_G_ERR "%s already assigned.\n",
242 dt_node_full_name(dev));
243 ret = -EINVAL;
244 }
245 break;
246 }
247
248 if ( d == dom_io )
249 return -EINVAL;
250
251 ret = iommu_add_dt_device(dev);
252 /*
253 * Ignore "-EEXIST" error code as it would mean that the device is
254 * already added to the IOMMU (positive result). Such happens after
255 * re-creating guest domain.
256 */
257 if ( ret < 0 && ret != -EEXIST )
258 {
259 printk(XENLOG_G_ERR "Failed to add %s to the IOMMU\n",
260 dt_node_full_name(dev));
261 break;
262 }
263
264 ret = iommu_assign_dt_device(d, dev);
265
266 if ( ret )
267 printk(XENLOG_G_ERR "XEN_DOMCTL_assign_dt_device: assign \"%s\""
268 " to dom%u failed (%d)\n",
269 dt_node_full_name(dev), d->domain_id, ret);
270 break;
271
272 case XEN_DOMCTL_deassign_device:
273 ret = -ENODEV;
274 if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
275 break;
276
277 ret = -EINVAL;
278 if ( domctl->u.assign_device.flags )
279 break;
280
281 ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
282 domctl->u.assign_device.u.dt.size,
283 &dev);
284 if ( ret )
285 break;
286
287 ret = xsm_deassign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev));
288
289 if ( d == dom_io )
290 return -EINVAL;
291
292 ret = iommu_deassign_dt_device(d, dev);
293
294 if ( ret )
295 printk(XENLOG_G_ERR "XEN_DOMCTL_assign_dt_device: assign \"%s\""
296 " to dom%u failed (%d)\n",
297 dt_node_full_name(dev), d->domain_id, ret);
298 break;
299
300 default:
301 ret = -ENOSYS;
302 break;
303 }
304
305 return ret;
306 }
307