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