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