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