virtio-mmio.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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 "devicetree.h"
  10. #include "alloc.h"
  11. #include "asm/page.h"
  12. #include "asm/io.h"
  13. #include "virtio.h"
  14. #include "virtio-mmio.h"
  15. static void vm_get(struct virtio_device *vdev, unsigned offset,
  16. void *buf, unsigned len)
  17. {
  18. struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  19. u8 *p = buf;
  20. unsigned i;
  21. for (i = 0; i < len; ++i)
  22. p[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
  23. }
  24. static void vm_set(struct virtio_device *vdev, unsigned offset,
  25. const void *buf, unsigned len)
  26. {
  27. struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  28. const u8 *p = buf;
  29. unsigned i;
  30. for (i = 0; i < len; ++i)
  31. writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
  32. }
  33. static bool vm_notify(struct virtqueue *vq)
  34. {
  35. struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
  36. writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
  37. return true;
  38. }
  39. static struct virtqueue *vm_setup_vq(struct virtio_device *vdev,
  40. unsigned index,
  41. void (*callback)(struct virtqueue *vq),
  42. const char *name)
  43. {
  44. struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  45. struct vring_virtqueue *vq;
  46. void *queue;
  47. unsigned num = VIRTIO_MMIO_QUEUE_NUM_MIN;
  48. vq = calloc(1, sizeof(*vq));
  49. queue = memalign(PAGE_SIZE, VIRTIO_MMIO_QUEUE_SIZE_MIN);
  50. assert(vq && queue);
  51. writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
  52. assert(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX) >= num);
  53. if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN) != 0) {
  54. printf("%s: virtqueue %d already setup! base=%p\n",
  55. __func__, index, vm_dev->base);
  56. return NULL;
  57. }
  58. writel(num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
  59. writel(VIRTIO_MMIO_VRING_ALIGN,
  60. vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
  61. writel(virt_to_pfn(queue), vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
  62. vring_init_virtqueue(vq, index, num, VIRTIO_MMIO_VRING_ALIGN,
  63. vdev, queue, vm_notify, callback, name);
  64. return &vq->vq;
  65. }
  66. static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
  67. struct virtqueue *vqs[], vq_callback_t *callbacks[],
  68. const char *names[])
  69. {
  70. unsigned i;
  71. for (i = 0; i < nvqs; ++i) {
  72. vqs[i] = vm_setup_vq(vdev, i,
  73. callbacks ? callbacks[i] : NULL,
  74. names ? names[i] : "");
  75. if (vqs[i] == NULL)
  76. return -1;
  77. }
  78. return 0;
  79. }
  80. static const struct virtio_config_ops vm_config_ops = {
  81. .get = vm_get,
  82. .set = vm_set,
  83. .find_vqs = vm_find_vqs,
  84. };
  85. static void vm_device_init(struct virtio_mmio_device *vm_dev)
  86. {
  87. vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
  88. vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
  89. vm_dev->vdev.config = &vm_config_ops;
  90. writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
  91. }
  92. /******************************************************
  93. * virtio-mmio device tree support
  94. ******************************************************/
  95. struct vm_dt_info {
  96. u32 devid;
  97. void *base;
  98. };
  99. static int vm_dt_match(const struct dt_device *dev, int fdtnode)
  100. {
  101. struct vm_dt_info *info = (struct vm_dt_info *)dev->info;
  102. struct dt_pbus_reg base;
  103. u32 magic;
  104. int ret;
  105. dt_device_bind_node((struct dt_device *)dev, fdtnode);
  106. ret = dt_pbus_get_base(dev, &base);
  107. assert(ret == 0);
  108. info->base = ioremap(base.addr, base.size);
  109. magic = readl(info->base + VIRTIO_MMIO_MAGIC_VALUE);
  110. if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24))
  111. return false;
  112. return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid;
  113. }
  114. static struct virtio_device *virtio_mmio_dt_bind(u32 devid)
  115. {
  116. struct virtio_mmio_device *vm_dev;
  117. struct dt_device dt_dev;
  118. struct dt_bus dt_bus;
  119. struct vm_dt_info info;
  120. int node;
  121. if (!dt_available())
  122. return NULL;
  123. dt_bus_init_defaults(&dt_bus);
  124. dt_bus.match = vm_dt_match;
  125. info.devid = devid;
  126. dt_device_init(&dt_dev, &dt_bus, &info);
  127. node = dt_device_find_compatible(&dt_dev, "virtio,mmio");
  128. assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
  129. if (node == -FDT_ERR_NOTFOUND)
  130. return NULL;
  131. vm_dev = calloc(1, sizeof(*vm_dev));
  132. assert(vm_dev != NULL);
  133. vm_dev->base = info.base;
  134. vm_device_init(vm_dev);
  135. return &vm_dev->vdev;
  136. }
  137. struct virtio_device *virtio_mmio_bind(u32 devid)
  138. {
  139. return virtio_mmio_dt_bind(devid);
  140. }