qemu/hw/net/rocker/rocker_desc.c
<<
>>
Prefs
   1/*
   2 * QEMU rocker switch emulation - Descriptor ring support
   3 *
   4 * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include "qemu/osdep.h"
  18#include "net/net.h"
  19#include "hw/pci/pci.h"
  20
  21#include "rocker.h"
  22#include "rocker_hw.h"
  23#include "rocker_desc.h"
  24
  25struct desc_ring {
  26    hwaddr base_addr;
  27    uint32_t size;
  28    uint32_t head;
  29    uint32_t tail;
  30    uint32_t ctrl;
  31    uint32_t credits;
  32    Rocker *r;
  33    DescInfo *info;
  34    int index;
  35    desc_ring_consume *consume;
  36    unsigned msix_vector;
  37};
  38
  39struct desc_info {
  40    DescRing *ring;
  41    RockerDesc desc;
  42    char *buf;
  43    size_t buf_size;
  44};
  45
  46uint16_t desc_buf_size(DescInfo *info)
  47{
  48    return le16_to_cpu(info->desc.buf_size);
  49}
  50
  51uint16_t desc_tlv_size(DescInfo *info)
  52{
  53    return le16_to_cpu(info->desc.tlv_size);
  54}
  55
  56char *desc_get_buf(DescInfo *info, bool read_only)
  57{
  58    PCIDevice *dev = PCI_DEVICE(info->ring->r);
  59    size_t size = read_only ? le16_to_cpu(info->desc.tlv_size) :
  60                              le16_to_cpu(info->desc.buf_size);
  61
  62    if (size > info->buf_size) {
  63        info->buf = g_realloc(info->buf, size);
  64        info->buf_size = size;
  65    }
  66
  67    pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size);
  68
  69    return info->buf;
  70}
  71
  72int desc_set_buf(DescInfo *info, size_t tlv_size)
  73{
  74    PCIDevice *dev = PCI_DEVICE(info->ring->r);
  75
  76    if (tlv_size > info->buf_size) {
  77        DPRINTF("ERROR: trying to write more to desc buf than it "
  78                "can hold buf_size %zu tlv_size %zu\n",
  79                info->buf_size, tlv_size);
  80        return -ROCKER_EMSGSIZE;
  81    }
  82
  83    info->desc.tlv_size = cpu_to_le16(tlv_size);
  84    pci_dma_write(dev, le64_to_cpu(info->desc.buf_addr), info->buf, tlv_size);
  85
  86    return ROCKER_OK;
  87}
  88
  89DescRing *desc_get_ring(DescInfo *info)
  90{
  91    return info->ring;
  92}
  93
  94int desc_ring_index(DescRing *ring)
  95{
  96    return ring->index;
  97}
  98
  99static bool desc_ring_empty(DescRing *ring)
 100{
 101    return ring->head == ring->tail;
 102}
 103
 104bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr)
 105{
 106    if (base_addr & 0x7) {
 107        DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
 108                ") not 8-byte aligned\n", ring->index, base_addr);
 109        return false;
 110    }
 111
 112    ring->base_addr = base_addr;
 113
 114    return true;
 115}
 116
 117uint64_t desc_ring_get_base_addr(DescRing *ring)
 118{
 119    return ring->base_addr;
 120}
 121
 122bool desc_ring_set_size(DescRing *ring, uint32_t size)
 123{
 124    int i;
 125
 126    if (size < 2 || size > 0x10000 || (size & (size - 1))) {
 127        DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
 128                "or in range [2, 64K]\n", ring->index, size);
 129        return false;
 130    }
 131
 132    for (i = 0; i < ring->size; i++) {
 133        g_free(ring->info[i].buf);
 134    }
 135
 136    ring->size = size;
 137    ring->head = ring->tail = 0;
 138
 139    ring->info = g_renew(DescInfo, ring->info, size);
 140
 141    memset(ring->info, 0, size * sizeof(DescInfo));
 142
 143    for (i = 0; i < size; i++) {
 144        ring->info[i].ring = ring;
 145    }
 146
 147    return true;
 148}
 149
 150uint32_t desc_ring_get_size(DescRing *ring)
 151{
 152    return ring->size;
 153}
 154
 155static DescInfo *desc_read(DescRing *ring, uint32_t index)
 156{
 157    PCIDevice *dev = PCI_DEVICE(ring->r);
 158    DescInfo *info = &ring->info[index];
 159    hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
 160
 161    pci_dma_read(dev, addr, &info->desc, sizeof(info->desc));
 162
 163    return info;
 164}
 165
 166static void desc_write(DescRing *ring, uint32_t index)
 167{
 168    PCIDevice *dev = PCI_DEVICE(ring->r);
 169    DescInfo *info = &ring->info[index];
 170    hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
 171
 172    pci_dma_write(dev, addr, &info->desc, sizeof(info->desc));
 173}
 174
 175static bool desc_ring_base_addr_check(DescRing *ring)
 176{
 177    if (!ring->base_addr) {
 178        DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
 179                ring->index);
 180        return false;
 181    }
 182    return true;
 183}
 184
 185static DescInfo *__desc_ring_fetch_desc(DescRing *ring)
 186{
 187    return desc_read(ring, ring->tail);
 188}
 189
 190DescInfo *desc_ring_fetch_desc(DescRing *ring)
 191{
 192    if (desc_ring_empty(ring) || !desc_ring_base_addr_check(ring)) {
 193        return NULL;
 194    }
 195
 196    return desc_read(ring, ring->tail);
 197}
 198
 199static bool __desc_ring_post_desc(DescRing *ring, int err)
 200{
 201    uint16_t comp_err = 0x8000 | (uint16_t)-err;
 202    DescInfo *info = &ring->info[ring->tail];
 203
 204    info->desc.comp_err = cpu_to_le16(comp_err);
 205    desc_write(ring, ring->tail);
 206    ring->tail = (ring->tail + 1) % ring->size;
 207
 208    /* return true if starting credit count */
 209
 210    return ring->credits++ == 0;
 211}
 212
 213bool desc_ring_post_desc(DescRing *ring, int err)
 214{
 215    if (desc_ring_empty(ring)) {
 216        DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
 217                ring->index);
 218        return false;
 219    }
 220
 221    if (!desc_ring_base_addr_check(ring)) {
 222        return false;
 223    }
 224
 225    return __desc_ring_post_desc(ring, err);
 226}
 227
 228static bool ring_pump(DescRing *ring)
 229{
 230    DescInfo *info;
 231    bool primed = false;
 232    int err;
 233
 234    /* If the ring has a consumer, call consumer for each
 235     * desc starting at tail and stopping when tail reaches
 236     * head (the empty ring condition).
 237     */
 238
 239    if (ring->consume) {
 240        while (ring->head != ring->tail) {
 241            info = __desc_ring_fetch_desc(ring);
 242            err = ring->consume(ring->r, info);
 243            if (__desc_ring_post_desc(ring, err)) {
 244                primed = true;
 245            }
 246        }
 247    }
 248
 249    return primed;
 250}
 251
 252bool desc_ring_set_head(DescRing *ring, uint32_t new)
 253{
 254    uint32_t tail = ring->tail;
 255    uint32_t head = ring->head;
 256
 257    if (!desc_ring_base_addr_check(ring)) {
 258        return false;
 259    }
 260
 261    if (new >= ring->size) {
 262        DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
 263                new, ring->index, ring->size);
 264        return false;
 265    }
 266
 267    if (((head < tail) && ((new >= tail) || (new < head))) ||
 268        ((head > tail) && ((new >= tail) && (new < head)))) {
 269        DPRINTF("ERROR: trying to wrap ring[%d] "
 270                "(head %d, tail %d, new head %d)\n",
 271                ring->index, head, tail, new);
 272        return false;
 273    }
 274
 275    if (new == ring->head) {
 276        DPRINTF("WARNING: setting head (%d) to current head position\n", new);
 277    }
 278
 279    ring->head = new;
 280
 281    return ring_pump(ring);
 282}
 283
 284uint32_t desc_ring_get_head(DescRing *ring)
 285{
 286    return ring->head;
 287}
 288
 289uint32_t desc_ring_get_tail(DescRing *ring)
 290{
 291    return ring->tail;
 292}
 293
 294void desc_ring_set_ctrl(DescRing *ring, uint32_t val)
 295{
 296    if (val & ROCKER_DMA_DESC_CTRL_RESET) {
 297        DPRINTF("ring[%d] resetting\n", ring->index);
 298        desc_ring_reset(ring);
 299    }
 300}
 301
 302bool desc_ring_ret_credits(DescRing *ring, uint32_t credits)
 303{
 304    if (credits > ring->credits) {
 305        DPRINTF("ERROR: trying to return more credits (%d) "
 306                "than are outstanding (%d)\n", credits, ring->credits);
 307        ring->credits = 0;
 308        return false;
 309    }
 310
 311    ring->credits -= credits;
 312
 313    /* return true if credits are still outstanding */
 314
 315    return ring->credits > 0;
 316}
 317
 318uint32_t desc_ring_get_credits(DescRing *ring)
 319{
 320    return ring->credits;
 321}
 322
 323void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume,
 324                           unsigned vector)
 325{
 326    ring->consume = consume;
 327    ring->msix_vector = vector;
 328}
 329
 330unsigned desc_ring_get_msix_vector(DescRing *ring)
 331{
 332    return ring->msix_vector;
 333}
 334
 335DescRing *desc_ring_alloc(Rocker *r, int index)
 336{
 337    DescRing *ring;
 338
 339    ring = g_new0(DescRing, 1);
 340
 341    ring->r = r;
 342    ring->index = index;
 343
 344    return ring;
 345}
 346
 347void desc_ring_free(DescRing *ring)
 348{
 349    g_free(ring->info);
 350    g_free(ring);
 351}
 352
 353void desc_ring_reset(DescRing *ring)
 354{
 355    ring->base_addr = 0;
 356    ring->size = 0;
 357    ring->head = 0;
 358    ring->tail = 0;
 359    ring->ctrl = 0;
 360    ring->credits = 0;
 361}
 362