Add qemu 2.4.0
[kvmfornfv.git] / qemu / hw / net / rocker / rocker_desc.c
diff --git a/qemu/hw/net/rocker/rocker_desc.c b/qemu/hw/net/rocker/rocker_desc.c
new file mode 100644 (file)
index 0000000..9d896fe
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * QEMU rocker switch emulation - Descriptor ring support
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "net/net.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+
+#include "rocker.h"
+#include "rocker_hw.h"
+#include "rocker_desc.h"
+
+struct desc_ring {
+    hwaddr base_addr;
+    uint32_t size;
+    uint32_t head;
+    uint32_t tail;
+    uint32_t ctrl;
+    uint32_t credits;
+    Rocker *r;
+    DescInfo *info;
+    int index;
+    desc_ring_consume *consume;
+    unsigned msix_vector;
+};
+
+struct desc_info {
+    DescRing *ring;
+    RockerDesc desc;
+    char *buf;
+    size_t buf_size;
+};
+
+uint16_t desc_buf_size(DescInfo *info)
+{
+    return le16_to_cpu(info->desc.buf_size);
+}
+
+uint16_t desc_tlv_size(DescInfo *info)
+{
+    return le16_to_cpu(info->desc.tlv_size);
+}
+
+char *desc_get_buf(DescInfo *info, bool read_only)
+{
+    PCIDevice *dev = PCI_DEVICE(info->ring->r);
+    size_t size = read_only ? le16_to_cpu(info->desc.tlv_size) :
+                              le16_to_cpu(info->desc.buf_size);
+
+    if (size > info->buf_size) {
+        info->buf = g_realloc(info->buf, size);
+        info->buf_size = size;
+    }
+
+    if (!info->buf) {
+        return NULL;
+    }
+
+    if (pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size)) {
+        return NULL;
+    }
+
+    return info->buf;
+}
+
+int desc_set_buf(DescInfo *info, size_t tlv_size)
+{
+    PCIDevice *dev = PCI_DEVICE(info->ring->r);
+
+    if (tlv_size > info->buf_size) {
+        DPRINTF("ERROR: trying to write more to desc buf than it "
+                "can hold buf_size %zu tlv_size %zu\n",
+                info->buf_size, tlv_size);
+        return -ROCKER_EMSGSIZE;
+    }
+
+    info->desc.tlv_size = cpu_to_le16(tlv_size);
+    pci_dma_write(dev, le64_to_cpu(info->desc.buf_addr), info->buf, tlv_size);
+
+    return ROCKER_OK;
+}
+
+DescRing *desc_get_ring(DescInfo *info)
+{
+    return info->ring;
+}
+
+int desc_ring_index(DescRing *ring)
+{
+    return ring->index;
+}
+
+static bool desc_ring_empty(DescRing *ring)
+{
+    return ring->head == ring->tail;
+}
+
+bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr)
+{
+    if (base_addr & 0x7) {
+        DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
+                ") not 8-byte aligned\n", ring->index, base_addr);
+        return false;
+    }
+
+    ring->base_addr = base_addr;
+
+    return true;
+}
+
+uint64_t desc_ring_get_base_addr(DescRing *ring)
+{
+    return ring->base_addr;
+}
+
+bool desc_ring_set_size(DescRing *ring, uint32_t size)
+{
+    int i;
+
+    if (size < 2 || size > 0x10000 || (size & (size - 1))) {
+        DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
+                "or in range [2, 64K]\n", ring->index, size);
+        return false;
+    }
+
+    for (i = 0; i < ring->size; i++) {
+        if (ring->info[i].buf) {
+            g_free(ring->info[i].buf);
+        }
+    }
+
+    ring->size = size;
+    ring->head = ring->tail = 0;
+
+    ring->info = g_realloc(ring->info, size * sizeof(DescInfo));
+    if (!ring->info) {
+        return false;
+    }
+
+    memset(ring->info, 0, size * sizeof(DescInfo));
+
+    for (i = 0; i < size; i++) {
+        ring->info[i].ring = ring;
+    }
+
+    return true;
+}
+
+uint32_t desc_ring_get_size(DescRing *ring)
+{
+    return ring->size;
+}
+
+static DescInfo *desc_read(DescRing *ring, uint32_t index)
+{
+    PCIDevice *dev = PCI_DEVICE(ring->r);
+    DescInfo *info = &ring->info[index];
+    hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
+
+    pci_dma_read(dev, addr, &info->desc, sizeof(info->desc));
+
+    return info;
+}
+
+static void desc_write(DescRing *ring, uint32_t index)
+{
+    PCIDevice *dev = PCI_DEVICE(ring->r);
+    DescInfo *info = &ring->info[index];
+    hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
+
+    pci_dma_write(dev, addr, &info->desc, sizeof(info->desc));
+}
+
+static bool desc_ring_base_addr_check(DescRing *ring)
+{
+    if (!ring->base_addr) {
+        DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
+                ring->index);
+        return false;
+    }
+    return true;
+}
+
+static DescInfo *__desc_ring_fetch_desc(DescRing *ring)
+{
+    return desc_read(ring, ring->tail);
+}
+
+DescInfo *desc_ring_fetch_desc(DescRing *ring)
+{
+    if (desc_ring_empty(ring) || !desc_ring_base_addr_check(ring)) {
+        return NULL;
+    }
+
+    return desc_read(ring, ring->tail);
+}
+
+static bool __desc_ring_post_desc(DescRing *ring, int err)
+{
+    uint16_t comp_err = 0x8000 | (uint16_t)-err;
+    DescInfo *info = &ring->info[ring->tail];
+
+    info->desc.comp_err = cpu_to_le16(comp_err);
+    desc_write(ring, ring->tail);
+    ring->tail = (ring->tail + 1) % ring->size;
+
+    /* return true if starting credit count */
+
+    return ring->credits++ == 0;
+}
+
+bool desc_ring_post_desc(DescRing *ring, int err)
+{
+    if (desc_ring_empty(ring)) {
+        DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
+                ring->index);
+        return false;
+    }
+
+    if (!desc_ring_base_addr_check(ring)) {
+        return false;
+    }
+
+    return __desc_ring_post_desc(ring, err);
+}
+
+static bool ring_pump(DescRing *ring)
+{
+    DescInfo *info;
+    bool primed = false;
+    int err;
+
+    /* If the ring has a consumer, call consumer for each
+     * desc starting at tail and stopping when tail reaches
+     * head (the empty ring condition).
+     */
+
+    if (ring->consume) {
+        while (ring->head != ring->tail) {
+            info = __desc_ring_fetch_desc(ring);
+            err = ring->consume(ring->r, info);
+            if (__desc_ring_post_desc(ring, err)) {
+                primed = true;
+            }
+        }
+    }
+
+    return primed;
+}
+
+bool desc_ring_set_head(DescRing *ring, uint32_t new)
+{
+    uint32_t tail = ring->tail;
+    uint32_t head = ring->head;
+
+    if (!desc_ring_base_addr_check(ring)) {
+        return false;
+    }
+
+    if (new >= ring->size) {
+        DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
+                new, ring->index, ring->size);
+        return false;
+    }
+
+    if (((head < tail) && ((new >= tail) || (new < head))) ||
+        ((head > tail) && ((new >= tail) && (new < head)))) {
+        DPRINTF("ERROR: trying to wrap ring[%d] "
+                "(head %d, tail %d, new head %d)\n",
+                ring->index, head, tail, new);
+        return false;
+    }
+
+    if (new == ring->head) {
+        DPRINTF("WARNING: setting head (%d) to current head position\n", new);
+    }
+
+    ring->head = new;
+
+    return ring_pump(ring);
+}
+
+uint32_t desc_ring_get_head(DescRing *ring)
+{
+    return ring->head;
+}
+
+uint32_t desc_ring_get_tail(DescRing *ring)
+{
+    return ring->tail;
+}
+
+void desc_ring_set_ctrl(DescRing *ring, uint32_t val)
+{
+    if (val & ROCKER_DMA_DESC_CTRL_RESET) {
+        DPRINTF("ring[%d] resetting\n", ring->index);
+        desc_ring_reset(ring);
+    }
+}
+
+bool desc_ring_ret_credits(DescRing *ring, uint32_t credits)
+{
+    if (credits > ring->credits) {
+        DPRINTF("ERROR: trying to return more credits (%d) "
+                "than are outstanding (%d)\n", credits, ring->credits);
+        ring->credits = 0;
+        return false;
+    }
+
+    ring->credits -= credits;
+
+    /* return true if credits are still outstanding */
+
+    return ring->credits > 0;
+}
+
+uint32_t desc_ring_get_credits(DescRing *ring)
+{
+    return ring->credits;
+}
+
+void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume,
+                           unsigned vector)
+{
+    ring->consume = consume;
+    ring->msix_vector = vector;
+}
+
+unsigned desc_ring_get_msix_vector(DescRing *ring)
+{
+    return ring->msix_vector;
+}
+
+DescRing *desc_ring_alloc(Rocker *r, int index)
+{
+    DescRing *ring;
+
+    ring = g_malloc0(sizeof(DescRing));
+    if (!ring) {
+        return NULL;
+    }
+
+    ring->r = r;
+    ring->index = index;
+
+    return ring;
+}
+
+void desc_ring_free(DescRing *ring)
+{
+    if (ring->info) {
+        g_free(ring->info);
+    }
+    g_free(ring);
+}
+
+void desc_ring_reset(DescRing *ring)
+{
+    ring->base_addr = 0;
+    ring->size = 0;
+    ring->head = 0;
+    ring->tail = 0;
+    ring->ctrl = 0;
+    ring->credits = 0;
+}