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