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/sys.h>
16 #include <metal/utilities.h>
17 #include <metal/alloc.h>
18
rproc_virtio_delete_virtqueues(struct virtio_device * vdev)19 static void rproc_virtio_delete_virtqueues(struct virtio_device *vdev)
20 {
21 struct virtio_vring_info *vring_info;
22 unsigned int i;
23
24 if (!vdev->vrings_info)
25 return;
26
27 for (i = 0; i < vdev->vrings_num; i++) {
28 vring_info = &vdev->vrings_info[i];
29 if (vring_info->vq)
30 virtqueue_free(vring_info->vq);
31 }
32 }
33
rproc_virtio_create_virtqueue(struct virtio_device * vdev,unsigned int flags,unsigned int idx,const char * name,vq_callback callback)34 static int rproc_virtio_create_virtqueue(struct virtio_device *vdev,
35 unsigned int flags,
36 unsigned int idx,
37 const char *name,
38 vq_callback callback)
39 {
40 struct virtio_vring_info *vring_info;
41 struct vring_alloc_info *vring_alloc;
42 int ret;
43 (void)flags;
44
45 /* Get the vring information */
46 vring_info = &vdev->vrings_info[idx];
47 vring_alloc = &vring_info->info;
48
49 /* Fail if the virtqueue has already been created */
50 if (vring_info->vq)
51 return ERROR_VQUEUE_INVLD_PARAM;
52
53 /* Alloc the virtqueue and init it */
54 vring_info->vq = virtqueue_allocate(vring_alloc->num_descs);
55 if (!vring_info->vq)
56 return ERROR_NO_MEM;
57
58 if (VIRTIO_ROLE_IS_DRIVER(vdev)) {
59 size_t offset = metal_io_virt_to_offset(vring_info->io, vring_alloc->vaddr);
60 size_t size = vring_size(vring_alloc->num_descs, vring_alloc->align);
61
62 metal_io_block_set(vring_info->io, offset, 0, size);
63 }
64
65 ret = virtqueue_create(vdev, idx, name, vring_alloc, callback,
66 vdev->func->notify, vring_info->vq);
67 if (ret)
68 return ret;
69
70 return 0;
71 }
72
rproc_virtio_create_virtqueues(struct virtio_device * vdev,unsigned int flags,unsigned int nvqs,const char * names[],vq_callback callbacks[],void * callback_args[])73 static int rproc_virtio_create_virtqueues(struct virtio_device *vdev,
74 unsigned int flags,
75 unsigned int nvqs,
76 const char *names[],
77 vq_callback callbacks[],
78 void *callback_args[])
79 {
80 unsigned int i;
81 int ret;
82 (void)callback_args;
83
84 /* Check virtqueue numbers and the vrings_info */
85 if (nvqs > vdev->vrings_num || !vdev || !vdev->vrings_info)
86 return ERROR_VQUEUE_INVLD_PARAM;
87
88 /* set the notification id for vrings */
89 for (i = 0; i < nvqs; i++) {
90 ret = rproc_virtio_create_virtqueue(vdev, flags, i, names[i], callbacks[i]);
91 if (ret)
92 goto err;
93 }
94 return 0;
95
96 err:
97 rproc_virtio_delete_virtqueues(vdev);
98 return ret;
99 }
100
rproc_virtio_virtqueue_notify(struct virtqueue * vq)101 static void rproc_virtio_virtqueue_notify(struct virtqueue *vq)
102 {
103 struct remoteproc_virtio *rpvdev;
104 struct virtio_vring_info *vring_info;
105 struct virtio_device *vdev;
106 unsigned int vq_id = vq->vq_queue_index;
107
108 vdev = vq->vq_dev;
109 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
110 metal_assert(vq_id < vdev->vrings_num);
111 vring_info = &vdev->vrings_info[vq_id];
112 rpvdev->notify(rpvdev->priv, vring_info->notifyid);
113 }
114
rproc_virtio_get_status(struct virtio_device * vdev)115 static unsigned char rproc_virtio_get_status(struct virtio_device *vdev)
116 {
117 struct remoteproc_virtio *rpvdev;
118 struct fw_rsc_vdev *vdev_rsc;
119 struct metal_io_region *io;
120 char status;
121
122 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
123 vdev_rsc = rpvdev->vdev_rsc;
124 io = rpvdev->vdev_rsc_io;
125 RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
126 status = metal_io_read8(io,
127 metal_io_virt_to_offset(io, &vdev_rsc->status));
128 return status;
129 }
130
131 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
rproc_virtio_set_status(struct virtio_device * vdev,unsigned char status)132 static void rproc_virtio_set_status(struct virtio_device *vdev,
133 unsigned char status)
134 {
135 struct remoteproc_virtio *rpvdev;
136 struct fw_rsc_vdev *vdev_rsc;
137 struct metal_io_region *io;
138
139 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
140 vdev_rsc = rpvdev->vdev_rsc;
141 io = rpvdev->vdev_rsc_io;
142 metal_io_write8(io,
143 metal_io_virt_to_offset(io, &vdev_rsc->status),
144 status);
145 RSC_TABLE_FLUSH(vdev_rsc, sizeof(struct fw_rsc_vdev));
146 rpvdev->notify(rpvdev->priv, vdev->notifyid);
147 }
148 #endif
149
rproc_virtio_get_dfeatures(struct virtio_device * vdev)150 static uint32_t rproc_virtio_get_dfeatures(struct virtio_device *vdev)
151 {
152 struct remoteproc_virtio *rpvdev;
153 struct fw_rsc_vdev *vdev_rsc;
154 struct metal_io_region *io;
155 uint32_t features;
156
157 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
158 vdev_rsc = rpvdev->vdev_rsc;
159 io = rpvdev->vdev_rsc_io;
160 RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
161 features = metal_io_read32(io,
162 metal_io_virt_to_offset(io, &vdev_rsc->dfeatures));
163
164 return features;
165 }
166
rproc_virtio_get_features(struct virtio_device * vdev)167 static uint32_t rproc_virtio_get_features(struct virtio_device *vdev)
168 {
169 struct remoteproc_virtio *rpvdev;
170 struct fw_rsc_vdev *vdev_rsc;
171 struct metal_io_region *io;
172 uint32_t gfeatures;
173 uint32_t dfeatures;
174
175 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
176 vdev_rsc = rpvdev->vdev_rsc;
177 io = rpvdev->vdev_rsc_io;
178 RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
179 gfeatures = metal_io_read32(io,
180 metal_io_virt_to_offset(io, &vdev_rsc->gfeatures));
181 dfeatures = rproc_virtio_get_dfeatures(vdev);
182
183 return dfeatures & gfeatures;
184 }
185
186 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
rproc_virtio_set_features(struct virtio_device * vdev,uint32_t features)187 static void rproc_virtio_set_features(struct virtio_device *vdev,
188 uint32_t features)
189 {
190 struct remoteproc_virtio *rpvdev;
191 struct fw_rsc_vdev *vdev_rsc;
192 struct metal_io_region *io;
193
194 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
195 vdev_rsc = rpvdev->vdev_rsc;
196 io = rpvdev->vdev_rsc_io;
197 metal_io_write32(io,
198 metal_io_virt_to_offset(io, &vdev_rsc->gfeatures),
199 features);
200 RSC_TABLE_FLUSH(vdev_rsc, sizeof(struct fw_rsc_vdev));
201 rpvdev->notify(rpvdev->priv, vdev->notifyid);
202 }
203
rproc_virtio_negotiate_features(struct virtio_device * vdev,uint32_t features)204 static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev,
205 uint32_t features)
206 {
207 features = features & rproc_virtio_get_dfeatures(vdev);
208 rproc_virtio_set_features(vdev, features);
209
210 /* return the mask of features successfully negotiated */
211 return features;
212 }
213 #endif
214
rproc_virtio_read_config(struct virtio_device * vdev,uint32_t offset,void * dst,int length)215 static void rproc_virtio_read_config(struct virtio_device *vdev,
216 uint32_t offset, void *dst, int length)
217 {
218 struct remoteproc_virtio *rpvdev;
219 struct fw_rsc_vdev *vdev_rsc;
220 struct metal_io_region *io;
221 char *config;
222
223 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
224 vdev_rsc = rpvdev->vdev_rsc;
225 config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
226 io = rpvdev->vdev_rsc_io;
227
228 if (offset + length <= vdev_rsc->config_len) {
229 RSC_TABLE_INVALIDATE(config + offset, length);
230 metal_io_block_read(io,
231 metal_io_virt_to_offset(io, config + offset),
232 dst, length);
233 }
234 }
235
236 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
rproc_virtio_write_config(struct virtio_device * vdev,uint32_t offset,void * src,int length)237 static void rproc_virtio_write_config(struct virtio_device *vdev,
238 uint32_t offset, void *src, int length)
239 {
240 struct remoteproc_virtio *rpvdev;
241 struct fw_rsc_vdev *vdev_rsc;
242 struct metal_io_region *io;
243 char *config;
244
245 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
246 vdev_rsc = rpvdev->vdev_rsc;
247 config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
248 io = rpvdev->vdev_rsc_io;
249
250 if (offset + length <= vdev_rsc->config_len) {
251 metal_io_block_write(io,
252 metal_io_virt_to_offset(io, config + offset),
253 src, length);
254 RSC_TABLE_FLUSH(config + offset, length);
255 rpvdev->notify(rpvdev->priv, vdev->notifyid);
256 }
257 }
258
rproc_virtio_reset_device(struct virtio_device * vdev)259 static void rproc_virtio_reset_device(struct virtio_device *vdev)
260 {
261 if (vdev->role == VIRTIO_DEV_DRIVER)
262 rproc_virtio_set_status(vdev,
263 VIRTIO_CONFIG_STATUS_NEEDS_RESET);
264 }
265 #endif
266
267 static const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = {
268 .create_virtqueues = rproc_virtio_create_virtqueues,
269 .delete_virtqueues = rproc_virtio_delete_virtqueues,
270 .get_status = rproc_virtio_get_status,
271 .get_features = rproc_virtio_get_features,
272 .read_config = rproc_virtio_read_config,
273 .notify = rproc_virtio_virtqueue_notify,
274 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
275 /*
276 * We suppose here that the vdev is in a shared memory so that can
277 * be access only by one core: the host. In this case salve core has
278 * only read access right.
279 */
280 .set_status = rproc_virtio_set_status,
281 .set_features = rproc_virtio_set_features,
282 .negotiate_features = rproc_virtio_negotiate_features,
283 .write_config = rproc_virtio_write_config,
284 .reset_device = rproc_virtio_reset_device,
285 #endif
286 };
287
288 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)289 rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
290 void *rsc, struct metal_io_region *rsc_io,
291 void *priv,
292 rpvdev_notify_func notify,
293 virtio_dev_reset_cb rst_cb)
294 {
295 struct remoteproc_virtio *rpvdev;
296 struct virtio_vring_info *vrings_info;
297 struct fw_rsc_vdev *vdev_rsc = rsc;
298 struct virtio_device *vdev;
299 unsigned int num_vrings = vdev_rsc->num_of_vrings;
300
301 rpvdev = metal_allocate_memory(sizeof(*rpvdev));
302 if (!rpvdev)
303 return NULL;
304 vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings);
305 if (!vrings_info)
306 goto err;
307 memset(rpvdev, 0, sizeof(*rpvdev));
308 memset(vrings_info, 0, sizeof(*vrings_info) * num_vrings);
309
310 /* Initialize the remoteproc virtio */
311 rpvdev->notify = notify;
312 rpvdev->priv = priv;
313 /* Assuming the shared memory has been mapped and registered if
314 * necessary
315 */
316 rpvdev->vdev_rsc = vdev_rsc;
317 rpvdev->vdev_rsc_io = rsc_io;
318
319 /* Initialize the virtio device */
320 vdev = &rpvdev->vdev;
321 vdev->vrings_info = vrings_info;
322 vdev->notifyid = notifyid;
323 vdev->id.device = vdev_rsc->id;
324 vdev->role = role;
325 vdev->reset_cb = rst_cb;
326 vdev->vrings_num = num_vrings;
327 vdev->func = &remoteproc_virtio_dispatch_funcs;
328
329 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
330 if (role == VIRTIO_DEV_DRIVER) {
331 uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev);
332 /* Assume the virtio driver support all remote features */
333 rproc_virtio_negotiate_features(vdev, dfeatures);
334 }
335 #endif
336
337 return &rpvdev->vdev;
338 err:
339 metal_free_memory(rpvdev);
340 return NULL;
341 }
342
rproc_virtio_remove_vdev(struct virtio_device * vdev)343 void rproc_virtio_remove_vdev(struct virtio_device *vdev)
344 {
345 struct remoteproc_virtio *rpvdev;
346
347 if (!vdev)
348 return;
349 rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
350 if (vdev->vrings_info)
351 metal_free_memory(vdev->vrings_info);
352 metal_free_memory(rpvdev);
353 }
354
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)355 int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
356 unsigned int notifyid, void *va,
357 struct metal_io_region *io,
358 unsigned int num_descs, unsigned int align)
359 {
360 struct virtio_vring_info *vring_info;
361 unsigned int num_vrings;
362
363 num_vrings = vdev->vrings_num;
364 if ((index >= num_vrings) || (num_descs > RPROC_MAX_VRING_DESC))
365 return -RPROC_EINVAL;
366 vring_info = &vdev->vrings_info[index];
367 vring_info->io = io;
368 vring_info->notifyid = notifyid;
369 vring_info->info.vaddr = va;
370 vring_info->info.num_descs = num_descs;
371 vring_info->info.align = align;
372
373 return 0;
374 }
375
rproc_virtio_notified(struct virtio_device * vdev,uint32_t notifyid)376 int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid)
377 {
378 unsigned int num_vrings, i;
379 struct virtio_vring_info *vring_info;
380 struct virtqueue *vq;
381
382 if (!vdev)
383 return -RPROC_EINVAL;
384 /* We do nothing for vdev notification in this implementation */
385 if (vdev->notifyid == notifyid)
386 return 0;
387 num_vrings = vdev->vrings_num;
388 for (i = 0; i < num_vrings; i++) {
389 vring_info = &vdev->vrings_info[i];
390 if (vring_info->notifyid == notifyid ||
391 notifyid == RSC_NOTIFY_ID_ANY) {
392 vq = vring_info->vq;
393 virtqueue_notification(vq);
394 }
395 }
396 return 0;
397 }
398
rproc_virtio_wait_remote_ready(struct virtio_device * vdev)399 void rproc_virtio_wait_remote_ready(struct virtio_device *vdev)
400 {
401 uint8_t status;
402
403 /*
404 * No status available for remote. As virtio driver has not to wait
405 * remote action, we can return. Behavior should be updated
406 * in future if a remote status is added.
407 */
408 if (VIRTIO_ROLE_IS_DRIVER(vdev))
409 return;
410
411 while (1) {
412 status = rproc_virtio_get_status(vdev);
413 if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
414 return;
415 metal_yield();
416 }
417 }
418