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     ASSERT(system_state < SYS_STATE_active || rw_is_locked(&dt_host_lock));
35 
36     if ( !is_iommu_enabled(d) )
37         return -EINVAL;
38 
39     if ( !dt_device_is_protected(dev) )
40         return -EINVAL;
41 
42     spin_lock(&dtdevs_lock);
43 
44     if ( !list_empty(&dev->domain_list) )
45         goto fail;
46 
47     /* The flag field doesn't matter to DT device. */
48     rc = hd->platform_ops->assign_device(d, 0, dt_to_dev(dev), 0);
49 
50     if ( rc )
51         goto fail;
52 
53     list_add(&dev->domain_list, &hd->dt_devices);
54     dt_device_set_used_by(dev, d->domain_id);
55 
56 fail:
57     spin_unlock(&dtdevs_lock);
58 
59     return rc;
60 }
61 
iommu_deassign_dt_device(struct domain * d,struct dt_device_node * dev)62 int iommu_deassign_dt_device(struct domain *d, struct dt_device_node *dev)
63 {
64     const struct domain_iommu *hd = dom_iommu(d);
65     int rc;
66 
67     ASSERT(rw_is_locked(&dt_host_lock));
68 
69     if ( !is_iommu_enabled(d) )
70         return -EINVAL;
71 
72     if ( !dt_device_is_protected(dev) )
73         return -EINVAL;
74 
75     spin_lock(&dtdevs_lock);
76 
77     rc = hd->platform_ops->reassign_device(d, NULL, 0, dt_to_dev(dev));
78     if ( rc )
79         goto fail;
80 
81     list_del_init(&dev->domain_list);
82     dt_device_set_used_by(dev, DOMID_IO);
83 
84 fail:
85     spin_unlock(&dtdevs_lock);
86 
87     return rc;
88 }
89 
iommu_dt_device_is_assigned_locked(const struct dt_device_node * dev)90 static bool iommu_dt_device_is_assigned_locked(const struct dt_device_node *dev)
91 {
92     bool assigned = false;
93 
94     ASSERT(spin_is_locked(&dtdevs_lock));
95 
96     if ( !dt_device_is_protected(dev) )
97         return 0;
98 
99     assigned = !list_empty(&dev->domain_list);
100 
101     return assigned;
102 }
103 
iommu_dt_domain_init(struct domain * d)104 int iommu_dt_domain_init(struct domain *d)
105 {
106     INIT_LIST_HEAD(&dom_iommu(d)->dt_devices);
107 
108     return 0;
109 }
110 
iommu_release_dt_devices(struct domain * d)111 int iommu_release_dt_devices(struct domain *d)
112 {
113     const struct domain_iommu *hd = dom_iommu(d);
114     struct dt_device_node *dev, *_dev;
115     int rc;
116 
117     if ( !is_iommu_enabled(d) )
118         return 0;
119 
120     read_lock(&dt_host_lock);
121 
122     list_for_each_entry_safe(dev, _dev, &hd->dt_devices, domain_list)
123     {
124         rc = iommu_deassign_dt_device(d, dev);
125         if ( rc )
126         {
127             dprintk(XENLOG_ERR, "Failed to deassign %s in domain %u\n",
128                     dt_node_full_name(dev), d->domain_id);
129             read_unlock(&dt_host_lock);
130 
131             return rc;
132         }
133     }
134 
135     read_unlock(&dt_host_lock);
136 
137     return 0;
138 }
139 
iommu_remove_dt_device(struct dt_device_node * np)140 int iommu_remove_dt_device(struct dt_device_node *np)
141 {
142     const struct iommu_ops *ops = iommu_get_ops();
143     struct device *dev = dt_to_dev(np);
144     int rc;
145 
146     ASSERT(rw_is_locked(&dt_host_lock));
147 
148     if ( !iommu_enabled )
149         return 1;
150 
151     if ( !ops )
152         return -EOPNOTSUPP;
153 
154     spin_lock(&dtdevs_lock);
155 
156     if ( iommu_dt_device_is_assigned_locked(np) )
157     {
158         rc = -EBUSY;
159         goto fail;
160     }
161 
162     if ( !ops->remove_device )
163     {
164         rc = -EOPNOTSUPP;
165         goto fail;
166     }
167 
168     /*
169      * De-register the device from the IOMMU driver.
170      * The driver is responsible for removing is_protected flag.
171      */
172     rc = ops->remove_device(0, dev);
173 
174     if ( !rc )
175     {
176         ASSERT(!dt_device_is_protected(np));
177         iommu_fwspec_free(dev);
178     }
179 
180  fail:
181     spin_unlock(&dtdevs_lock);
182     return rc;
183 }
184 
iommu_add_dt_device(struct dt_device_node * np)185 int iommu_add_dt_device(struct dt_device_node *np)
186 {
187     const struct iommu_ops *ops = iommu_get_ops();
188     struct dt_phandle_args iommu_spec;
189     struct device *dev = dt_to_dev(np);
190     int rc = 1, index = 0;
191 
192     ASSERT(system_state < SYS_STATE_active || rw_is_locked(&dt_host_lock));
193 
194     if ( !iommu_enabled )
195         return 1;
196 
197     if ( !ops )
198         return -EINVAL;
199 
200     /*
201      * The device may already have been registered. As there is no harm in
202      * it just return success early.
203      */
204     if ( dev_iommu_fwspec_get(dev) )
205         return 0;
206 
207     spin_lock(&dtdevs_lock);
208 
209     /*
210      * According to the Documentation/devicetree/bindings/iommu/iommu.txt
211      * from Linux.
212      */
213     while ( !dt_parse_phandle_with_args(np, "iommus", "#iommu-cells",
214                                         index, &iommu_spec) )
215     {
216         /*
217          * The driver which supports generic IOMMU DT bindings must have
218          * these callback implemented.
219          */
220         if ( !ops->add_device || !ops->dt_xlate )
221         {
222             rc = -EINVAL;
223             goto fail;
224         }
225 
226         if ( !dt_device_is_available(iommu_spec.np) )
227             break;
228 
229         rc = iommu_fwspec_init(dev, &iommu_spec.np->dev);
230         if ( rc )
231             break;
232 
233         /*
234          * Provide DT IOMMU specifier which describes the IOMMU master
235          * interfaces of that device (device IDs, etc) to the driver.
236          * The driver is responsible to decide how to interpret them.
237          */
238         rc = ops->dt_xlate(dev, &iommu_spec);
239         if ( rc )
240             break;
241 
242         index++;
243     }
244 
245     /*
246      * Add master device to the IOMMU if latter is present and available.
247      * The driver is responsible to mark that device as protected.
248      */
249     if ( !rc )
250         rc = ops->add_device(0, dev);
251 
252     if ( rc < 0 )
253         iommu_fwspec_free(dev);
254 
255  fail:
256     spin_unlock(&dtdevs_lock);
257     return rc;
258 }
259 
iommu_do_dt_domctl(struct xen_domctl * domctl,struct domain * d,XEN_GUEST_HANDLE_PARAM (xen_domctl_t)u_domctl)260 int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
261                        XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
262 {
263     int ret;
264     struct dt_device_node *dev;
265 
266     read_lock(&dt_host_lock);
267 
268     switch ( domctl->cmd )
269     {
270     case XEN_DOMCTL_assign_device:
271         ASSERT(d);
272         /* fall through */
273     case XEN_DOMCTL_test_assign_device:
274         ret = -ENODEV;
275         if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
276             break;
277 
278         ret = -EINVAL;
279         if ( (d && d->is_dying) || domctl->u.assign_device.flags )
280             break;
281 
282         ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
283                                     domctl->u.assign_device.u.dt.size,
284                                     &dev);
285         if ( ret )
286             break;
287 
288         ret = xsm_assign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev));
289         if ( ret )
290             break;
291 
292         if ( domctl->cmd == XEN_DOMCTL_test_assign_device )
293         {
294             spin_lock(&dtdevs_lock);
295 
296             if ( iommu_dt_device_is_assigned_locked(dev) )
297             {
298                 printk(XENLOG_G_ERR "%s already assigned.\n",
299                        dt_node_full_name(dev));
300                 ret = -EINVAL;
301             }
302 
303             spin_unlock(&dtdevs_lock);
304             break;
305         }
306 
307         if ( d == dom_io )
308         {
309             ret = -EINVAL;
310             break;
311         }
312 
313         ret = iommu_add_dt_device(dev);
314         if ( ret < 0 )
315         {
316             printk(XENLOG_G_ERR "Failed to add %s to the IOMMU\n",
317                    dt_node_full_name(dev));
318             break;
319         }
320 
321         ret = iommu_assign_dt_device(d, dev);
322 
323         if ( ret )
324             printk(XENLOG_G_ERR "XEN_DOMCTL_assign_dt_device: assign \"%s\""
325                    " to dom%u failed (%d)\n",
326                    dt_node_full_name(dev), d->domain_id, ret);
327         break;
328 
329     case XEN_DOMCTL_deassign_device:
330         ret = -ENODEV;
331         if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
332             break;
333 
334         ret = -EINVAL;
335         if ( domctl->u.assign_device.flags )
336             break;
337 
338         ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
339                                     domctl->u.assign_device.u.dt.size,
340                                     &dev);
341         if ( ret )
342             break;
343 
344         ret = xsm_deassign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev));
345         if ( ret )
346             break;
347 
348         if ( d == dom_io )
349         {
350             ret = -EINVAL;
351             break;
352         }
353 
354         ret = iommu_deassign_dt_device(d, dev);
355 
356         if ( ret )
357             printk(XENLOG_G_ERR "XEN_DOMCTL_assign_dt_device: assign \"%s\""
358                    " to dom%u failed (%d)\n",
359                    dt_node_full_name(dev), d->domain_id, ret);
360         break;
361 
362     default:
363         ret = -ENOSYS;
364         break;
365     }
366 
367     read_unlock(&dt_host_lock);
368 
369     return ret;
370 }
371