| 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_device.h" |
| 20 | |
| 21 | #include "rocker.h" |
| 22 | #include "rocker_hw.h" |
| 23 | #include "rocker_desc.h" |
| 24 | |
| 25 | struct 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 | |
| 39 | struct desc_info { |
| 40 | DescRing *ring; |
| 41 | RockerDesc desc; |
| 42 | char *buf; |
| 43 | size_t buf_size; |
| 44 | }; |
| 45 | |
| 46 | uint16_t desc_buf_size(DescInfo *info) |
| 47 | { |
| 48 | return le16_to_cpu(info->desc.buf_size); |
| 49 | } |
| 50 | |
| 51 | uint16_t desc_tlv_size(DescInfo *info) |
| 52 | { |
| 53 | return le16_to_cpu(info->desc.tlv_size); |
| 54 | } |
| 55 | |
| 56 | char *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 | |
| 72 | int 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 | |
| 89 | DescRing *desc_get_ring(DescInfo *info) |
| 90 | { |
| 91 | return info->ring; |
| 92 | } |
| 93 | |
| 94 | int desc_ring_index(DescRing *ring) |
| 95 | { |
| 96 | return ring->index; |
| 97 | } |
| 98 | |
| 99 | static bool desc_ring_empty(DescRing *ring) |
| 100 | { |
| 101 | return ring->head == ring->tail; |
| 102 | } |
| 103 | |
| 104 | bool 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" HWADDR_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 | |
| 117 | uint64_t desc_ring_get_base_addr(DescRing *ring) |
| 118 | { |
| 119 | return ring->base_addr; |
| 120 | } |
| 121 | |
| 122 | bool 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 | |
| 150 | uint32_t desc_ring_get_size(DescRing *ring) |
| 151 | { |
| 152 | return ring->size; |
| 153 | } |
| 154 | |
| 155 | static 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 | |
| 166 | static 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 | |
| 175 | static 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 | |
| 185 | static DescInfo *__desc_ring_fetch_desc(DescRing *ring) |
| 186 | { |
| 187 | return desc_read(ring, ring->tail); |
| 188 | } |
| 189 | |
| 190 | DescInfo *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 | |
| 199 | static 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 | |
| 213 | bool 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 | |
| 228 | static 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 | |
| 252 | bool 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 | |
| 284 | uint32_t desc_ring_get_head(DescRing *ring) |
| 285 | { |
| 286 | return ring->head; |
| 287 | } |
| 288 | |
| 289 | uint32_t desc_ring_get_tail(DescRing *ring) |
| 290 | { |
| 291 | return ring->tail; |
| 292 | } |
| 293 | |
| 294 | void 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 | |
| 302 | bool 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 | |
| 318 | uint32_t desc_ring_get_credits(DescRing *ring) |
| 319 | { |
| 320 | return ring->credits; |
| 321 | } |
| 322 | |
| 323 | void 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 | |
| 330 | unsigned desc_ring_get_msix_vector(DescRing *ring) |
| 331 | { |
| 332 | return ring->msix_vector; |
| 333 | } |
| 334 | |
| 335 | DescRing *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 | |
| 347 | void desc_ring_free(DescRing *ring) |
| 348 | { |
| 349 | g_free(ring->info); |
| 350 | g_free(ring); |
| 351 | } |
| 352 | |
| 353 | void 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 | } |