rocker_desc.c (8276B)
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}