virtio.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. * virtqueue support adapted from the Linux kernel.
  3. *
  4. * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
  5. *
  6. * This work is licensed under the terms of the GNU GPL, version 2.
  7. */
  8. #include "libcflat.h"
  9. #include "asm/io.h"
  10. #include "virtio.h"
  11. #include "virtio-mmio.h"
  12. void vring_init(struct vring *vr, unsigned int num, void *p,
  13. unsigned long align)
  14. {
  15. vr->num = num;
  16. vr->desc = p;
  17. vr->avail = p + num*sizeof(struct vring_desc);
  18. vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(u16)
  19. + align-1) & ~(align - 1));
  20. }
  21. void vring_init_virtqueue(struct vring_virtqueue *vq, unsigned index,
  22. unsigned num, unsigned vring_align,
  23. struct virtio_device *vdev, void *pages,
  24. bool (*notify)(struct virtqueue *),
  25. void (*callback)(struct virtqueue *),
  26. const char *name)
  27. {
  28. unsigned i;
  29. vring_init(&vq->vring, num, pages, vring_align);
  30. vq->vq.callback = callback;
  31. vq->vq.vdev = vdev;
  32. vq->vq.name = name;
  33. vq->vq.num_free = num;
  34. vq->vq.index = index;
  35. vq->notify = notify;
  36. vq->last_used_idx = 0;
  37. vq->num_added = 0;
  38. vq->free_head = 0;
  39. for (i = 0; i < num-1; i++) {
  40. vq->vring.desc[i].next = i+1;
  41. vq->data[i] = NULL;
  42. }
  43. vq->data[i] = NULL;
  44. }
  45. int virtqueue_add_outbuf(struct virtqueue *_vq, char *buf, unsigned int len)
  46. {
  47. struct vring_virtqueue *vq = to_vvq(_vq);
  48. unsigned avail;
  49. int head;
  50. assert(buf != NULL);
  51. assert(len != 0);
  52. if (!vq->vq.num_free)
  53. return -1;
  54. --vq->vq.num_free;
  55. head = vq->free_head;
  56. vq->vring.desc[head].flags = 0;
  57. vq->vring.desc[head].addr = virt_to_phys(buf);
  58. vq->vring.desc[head].len = len;
  59. vq->free_head = vq->vring.desc[head].next;
  60. vq->data[head] = buf;
  61. avail = (vq->vring.avail->idx & (vq->vring.num-1));
  62. vq->vring.avail->ring[avail] = head;
  63. wmb();
  64. vq->vring.avail->idx++;
  65. vq->num_added++;
  66. return 0;
  67. }
  68. bool virtqueue_kick(struct virtqueue *_vq)
  69. {
  70. struct vring_virtqueue *vq = to_vvq(_vq);
  71. mb();
  72. return vq->notify(_vq);
  73. }
  74. void detach_buf(struct vring_virtqueue *vq, unsigned head)
  75. {
  76. unsigned i = head;
  77. vq->data[head] = NULL;
  78. while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
  79. i = vq->vring.desc[i].next;
  80. vq->vq.num_free++;
  81. }
  82. vq->vring.desc[i].next = vq->free_head;
  83. vq->free_head = head;
  84. vq->vq.num_free++;
  85. }
  86. void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
  87. {
  88. struct vring_virtqueue *vq = to_vvq(_vq);
  89. u16 last_used;
  90. unsigned i;
  91. void *ret;
  92. rmb();
  93. last_used = (vq->last_used_idx & (vq->vring.num-1));
  94. i = vq->vring.used->ring[last_used].id;
  95. *len = vq->vring.used->ring[last_used].len;
  96. ret = vq->data[i];
  97. detach_buf(vq, i);
  98. vq->last_used_idx++;
  99. return ret;
  100. }
  101. struct virtio_device *virtio_bind(u32 devid)
  102. {
  103. return virtio_mmio_bind(devid);
  104. }