1 /*
2 * Remoteproc Virtio Framework Implementation
3 *
4 * Copyright(c) 2018 Xilinx Ltd.
5 * Copyright(c) 2011 Texas Instruments, Inc.
6 * Copyright(c) 2011 Google, Inc.
7 * All rights reserved.
8 *
9 * SPDX-License-Identifier: BSD-3-Clause
10 */
11
12 #include <openamp/remoteproc.h>
13 #include <openamp/remoteproc_virtio.h>
14 #include <openamp/virtqueue.h>
15 #include <metal/cpu.h>
16 #include <metal/utilities.h>
17 #include <metal/alloc.h>
18
rproc_virtio_virtqueue_notify(struct virtqueue * vq)19 static void rproc_virtio_virtqueue_notify(struct virtqueue *vq)
20 {
21 struct remoteproc_virtio *rpvdev;
22 struct virtio_vring_info *vring_info;
23 struct virtio_device *vdev;
24 unsigned int vq_id = vq->vq_queue_index;
25
26 vdev = vq->vq_dev;
27 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
28 metal_assert(vq_id < vdev->vrings_num);
29 vring_info = &vdev->vrings_info[vq_id];
30 rpvdev->notify(rpvdev->priv, vring_info->notifyid);
31 }
32
rproc_virtio_get_status(struct virtio_device * vdev)33 static unsigned char rproc_virtio_get_status(struct virtio_device *vdev)
34 {
35 struct remoteproc_virtio *rpvdev;
36 struct fw_rsc_vdev *vdev_rsc;
37 struct metal_io_region *io;
38 char status;
39
40 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
41 vdev_rsc = rpvdev->vdev_rsc;
42 io = rpvdev->vdev_rsc_io;
43 RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
44 status = metal_io_read8(io,
45 metal_io_virt_to_offset(io, &vdev_rsc->status));
46 return status;
47 }
48
49 #ifndef VIRTIO_DEVICE_ONLY
rproc_virtio_set_status(struct virtio_device * vdev,unsigned char status)50 static void rproc_virtio_set_status(struct virtio_device *vdev,
51 unsigned char status)
52 {
53 struct remoteproc_virtio *rpvdev;
54 struct fw_rsc_vdev *vdev_rsc;
55 struct metal_io_region *io;
56
57 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
58 vdev_rsc = rpvdev->vdev_rsc;
59 io = rpvdev->vdev_rsc_io;
60 metal_io_write8(io,
61 metal_io_virt_to_offset(io, &vdev_rsc->status),
62 status);
63 RSC_TABLE_FLUSH(vdev_rsc, sizeof(struct fw_rsc_vdev));
64 rpvdev->notify(rpvdev->priv, vdev->notifyid);
65 }
66 #endif
67
rproc_virtio_get_dfeatures(struct virtio_device * vdev)68 static uint32_t rproc_virtio_get_dfeatures(struct virtio_device *vdev)
69 {
70 struct remoteproc_virtio *rpvdev;
71 struct fw_rsc_vdev *vdev_rsc;
72 struct metal_io_region *io;
73 uint32_t features;
74
75 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
76 vdev_rsc = rpvdev->vdev_rsc;
77 io = rpvdev->vdev_rsc_io;
78 RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
79 features = metal_io_read32(io,
80 metal_io_virt_to_offset(io, &vdev_rsc->dfeatures));
81
82 return features;
83 }
84
rproc_virtio_get_features(struct virtio_device * vdev)85 static uint32_t rproc_virtio_get_features(struct virtio_device *vdev)
86 {
87 struct remoteproc_virtio *rpvdev;
88 struct fw_rsc_vdev *vdev_rsc;
89 struct metal_io_region *io;
90 uint32_t gfeatures;
91 uint32_t dfeatures;
92
93 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
94 vdev_rsc = rpvdev->vdev_rsc;
95 io = rpvdev->vdev_rsc_io;
96 RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
97 gfeatures = metal_io_read32(io,
98 metal_io_virt_to_offset(io, &vdev_rsc->gfeatures));
99 dfeatures = rproc_virtio_get_dfeatures(vdev);
100
101 return dfeatures & gfeatures;
102 }
103
104 #ifndef VIRTIO_DEVICE_ONLY
rproc_virtio_set_features(struct virtio_device * vdev,uint32_t features)105 static void rproc_virtio_set_features(struct virtio_device *vdev,
106 uint32_t features)
107 {
108 struct remoteproc_virtio *rpvdev;
109 struct fw_rsc_vdev *vdev_rsc;
110 struct metal_io_region *io;
111
112 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
113 vdev_rsc = rpvdev->vdev_rsc;
114 io = rpvdev->vdev_rsc_io;
115 metal_io_write32(io,
116 metal_io_virt_to_offset(io, &vdev_rsc->gfeatures),
117 features);
118 RSC_TABLE_FLUSH(vdev_rsc, sizeof(struct fw_rsc_vdev));
119 rpvdev->notify(rpvdev->priv, vdev->notifyid);
120 }
121
rproc_virtio_negotiate_features(struct virtio_device * vdev,uint32_t features)122 static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev,
123 uint32_t features)
124 {
125 uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev);
126
127 rproc_virtio_set_features(vdev, dfeatures & features);
128
129 return 0;
130 }
131 #endif
132
rproc_virtio_read_config(struct virtio_device * vdev,uint32_t offset,void * dst,int length)133 static void rproc_virtio_read_config(struct virtio_device *vdev,
134 uint32_t offset, void *dst, int length)
135 {
136 struct remoteproc_virtio *rpvdev;
137 struct fw_rsc_vdev *vdev_rsc;
138 struct metal_io_region *io;
139 char *config;
140
141 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
142 vdev_rsc = rpvdev->vdev_rsc;
143 config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
144 io = rpvdev->vdev_rsc_io;
145
146 if (offset + length <= vdev_rsc->config_len) {
147 RSC_TABLE_INVALIDATE(config + offset, length);
148 metal_io_block_read(io,
149 metal_io_virt_to_offset(io, config + offset),
150 dst, length);
151 }
152 }
153
154 #ifndef VIRTIO_DEVICE_ONLY
rproc_virtio_write_config(struct virtio_device * vdev,uint32_t offset,void * src,int length)155 static void rproc_virtio_write_config(struct virtio_device *vdev,
156 uint32_t offset, void *src, int length)
157 {
158 struct remoteproc_virtio *rpvdev;
159 struct fw_rsc_vdev *vdev_rsc;
160 struct metal_io_region *io;
161 char *config;
162
163 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
164 vdev_rsc = rpvdev->vdev_rsc;
165 config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
166 io = rpvdev->vdev_rsc_io;
167
168 if (offset + length <= vdev_rsc->config_len) {
169 metal_io_block_write(io,
170 metal_io_virt_to_offset(io, config + offset),
171 src, length);
172 RSC_TABLE_FLUSH(config + offset, length);
173 rpvdev->notify(rpvdev->priv, vdev->notifyid);
174 }
175 }
176
rproc_virtio_reset_device(struct virtio_device * vdev)177 static void rproc_virtio_reset_device(struct virtio_device *vdev)
178 {
179 if (vdev->role == VIRTIO_DEV_DRIVER)
180 rproc_virtio_set_status(vdev,
181 VIRTIO_CONFIG_STATUS_NEEDS_RESET);
182 }
183 #endif
184
185 static const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = {
186 .get_status = rproc_virtio_get_status,
187 .get_features = rproc_virtio_get_features,
188 .read_config = rproc_virtio_read_config,
189 .notify = rproc_virtio_virtqueue_notify,
190 #ifndef VIRTIO_DEVICE_ONLY
191 /*
192 * We suppose here that the vdev is in a shared memory so that can
193 * be access only by one core: the host. In this case salve core has
194 * only read access right.
195 */
196 .set_status = rproc_virtio_set_status,
197 .set_features = rproc_virtio_set_features,
198 .negotiate_features = rproc_virtio_negotiate_features,
199 .write_config = rproc_virtio_write_config,
200 .reset_device = rproc_virtio_reset_device,
201 #endif
202 };
203
204 struct virtio_device *
rproc_virtio_create_vdev(unsigned int role,unsigned int notifyid,void * rsc,struct metal_io_region * rsc_io,void * priv,rpvdev_notify_func notify,virtio_dev_reset_cb rst_cb)205 rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
206 void *rsc, struct metal_io_region *rsc_io,
207 void *priv,
208 rpvdev_notify_func notify,
209 virtio_dev_reset_cb rst_cb)
210 {
211 struct remoteproc_virtio *rpvdev;
212 struct virtio_vring_info *vrings_info;
213 struct fw_rsc_vdev *vdev_rsc = rsc;
214 struct virtio_device *vdev;
215 unsigned int num_vrings = vdev_rsc->num_of_vrings;
216 unsigned int i;
217
218 rpvdev = metal_allocate_memory(sizeof(*rpvdev));
219 if (!rpvdev)
220 return NULL;
221 vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings);
222 if (!vrings_info)
223 goto err0;
224 memset(rpvdev, 0, sizeof(*rpvdev));
225 memset(vrings_info, 0, sizeof(*vrings_info));
226 vdev = &rpvdev->vdev;
227
228 for (i = 0; i < num_vrings; i++) {
229 struct virtqueue *vq;
230 #ifndef VIRTIO_DEVICE_ONLY
231 struct fw_rsc_vdev_vring *vring_rsc;
232 #endif
233 unsigned int num_extra_desc = 0;
234
235 #ifndef VIRTIO_DEVICE_ONLY
236 vring_rsc = &vdev_rsc->vring[i];
237 if (role == VIRTIO_DEV_DRIVER) {
238 num_extra_desc = vring_rsc->num;
239 }
240 #endif
241 vq = virtqueue_allocate(num_extra_desc);
242 if (!vq)
243 goto err1;
244 vrings_info[i].vq = vq;
245 }
246
247 rpvdev->notify = notify;
248 rpvdev->priv = priv;
249 vdev->vrings_info = vrings_info;
250 /* Assuming the shared memory has been mapped and registered if
251 * necessary
252 */
253 rpvdev->vdev_rsc = vdev_rsc;
254 rpvdev->vdev_rsc_io = rsc_io;
255
256 vdev->notifyid = notifyid;
257 vdev->role = role;
258 vdev->reset_cb = rst_cb;
259 vdev->vrings_num = num_vrings;
260 vdev->func = &remoteproc_virtio_dispatch_funcs;
261
262 #ifndef VIRTIO_DEVICE_ONLY
263 if (role == VIRTIO_DEV_DRIVER) {
264 uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev);
265 /* Assume the virtio driver support all remote features */
266 rproc_virtio_negotiate_features(vdev, dfeatures);
267 }
268 #endif
269
270 return &rpvdev->vdev;
271
272 err1:
273 for (i = 0; i < num_vrings; i++) {
274 if (vrings_info[i].vq)
275 metal_free_memory(vrings_info[i].vq);
276 }
277 metal_free_memory(vrings_info);
278 err0:
279 metal_free_memory(rpvdev);
280 return NULL;
281 }
282
rproc_virtio_remove_vdev(struct virtio_device * vdev)283 void rproc_virtio_remove_vdev(struct virtio_device *vdev)
284 {
285 struct remoteproc_virtio *rpvdev;
286 unsigned int i;
287
288 if (!vdev)
289 return;
290 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
291 for (i = 0; i < vdev->vrings_num; i++) {
292 struct virtqueue *vq;
293
294 vq = vdev->vrings_info[i].vq;
295 if (vq)
296 metal_free_memory(vq);
297 }
298 if (vdev->vrings_info)
299 metal_free_memory(vdev->vrings_info);
300 metal_free_memory(rpvdev);
301 }
302
rproc_virtio_init_vring(struct virtio_device * vdev,unsigned int index,unsigned int notifyid,void * va,struct metal_io_region * io,unsigned int num_descs,unsigned int align)303 int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
304 unsigned int notifyid, void *va,
305 struct metal_io_region *io,
306 unsigned int num_descs, unsigned int align)
307 {
308 struct virtio_vring_info *vring_info;
309 unsigned int num_vrings;
310
311 num_vrings = vdev->vrings_num;
312 if ((index >= num_vrings) || (num_descs > RPROC_MAX_VRING_DESC))
313 return -RPROC_EINVAL;
314 vring_info = &vdev->vrings_info[index];
315 vring_info->io = io;
316 vring_info->notifyid = notifyid;
317 vring_info->info.vaddr = va;
318 vring_info->info.num_descs = num_descs;
319 vring_info->info.align = align;
320
321 return 0;
322 }
323
rproc_virtio_notified(struct virtio_device * vdev,uint32_t notifyid)324 int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid)
325 {
326 unsigned int num_vrings, i;
327 struct virtio_vring_info *vring_info;
328 struct virtqueue *vq;
329
330 if (!vdev)
331 return -RPROC_EINVAL;
332 /* We do nothing for vdev notification in this implementation */
333 if (vdev->notifyid == notifyid)
334 return 0;
335 num_vrings = vdev->vrings_num;
336 for (i = 0; i < num_vrings; i++) {
337 vring_info = &vdev->vrings_info[i];
338 if (vring_info->notifyid == notifyid ||
339 notifyid == RSC_NOTIFY_ID_ANY) {
340 vq = vring_info->vq;
341 virtqueue_notification(vq);
342 }
343 }
344 return 0;
345 }
346
rproc_virtio_wait_remote_ready(struct virtio_device * vdev)347 void rproc_virtio_wait_remote_ready(struct virtio_device *vdev)
348 {
349 uint8_t status;
350
351 #ifndef VIRTIO_DEVICE_ONLY
352 /*
353 * No status available for remote. As virtio driver has not to wait
354 * remote action, we can return. Behavior should be updated
355 * in future if a remote status is added.
356 */
357 if (vdev->role == VIRTIO_DEV_DRIVER)
358 return;
359 #endif
360 while (1) {
361 status = rproc_virtio_get_status(vdev);
362 if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
363 return;
364 metal_cpu_yield();
365 }
366 }
367