Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libvirtio / virtio.c
diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.c b/qemu/roms/SLOF/lib/libvirtio/virtio.c
new file mode 100644 (file)
index 0000000..f9c00a6
--- /dev/null
@@ -0,0 +1,241 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <cpu.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio.h"
+
+/* PCI virtio header offsets */
+#define VIRTIOHDR_DEVICE_FEATURES      0
+#define VIRTIOHDR_GUEST_FEATURES       4
+#define VIRTIOHDR_QUEUE_ADDRESS        8
+#define VIRTIOHDR_QUEUE_SIZE           12
+#define VIRTIOHDR_QUEUE_SELECT         14
+#define VIRTIOHDR_QUEUE_NOTIFY         16
+#define VIRTIOHDR_DEVICE_STATUS        18
+#define VIRTIOHDR_ISR_STATUS           19
+#define VIRTIOHDR_DEVICE_CONFIG        20
+
+
+/**
+ * Calculate ring size according to queue size number
+ */
+unsigned long virtio_vring_size(unsigned int qsize)
+{
+       return VQ_ALIGN(sizeof(struct vring_desc) * qsize +
+                        sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) +
+               VQ_ALIGN(sizeof(struct vring_used) +
+                        sizeof(struct vring_used_elem) * qsize);
+}
+
+
+/**
+ * Get number of elements in a vring
+ * @param   dev  pointer to virtio device information
+ * @param   queue virtio queue number
+ * @return  number of elements
+ */
+int virtio_get_qsize(struct virtio_device *dev, int queue)
+{
+       int size = 0;
+
+       if (dev->type == VIRTIO_TYPE_PCI) {
+               ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT,
+                           cpu_to_le16(queue));
+               eieio();
+               size = le16_to_cpu(ci_read_16(dev->base+VIRTIOHDR_QUEUE_SIZE));
+       }
+
+       return size;
+}
+
+
+/**
+ * Get address of descriptor vring
+ * @param   dev  pointer to virtio device information
+ * @param   queue virtio queue number
+ * @return  pointer to the descriptor ring
+ */
+struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue)
+{
+       struct vring_desc *desc = 0;
+
+       if (dev->type == VIRTIO_TYPE_PCI) {
+               ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT,
+                           cpu_to_le16(queue));
+               eieio();
+               desc = (void*)(4096L *
+                  le32_to_cpu(ci_read_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS)));
+       }
+
+       return desc;
+}
+
+
+/**
+ * Get address of "available" vring
+ * @param   dev  pointer to virtio device information
+ * @param   queue virtio queue number
+ * @return  pointer to the "available" ring
+ */
+struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue)
+{
+       return (void*)((uint64_t)virtio_get_vring_desc(dev, queue)
+                       + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc));
+}
+
+
+/**
+ * Get address of "used" vring
+ * @param   dev  pointer to virtio device information
+ * @param   queue virtio queue number
+ * @return  pointer to the "used" ring
+ */
+struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue)
+{
+       return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue)
+                                 + virtio_get_qsize(dev, queue)
+                                   * sizeof(struct vring_avail));
+}
+
+
+/**
+ * Reset virtio device
+ */
+void virtio_reset_device(struct virtio_device *dev)
+{
+       if (dev->type == VIRTIO_TYPE_PCI) {
+               ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, 0);
+       }
+}
+
+
+/**
+ * Notify hypervisor about queue update
+ */
+void virtio_queue_notify(struct virtio_device *dev, int queue)
+{
+       if (dev->type == VIRTIO_TYPE_PCI) {
+               ci_write_16(dev->base+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue));
+       }
+}
+
+/**
+ * Set queue address
+ */
+void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr)
+{
+        if (dev->type == VIRTIO_TYPE_PCI) {
+                uint32_t val = qaddr;
+                val = val >> 12;
+                ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT,
+                            cpu_to_le16(queue));
+                eieio();
+                ci_write_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS,
+                            cpu_to_le32(val));
+        }
+}
+
+/**
+ * Set device status bits
+ */
+void virtio_set_status(struct virtio_device *dev, int status)
+{
+       if (dev->type == VIRTIO_TYPE_PCI) {
+               ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, status);
+       }
+}
+
+
+/**
+ * Set guest feature bits
+ */
+void virtio_set_guest_features(struct virtio_device *dev, int features)
+
+{
+       if (dev->type == VIRTIO_TYPE_PCI) {
+               ci_write_32(dev->base+VIRTIOHDR_GUEST_FEATURES, bswap_32(features));
+       }
+}
+
+/**
+ * Get host feature bits
+ */
+void virtio_get_host_features(struct virtio_device *dev, int *features)
+
+{
+       if (dev->type == VIRTIO_TYPE_PCI && features) {
+               *features = bswap_32(ci_read_32(dev->base+VIRTIOHDR_DEVICE_FEATURES));
+       }
+}
+
+
+/**
+ * Get additional config values
+ */
+uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size)
+{
+       uint64_t val = ~0ULL;
+       void *confbase;
+
+       switch (dev->type) {
+        case VIRTIO_TYPE_PCI:
+               confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG;
+               break;
+        default:
+               return ~0ULL;
+       }
+       switch (size) {
+        case 1:
+               val = ci_read_8(confbase+offset);
+               break;
+        case 2:
+               val = ci_read_16(confbase+offset);
+               break;
+        case 4:
+               val = ci_read_32(confbase+offset);
+               break;
+        case 8:
+               /* We don't support 8 bytes PIO accesses
+                * in qemu and this is all PIO
+                */
+               val = ci_read_32(confbase+offset);
+               val <<= 32;
+               val |= ci_read_32(confbase+offset+4);
+               break;
+       }
+
+       return val;
+}
+
+/**
+ * Get config blob
+ */
+int __virtio_read_config(struct virtio_device *dev, void *dst,
+                         int offset, int len)
+{
+       void *confbase;
+       unsigned char *buf = dst;
+       int i;
+
+       switch (dev->type) {
+        case VIRTIO_TYPE_PCI:
+               confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG;
+               break;
+        default:
+               return 0;
+       }
+       for (i = 0; i < len; i++)
+               buf[i] = ci_read_8(confbase + offset + i);
+       return len;
+}