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