xref: /openbmc/qemu/hw/net/rocker/rocker_desc.c (revision ad1a706f)
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 
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" 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 
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 }
362