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/lib.h>
19 #include <xen/sched.h>
20 #include <xen/guest_access.h>
21 #include <xen/iommu.h>
22 #include <xen/device_tree.h>
23 #include <xsm/xsm.h>
24 
25 static spinlock_t dtdevs_lock = SPIN_LOCK_UNLOCKED;
26 
iommu_assign_dt_device(struct domain * d,struct dt_device_node * dev)27 int iommu_assign_dt_device(struct domain *d, struct dt_device_node *dev)
28 {
29     int rc = -EBUSY;
30     struct domain_iommu *hd = dom_iommu(d);
31 
32     if ( !iommu_enabled || !hd->platform_ops )
33         return -EINVAL;
34 
35     if ( !dt_device_is_protected(dev) )
36         return -EINVAL;
37 
38     spin_lock(&dtdevs_lock);
39 
40     if ( !list_empty(&dev->domain_list) )
41         goto fail;
42 
43     if ( need_iommu(d) <= 0 )
44     {
45         /*
46          * The hwdom is forced to use IOMMU for protecting assigned
47          * device. Therefore the IOMMU data is already set up.
48          */
49         ASSERT(!is_hardware_domain(d));
50         rc = iommu_construct(d);
51         if ( rc )
52             goto fail;
53     }
54 
55     /* The flag field doesn't matter to DT device. */
56     rc = hd->platform_ops->assign_device(d, 0, dt_to_dev(dev), 0);
57 
58     if ( rc )
59         goto fail;
60 
61     list_add(&dev->domain_list, &hd->dt_devices);
62     dt_device_set_used_by(dev, d->domain_id);
63 
64 fail:
65     spin_unlock(&dtdevs_lock);
66 
67     return rc;
68 }
69 
iommu_deassign_dt_device(struct domain * d,struct dt_device_node * dev)70 int iommu_deassign_dt_device(struct domain *d, struct dt_device_node *dev)
71 {
72     const struct domain_iommu *hd = dom_iommu(d);
73     int rc;
74 
75     if ( !iommu_enabled || !hd->platform_ops )
76         return -EINVAL;
77 
78     if ( !dt_device_is_protected(dev) )
79         return -EINVAL;
80 
81     spin_lock(&dtdevs_lock);
82 
83     rc = hd->platform_ops->reassign_device(d, NULL, 0, dt_to_dev(dev));
84     if ( rc )
85         goto fail;
86 
87     list_del_init(&dev->domain_list);
88     dt_device_set_used_by(dev, DOMID_IO);
89 
90 fail:
91     spin_unlock(&dtdevs_lock);
92 
93     return rc;
94 }
95 
iommu_dt_device_is_assigned(const struct dt_device_node * dev)96 static bool_t iommu_dt_device_is_assigned(const struct dt_device_node *dev)
97 {
98     bool_t assigned = 0;
99 
100     if ( !dt_device_is_protected(dev) )
101         return 0;
102 
103     spin_lock(&dtdevs_lock);
104     assigned = !list_empty(&dev->domain_list);
105     spin_unlock(&dtdevs_lock);
106 
107     return assigned;
108 }
109 
iommu_dt_domain_init(struct domain * d)110 int iommu_dt_domain_init(struct domain *d)
111 {
112     INIT_LIST_HEAD(&dom_iommu(d)->dt_devices);
113 
114     return 0;
115 }
116 
iommu_release_dt_devices(struct domain * d)117 int iommu_release_dt_devices(struct domain *d)
118 {
119     const struct domain_iommu *hd = dom_iommu(d);
120     struct dt_device_node *dev, *_dev;
121     int rc;
122 
123     list_for_each_entry_safe(dev, _dev, &hd->dt_devices, domain_list)
124     {
125         rc = iommu_deassign_dt_device(d, dev);
126         if ( rc )
127         {
128             dprintk(XENLOG_ERR, "Failed to deassign %s in domain %u\n",
129                     dt_node_full_name(dev), d->domain_id);
130             return rc;
131         }
132     }
133 
134     return 0;
135 }
136 
iommu_do_dt_domctl(struct xen_domctl * domctl,struct domain * d,XEN_GUEST_HANDLE_PARAM (xen_domctl_t)u_domctl)137 int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
138                        XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
139 {
140     int ret;
141     struct dt_device_node *dev;
142 
143     switch ( domctl->cmd )
144     {
145     case XEN_DOMCTL_assign_device:
146         ASSERT(d);
147         /* fall through */
148     case XEN_DOMCTL_test_assign_device:
149         ret = -ENODEV;
150         if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
151             break;
152 
153         ret = -EINVAL;
154         if ( (d && d->is_dying) || domctl->u.assign_device.flags )
155             break;
156 
157         ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
158                                     domctl->u.assign_device.u.dt.size,
159                                     &dev);
160         if ( ret )
161             break;
162 
163         ret = xsm_assign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev));
164         if ( ret )
165             break;
166 
167         if ( domctl->cmd == XEN_DOMCTL_test_assign_device )
168         {
169             if ( iommu_dt_device_is_assigned(dev) )
170             {
171                 printk(XENLOG_G_ERR "%s already assigned.\n",
172                        dt_node_full_name(dev));
173                 ret = -EINVAL;
174             }
175             break;
176         }
177 
178         ret = iommu_assign_dt_device(d, dev);
179 
180         if ( ret )
181             printk(XENLOG_G_ERR "XEN_DOMCTL_assign_dt_device: assign \"%s\""
182                    " to dom%u failed (%d)\n",
183                    dt_node_full_name(dev), d->domain_id, ret);
184         break;
185 
186     case XEN_DOMCTL_deassign_device:
187         ret = -ENODEV;
188         if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
189             break;
190 
191         ret = -EINVAL;
192         if ( domctl->u.assign_device.flags )
193             break;
194 
195         ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
196                                     domctl->u.assign_device.u.dt.size,
197                                     &dev);
198         if ( ret )
199             break;
200 
201         ret = xsm_deassign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev));
202 
203         ret = iommu_deassign_dt_device(d, dev);
204 
205         if ( ret )
206             printk(XENLOG_G_ERR "XEN_DOMCTL_assign_dt_device: assign \"%s\""
207                    " to dom%u failed (%d)\n",
208                    dt_node_full_name(dev), d->domain_id, ret);
209         break;
210 
211     default:
212         ret = -ENOSYS;
213         break;
214     }
215 
216     return ret;
217 }
218