blob: 71d1ceb8c853203d25faa3fc8a58feaa2dcc7429 [file] [log] [blame]
Rui Miguel Silva909ff4d2021-12-03 19:00:54 +00001/*
2 * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
3 * Copyright (c) 2021-2022, Linaro Limited. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#include <metal/device.h>
9#include <metal/spinlock.h>
10#include <openamp/open_amp.h>
11#include <platform/interface/device_region.h>
12#include <config/interface/config_store.h>
13
14#include <errno.h>
15#include <stddef.h>
16#include <trace.h>
17#include "openamp_messenger_api.h"
18
19#define OPENAMP_SHEM_DEVICE_NAME "openamp-virtio"
20#define OPENAMP_RPMSG_ENDPOINT_NAME OPENAMP_SHEM_DEVICE_NAME
21#define OPENAMP_RPMSG_ENDPOINT_ADDR 1024
22
23#define OPENAMP_SHEM_PHYS 0x88000000
24#define OPENAMP_SHEM_PHYS_PAGES 1
25#define OPENAMP_SHEM_SE_PHYS 0xa8000000
26
27#define OPENAMP_SHEM_VDEV_SIZE (4 * 1024)
28#define OPENAMP_SHEM_VRING_SIZE (4 * 1024)
29
30#define OPENAMP_BUFFER_NO_WAIT 0
31#define OPENAMP_BUFFER_WAIT 1
32
33#define VIRTQUEUE_NR 2
34#define VQ_TX 0
35#define VQ_RX 1
36
37#define VRING_DESCRIPTORS 16
38#define VRING_ALIGN 4
39
40#define container_of(ptr, type, member) \
41 ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
42
43struct openamp_virtio_shm {
44 uintptr_t base_addr;
45 size_t size;
46 uintptr_t vdev_status;
47 size_t vdev_status_size;
48 uintptr_t payload_addr;
49 size_t payload_size;
50 uintptr_t vring_tx;
51 size_t vring_tx_size;
52 uintptr_t vring_rx;
53 size_t vring_rx_size;
54
55 metal_phys_addr_t shm_physmap[OPENAMP_SHEM_PHYS_PAGES];
56};
57
58struct openamp_virtio_metal {
59 struct metal_spinlock lock;
60 struct metal_device shm_dev;
61 struct metal_device *io_dev;
62
63 struct metal_io_region *io;
64 struct openamp_virtio_shm shm;
65};
66
67struct openamp_virtio_device {
68 struct virtio_device virtio_dev;
69 struct virtqueue *vq[VIRTQUEUE_NR];
70 struct virtio_vring_info rvrings[VIRTQUEUE_NR];
71};
72
73struct openamp_virtio_rpmsg {
74 struct rpmsg_virtio_device rpmsg_vdev;
75 struct rpmsg_endpoint ep;
76 uint8_t *req_buf;
77 uint32_t req_len;
78 uint8_t *resp_buf;
79 size_t resp_len;
80};
81
82struct openamp_virtio {
83 struct openamp_messenger *openamp;
84 struct openamp_virtio_rpmsg rpmsg;
85 struct openamp_virtio_device vdev;
86 struct openamp_virtio_metal metal;
87};
88
89static struct openamp_virtio *openamp_virtio_from_dev(struct virtio_device *vdev)
90{
91 struct openamp_virtio_device *openamp_vdev;
92
93 openamp_vdev = container_of(vdev, struct openamp_virtio_device,
94 virtio_dev);
95
96 return container_of(openamp_vdev, struct openamp_virtio, vdev);
97}
98
99static struct openamp_virtio_rpmsg *openamp_virtio_rpmsg_from_dev(struct rpmsg_device *rdev)
100{
101 struct rpmsg_virtio_device *rvdev;
102
103 rvdev = container_of(rdev, struct rpmsg_virtio_device, rdev);
104
105 return container_of(rvdev, struct openamp_virtio_rpmsg, rpmsg_vdev);
106
107}
108
109static void openamp_virtio_metal_device_setup(struct metal_device *shm_dev,
110 struct openamp_virtio_shm *shm)
111{
112 struct metal_io_region *shm_region;
113
114 shm_region = &shm_dev->regions[0];
115
116 shm_dev->name = OPENAMP_SHEM_DEVICE_NAME;
117 shm_dev->num_regions = 1;
118
119 shm_region->virt = (void *)shm->payload_addr;
120 shm_region->size = shm->payload_size;
121
122 shm_region->physmap = (metal_phys_addr_t *)&shm->shm_physmap;
123 shm_region->page_shift = (metal_phys_addr_t)(-1);
124 shm_region->page_mask = (metal_phys_addr_t)(-1);
125}
126
127static int openamp_virtio_metal_init(struct openamp_virtio_metal *metal)
128{
129 struct metal_init_params params = METAL_INIT_DEFAULTS;
130 struct metal_device *shm_dev = &metal->shm_dev;
131 int ret;
132
133 openamp_virtio_metal_device_setup(shm_dev, &metal->shm);
134
135 metal_spinlock_init(&metal->lock);
136
137 ret = metal_init(&params);
138 if (ret < 0)
139 return ret;
140
141 ret = metal_register_generic_device(shm_dev);
142 if (ret < 0)
143 goto metal_finish;
144
145 ret = metal_device_open("generic", OPENAMP_SHEM_DEVICE_NAME,
146 &metal->io_dev);
147 if (ret < 0)
148 goto metal_finish;
149
150 metal->io = metal_device_io_region(metal->io_dev, 0);
151 if (!metal->io) {
152 EMSG("openamp: virtio: failed to init metal io");
153 ret = -EPROTO;
154 goto metal_finish;
155 }
156
157 return 0;
158
159metal_finish:
160 metal_finish();
161 return ret;
162}
163
164static unsigned char openamp_virtio_status_get(struct virtio_device *vdev)
165{
166 struct openamp_virtio *virtio = openamp_virtio_from_dev(vdev);
167 struct openamp_virtio_shm *shm = &virtio->metal.shm;
168
169 uint32_t status = *(volatile uint32_t *)shm->vdev_status;
170
171 return status;
172}
173
174static void openamp_virtio_status_set(struct virtio_device *vdev,
175 unsigned char status)
176{
177 struct openamp_virtio *virtio = openamp_virtio_from_dev(vdev);
178 struct openamp_virtio_shm *shm = &virtio->metal.shm;
179
180 *(volatile uint32_t *)shm->vdev_status = status;
181}
182
183static uint32_t openamp_virtio_features_get(struct virtio_device *vdev)
184{
185 return 1 << VIRTIO_RPMSG_F_NS;
186}
187
188static void openamp_virtio_notify(struct virtqueue *vq)
189{
190 struct openamp_virtio_device *openamp_vdev;
191 struct openamp_messenger *openamp;
192 struct openamp_virtio *virtio;
193 int ret;
194
195 openamp_vdev = container_of(vq->vq_dev, struct openamp_virtio_device, virtio_dev);
196 virtio = container_of(openamp_vdev, struct openamp_virtio, vdev);
197 openamp = virtio->openamp;
198
199 ret = openamp->platform_ops->transport_notify(openamp);
200 if (ret < 0)
201 EMSG("openamp: virtio: erro in transport_notify: %d", ret);
202}
203
204const static struct virtio_dispatch openamp_virtio_dispatch = {
205 .get_status = openamp_virtio_status_get,
206 .set_status = openamp_virtio_status_set,
207 .get_features = openamp_virtio_features_get,
208 .notify = openamp_virtio_notify,
209};
210
211static int openamp_virtio_device_setup(struct openamp_virtio *virtio)
212{
213 struct openamp_virtio_metal *metal = &virtio->metal;
214 struct openamp_virtio_device *openamp_vdev = &virtio->vdev;
215 struct virtio_device *vdev = &openamp_vdev->virtio_dev;
216 struct openamp_virtio_shm *shm = &metal->shm;
217 struct virtio_vring_info *rvring;
218
219 rvring = &openamp_vdev->rvrings[0];
220
221 vdev->role = RPMSG_REMOTE;
222 vdev->vrings_num = VIRTQUEUE_NR;
223 vdev->func = &openamp_virtio_dispatch;
224
225 openamp_vdev->vq[VQ_TX] = virtqueue_allocate(VRING_DESCRIPTORS);
226 if (!openamp_vdev->vq[VQ_TX]) {
227 EMSG("openamp: virtio: failed to allocate virtqueue 0");
228 return -ENOMEM;
229 }
230 rvring->io = metal->io;
231 rvring->info.vaddr = (void *)shm->vring_tx;
232 rvring->info.num_descs = VRING_DESCRIPTORS;
233 rvring->info.align = VRING_ALIGN;
234 rvring->vq = openamp_vdev->vq[VQ_TX];
235
236 openamp_vdev->vq[VQ_RX] = virtqueue_allocate(VRING_DESCRIPTORS);
237 if (!openamp_vdev->vq[VQ_RX]) {
238 EMSG("openamp: virtio: failed to allocate virtqueue 1");
239 goto free_vq;
240 }
241 rvring = &openamp_vdev->rvrings[VQ_RX];
242 rvring->io = metal->io;
243 rvring->info.vaddr = (void *)shm->vring_rx;
244 rvring->info.num_descs = VRING_DESCRIPTORS;
245 rvring->info.align = VRING_ALIGN;
246 rvring->vq = openamp_vdev->vq[VQ_RX];
247
248 vdev->vrings_info = &openamp_vdev->rvrings[0];
249
250 return 0;
251
252free_vq:
253 virtqueue_free(openamp_vdev->vq[VQ_TX]);
254 virtqueue_free(openamp_vdev->vq[VQ_RX]);
255
256 return -ENOMEM;
257}
258
259static int openamp_virtio_rpmsg_endpoint_callback(struct rpmsg_endpoint *ep,
260 void *data, size_t len,
261 uint32_t src, void *priv)
262{
263 struct openamp_virtio_rpmsg *vrpmsg;
264 struct rpmsg_device *rdev;
265
266 rdev = ep->rdev;
267 vrpmsg = openamp_virtio_rpmsg_from_dev(rdev);
268
269 rpmsg_hold_rx_buffer(ep, data);
270 vrpmsg->resp_buf = data;
271 vrpmsg->resp_len = len;
272
273 return 0;
274}
275
276static void openamp_virtio_rpmsg_service_unbind(struct rpmsg_endpoint *ep)
277{
278 struct openamp_virtio_rpmsg *vrpmsg;
279 struct rpmsg_device *rdev;
280
281 rdev = container_of(ep, struct rpmsg_device, ns_ept);
282 vrpmsg = openamp_virtio_rpmsg_from_dev(rdev);
283
284 rpmsg_destroy_ept(&vrpmsg->ep);
285}
286
287static void openamp_virtio_rpmsg_endpoint_bind(struct rpmsg_device *rdev,
288 const char *name,
289 unsigned int dest)
290{
291 struct openamp_virtio_rpmsg *vrpmsg;
292
293 vrpmsg = openamp_virtio_rpmsg_from_dev(rdev);
294
295 rpmsg_create_ept(&vrpmsg->ep, rdev, name, RPMSG_ADDR_ANY, dest,
296 openamp_virtio_rpmsg_endpoint_callback,
297 openamp_virtio_rpmsg_service_unbind);
298}
299
300static int openamp_virtio_rpmsg_device_setup(struct openamp_virtio *virtio,
301 struct device_region *virtio_dev)
302{
303 struct openamp_virtio_rpmsg *vrpmsg = &virtio->rpmsg;
304 struct rpmsg_virtio_device *rpmsg_vdev = &vrpmsg->rpmsg_vdev;
305 struct openamp_virtio_device *openamp_vdev = &virtio->vdev;
306 struct virtio_device *vdev = &openamp_vdev->virtio_dev;
307 struct openamp_virtio_metal *metal = &virtio->metal;
308 int ret;
309
310 /*
311 * we assume here that we are the client side and do not need to
312 * initialize the share memory poll (this is done at server side).
313 */
314 ret = rpmsg_init_vdev(rpmsg_vdev, vdev,
315 openamp_virtio_rpmsg_endpoint_bind, metal->io,
316 NULL);
317 if (ret < 0) {
318 EMSG("openamp: virtio: init vdev failed: %d", ret);
319 return ret;
320 }
321
322
323 ret = rpmsg_create_ept(&vrpmsg->ep, &rpmsg_vdev->rdev,
324 OPENAMP_RPMSG_ENDPOINT_NAME, RPMSG_ADDR_ANY,
325 RPMSG_ADDR_ANY,
326 openamp_virtio_rpmsg_endpoint_callback,
327 openamp_virtio_rpmsg_service_unbind);
328 if (ret < 0) {
329 EMSG("openamp: virtio: failed to create endpoint: %d", ret);
330 return ret;
331 }
332
333 /* set default remote addr */
334 vrpmsg->ep.dest_addr = OPENAMP_RPMSG_ENDPOINT_ADDR;
335
336 return 0;
337}
338
339static void openamp_virtio_shm_set(struct openamp_virtio *virtio,
340 struct device_region *virtio_region)
341{
342 struct openamp_virtio_shm *shm = &virtio->metal.shm;
343
344 shm->base_addr = virtio_region->base_addr;
345 shm->size = virtio_region->io_region_size;
346
347 shm->vdev_status = shm->base_addr;
348 shm->vdev_status_size = OPENAMP_SHEM_VDEV_SIZE;
349
350 shm->vring_rx = shm->base_addr + shm->size -
351 (2 * OPENAMP_SHEM_VRING_SIZE);
352 shm->vring_rx_size = OPENAMP_SHEM_VRING_SIZE;
353
354 shm->vring_tx = shm->vring_rx + shm->vring_rx_size;
355 shm->vring_tx_size = OPENAMP_SHEM_VRING_SIZE;
356
357 shm->payload_addr = shm->vdev_status + shm->vdev_status_size;
358 shm->payload_size = shm->size - shm->vdev_status_size -
359 shm->vring_rx_size - shm->vring_tx_size;
360
361 shm->shm_physmap[0] = OPENAMP_SHEM_PHYS + shm->vdev_status_size;
362
363 IMSG("SHEM: base: 0x%p size: %ld",
364 (void *)shm->base_addr, shm->size);
365 IMSG("VDEV: base: 0x%p size: %ld",
366 (void *)shm->vdev_status, shm->vdev_status_size);
367 IMSG("PAYLOAD: base: 0x%p size: %ld",
368 (void *)shm->payload_addr, shm->payload_size);
369 IMSG("VRING_TX: base: 0x%p size: %ld",
370 (void *)shm->vring_tx, shm->vring_tx_size);
371 IMSG("VRING_RX: base: 0x%p size: %ld",
372 (void *)shm->vring_rx, shm->vring_rx_size);
373 IMSG("PHYMAP: base: 0x%p", (void *)shm->shm_physmap[0]);
374}
375
376static int openamp_virtio_device_get(const char *dev,
377 struct device_region *dev_region)
378{
379 bool found;
380
381 found = config_store_query(CONFIG_CLASSIFIER_DEVICE_REGION, dev, 0,
382 dev_region, sizeof(*dev_region));
383 if (!found) {
384 EMSG("openamp: virtio: device region not found: %s", dev);
385 return -EINVAL;
386 }
387
388 if (dev_region->base_addr == 0 || dev_region->io_region_size == 0) {
389 EMSG("openamp: virtio: device region not valid");
390 return -EINVAL;
391 }
392
393 IMSG("openamp: virtio: device region found: %s addr: 0x%p size: %ld",
394 dev, (void *)dev_region->base_addr, dev_region->io_region_size);
395
396 return 0;
397}
398
399int openamp_virtio_call_begin(struct openamp_messenger *openamp, uint8_t **req_buf,
400 size_t req_len)
401{
402 struct openamp_virtio *virtio = openamp->platform;
403 struct openamp_virtio_rpmsg *vrpmsg = &virtio->rpmsg;
404 struct rpmsg_endpoint *ep = &vrpmsg->ep;
405
406
407 *req_buf = rpmsg_get_tx_payload_buffer(ep, &vrpmsg->req_len,
408 OPENAMP_BUFFER_WAIT);
409 if (*req_buf == NULL)
410 return -EINVAL;
411
412 if (vrpmsg->req_len < req_len)
413 return -E2BIG;
414
415 vrpmsg->req_buf = *req_buf;
416
417 return 0;
418}
419
420int openamp_virtio_call_invoke(struct openamp_messenger *openamp,
421 uint8_t **resp_buf, size_t *resp_len)
422{
423 const struct openamp_platform_ops *ops = openamp->platform_ops;
424 struct openamp_virtio *virtio = openamp->platform;
425 struct openamp_virtio_device *openamp_vdev = &virtio->vdev;
426 struct openamp_virtio_rpmsg *vrpmsg = &virtio->rpmsg;
427 struct rpmsg_endpoint *ep = &vrpmsg->ep;
428 int ret;
429
430 ret = rpmsg_send_nocopy(ep, vrpmsg->req_buf, vrpmsg->req_len);
431 if (ret < 0) {
432 EMSG("openamp: virtio: send nocopy failed: %d", ret);
433 return -EIO;
434 }
435
436 if (ret != vrpmsg->req_len) {
437 EMSG("openamp: virtio: send less bytes %d than requested %d",
438 ret, vrpmsg->req_len);
439 return -EIO;
440 }
441
442 if (!ops->transport_receive)
443 return 0;
444
445 ret = ops->transport_receive(openamp);
446 if (ret < 0) {
447 EMSG("openamp: virtio: failed transport_receive");
448 return -EIO;
449 }
450
451 virtqueue_notification(openamp_vdev->vq[VQ_RX]);
452
453 *resp_buf = vrpmsg->resp_buf;
454 *resp_len = vrpmsg->resp_len;
455
456 return 0;
457}
458
459void openamp_virtio_call_end(struct openamp_messenger *openamp)
460{
461 struct openamp_virtio *virtio = openamp->platform;
462 struct openamp_virtio_rpmsg *vrpmsg = &virtio->rpmsg;
463
464 rpmsg_release_rx_buffer(&vrpmsg->ep, vrpmsg->resp_buf);
465
466 vrpmsg->req_buf = NULL;
467 vrpmsg->req_len = 0;
468 vrpmsg->resp_buf = NULL;
469 vrpmsg->resp_len = 0;
470}
471
472void *openamp_virtio_virt_to_phys(struct openamp_messenger *openamp, void *va)
473{
474 struct openamp_virtio *virtio = openamp->platform;
475 struct openamp_virtio_metal *metal = &virtio->metal;
476
477 return (void *)metal_io_virt_to_phys(metal->io, va);
478}
479
480void *openamp_virtio_phys_to_virt(struct openamp_messenger *openamp, void *pa)
481{
482 struct openamp_virtio *virtio = openamp->platform;
483 struct openamp_virtio_metal *metal = &virtio->metal;
484
485 return metal_io_phys_to_virt(metal->io, (metal_phys_addr_t)pa);
486}
487
488int openamp_virtio_init(struct openamp_messenger *openamp)
489{
490 struct device_region virtio_dev;
491 struct openamp_virtio *virtio;
492 int ret;
493
494 if (openamp->platform)
495 return 0;
496
497
498 virtio = malloc(sizeof(*virtio));
499 if (!virtio)
500 return -ENOMEM;
501
502 virtio->openamp = openamp;
503
504 ret = openamp_virtio_device_get(OPENAMP_SHEM_DEVICE_NAME, &virtio_dev);
505 if (ret < 0)
506 goto free_virtio;
507
508 openamp_virtio_shm_set(virtio, &virtio_dev);
509
510 ret = openamp_virtio_metal_init(&virtio->metal);
511 if (ret < 0)
512 goto free_virtio;
513
514 ret = openamp_virtio_device_setup(virtio);
515 if (ret < 0)
516 goto finish_metal;
517
518 ret = openamp_virtio_rpmsg_device_setup(virtio, &virtio_dev);
519 if (ret < 0) {
520 EMSG("openamp: virtio: rpmsg device setup failed: %d", ret);
521 goto finish_metal;
522 }
523
524 openamp->platform = virtio;
525
526 return 0;
527
528finish_metal:
529 metal_finish();
530
531free_virtio:
532 free(virtio);
533
534 return ret;
535}
536
537int openamp_virtio_deinit(struct openamp_messenger *openamp)
538{
539 struct openamp_virtio *virtio;
540
541 if (!openamp->platform)
542 return 0;
543
544 virtio = openamp->platform;
545
546 metal_finish();
547 free(virtio);
548
549 openamp->platform = NULL;
550
551 return 0;
552}