1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
|
/*
* 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 "qemu/osdep.h"
#include "net/net.h"
#include "hw/pci/pci_device.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;
}
pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size);
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" HWADDR_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++) {
g_free(ring->info[i].buf);
}
ring->size = size;
ring->head = ring->tail = 0;
ring->info = g_renew(DescInfo, ring->info, size);
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_new0(DescRing, 1);
ring->r = r;
ring->index = index;
return ring;
}
void desc_ring_free(DescRing *ring)
{
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;
}
|