blob: a6922ce1b8397cbb63fcc86ec7232b7dc8790175 [file] [log] [blame]
Tuomas Tynkkynend58269f2018-10-15 02:21:01 -07001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
4 * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
5 *
6 * virtio ring implementation
7 */
8
9#include <common.h>
10#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060011#include <log.h>
Tuomas Tynkkynend58269f2018-10-15 02:21:01 -070012#include <malloc.h>
13#include <virtio_types.h>
14#include <virtio.h>
15#include <virtio_ring.h>
Simon Glassc06c1be2020-05-10 11:40:08 -060016#include <linux/bug.h>
Simon Glass9bc15642020-02-03 07:36:16 -070017#include <linux/compat.h>
Tuomas Tynkkynend58269f2018-10-15 02:21:01 -070018
19int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
20 unsigned int out_sgs, unsigned int in_sgs)
21{
22 struct vring_desc *desc;
Andrew Scullb3f36442022-05-16 10:41:29 +000023 unsigned int descs_used = out_sgs + in_sgs;
24 unsigned int i, n, avail, uninitialized_var(prev);
Tuomas Tynkkynend58269f2018-10-15 02:21:01 -070025 int head;
26
Andrew Scullb3f36442022-05-16 10:41:29 +000027 WARN_ON(descs_used == 0);
Tuomas Tynkkynend58269f2018-10-15 02:21:01 -070028
29 head = vq->free_head;
30
31 desc = vq->vring.desc;
32 i = head;
Tuomas Tynkkynend58269f2018-10-15 02:21:01 -070033
34 if (vq->num_free < descs_used) {
35 debug("Can't add buf len %i - avail = %i\n",
36 descs_used, vq->num_free);
37 /*
38 * FIXME: for historical reasons, we force a notify here if
39 * there are outgoing parts to the buffer. Presumably the
40 * host should service the ring ASAP.
41 */
42 if (out_sgs)
43 virtio_notify(vq->vdev, vq);
44 return -ENOSPC;
45 }
46
47 for (n = 0; n < out_sgs; n++) {
48 struct virtio_sg *sg = sgs[n];
49
50 desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
51 desc[i].addr = cpu_to_virtio64(vq->vdev, (u64)(size_t)sg->addr);
52 desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
53
54 prev = i;
55 i = virtio16_to_cpu(vq->vdev, desc[i].next);
56 }
57 for (; n < (out_sgs + in_sgs); n++) {
58 struct virtio_sg *sg = sgs[n];
59
60 desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT |
61 VRING_DESC_F_WRITE);
62 desc[i].addr = cpu_to_virtio64(vq->vdev,
63 (u64)(uintptr_t)sg->addr);
64 desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
65
66 prev = i;
67 i = virtio16_to_cpu(vq->vdev, desc[i].next);
68 }
69 /* Last one doesn't continue */
70 desc[prev].flags &= cpu_to_virtio16(vq->vdev, ~VRING_DESC_F_NEXT);
71
72 /* We're using some buffers from the free list. */
73 vq->num_free -= descs_used;
74
75 /* Update free pointer */
76 vq->free_head = i;
77
78 /*
79 * Put entry in available array (but don't update avail->idx
80 * until they do sync).
81 */
82 avail = vq->avail_idx_shadow & (vq->vring.num - 1);
83 vq->vring.avail->ring[avail] = cpu_to_virtio16(vq->vdev, head);
84
85 /*
86 * Descriptors and available array need to be set before we expose the
87 * new available array entries.
88 */
89 virtio_wmb();
90 vq->avail_idx_shadow++;
91 vq->vring.avail->idx = cpu_to_virtio16(vq->vdev, vq->avail_idx_shadow);
92 vq->num_added++;
93
94 /*
95 * This is very unlikely, but theoretically possible.
96 * Kick just in case.
97 */
98 if (unlikely(vq->num_added == (1 << 16) - 1))
99 virtqueue_kick(vq);
100
101 return 0;
102}
103
104static bool virtqueue_kick_prepare(struct virtqueue *vq)
105{
106 u16 new, old;
107 bool needs_kick;
108
109 /*
110 * We need to expose available array entries before checking
111 * avail event.
112 */
113 virtio_mb();
114
115 old = vq->avail_idx_shadow - vq->num_added;
116 new = vq->avail_idx_shadow;
117 vq->num_added = 0;
118
119 if (vq->event) {
120 needs_kick = vring_need_event(virtio16_to_cpu(vq->vdev,
121 vring_avail_event(&vq->vring)), new, old);
122 } else {
123 needs_kick = !(vq->vring.used->flags & cpu_to_virtio16(vq->vdev,
124 VRING_USED_F_NO_NOTIFY));
125 }
126
127 return needs_kick;
128}
129
130void virtqueue_kick(struct virtqueue *vq)
131{
132 if (virtqueue_kick_prepare(vq))
133 virtio_notify(vq->vdev, vq);
134}
135
136static void detach_buf(struct virtqueue *vq, unsigned int head)
137{
138 unsigned int i;
139 __virtio16 nextflag = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
140
141 /* Put back on free list: unmap first-level descriptors and find end */
142 i = head;
143
144 while (vq->vring.desc[i].flags & nextflag) {
145 i = virtio16_to_cpu(vq->vdev, vq->vring.desc[i].next);
146 vq->num_free++;
147 }
148
149 vq->vring.desc[i].next = cpu_to_virtio16(vq->vdev, vq->free_head);
150 vq->free_head = head;
151
152 /* Plus final descriptor */
153 vq->num_free++;
154}
155
156static inline bool more_used(const struct virtqueue *vq)
157{
158 return vq->last_used_idx != virtio16_to_cpu(vq->vdev,
159 vq->vring.used->idx);
160}
161
162void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len)
163{
164 unsigned int i;
165 u16 last_used;
166
167 if (!more_used(vq)) {
168 debug("(%s.%d): No more buffers in queue\n",
169 vq->vdev->name, vq->index);
170 return NULL;
171 }
172
173 /* Only get used array entries after they have been exposed by host */
174 virtio_rmb();
175
176 last_used = (vq->last_used_idx & (vq->vring.num - 1));
177 i = virtio32_to_cpu(vq->vdev, vq->vring.used->ring[last_used].id);
178 if (len) {
179 *len = virtio32_to_cpu(vq->vdev,
180 vq->vring.used->ring[last_used].len);
181 debug("(%s.%d): last used idx %u with len %u\n",
182 vq->vdev->name, vq->index, i, *len);
183 }
184
185 if (unlikely(i >= vq->vring.num)) {
186 printf("(%s.%d): id %u out of range\n",
187 vq->vdev->name, vq->index, i);
188 return NULL;
189 }
190
191 detach_buf(vq, i);
192 vq->last_used_idx++;
193 /*
194 * If we expect an interrupt for the next entry, tell host
195 * by writing event index and flush out the write before
196 * the read in the next get_buf call.
197 */
198 if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT))
199 virtio_store_mb(&vring_used_event(&vq->vring),
200 cpu_to_virtio16(vq->vdev, vq->last_used_idx));
201
202 return (void *)(uintptr_t)virtio64_to_cpu(vq->vdev,
203 vq->vring.desc[i].addr);
204}
205
206static struct virtqueue *__vring_new_virtqueue(unsigned int index,
207 struct vring vring,
208 struct udevice *udev)
209{
210 unsigned int i;
211 struct virtqueue *vq;
212 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
213 struct udevice *vdev = uc_priv->vdev;
214
215 vq = malloc(sizeof(*vq));
216 if (!vq)
217 return NULL;
218
219 vq->vdev = vdev;
220 vq->index = index;
221 vq->num_free = vring.num;
222 vq->vring = vring;
223 vq->last_used_idx = 0;
224 vq->avail_flags_shadow = 0;
225 vq->avail_idx_shadow = 0;
226 vq->num_added = 0;
227 list_add_tail(&vq->list, &uc_priv->vqs);
228
229 vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
230
231 /* Tell other side not to bother us */
232 vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
233 if (!vq->event)
234 vq->vring.avail->flags = cpu_to_virtio16(vdev,
235 vq->avail_flags_shadow);
236
237 /* Put everything in free lists */
238 vq->free_head = 0;
239 for (i = 0; i < vring.num - 1; i++)
240 vq->vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
241
242 return vq;
243}
244
245struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
246 unsigned int vring_align,
247 struct udevice *udev)
248{
249 struct virtqueue *vq;
250 void *queue = NULL;
251 struct vring vring;
252
253 /* We assume num is a power of 2 */
254 if (num & (num - 1)) {
255 printf("Bad virtqueue length %u\n", num);
256 return NULL;
257 }
258
259 /* TODO: allocate each queue chunk individually */
260 for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
261 queue = memalign(PAGE_SIZE, vring_size(num, vring_align));
262 if (queue)
263 break;
264 }
265
266 if (!num)
267 return NULL;
268
269 if (!queue) {
270 /* Try to get a single page. You are my only hope! */
271 queue = memalign(PAGE_SIZE, vring_size(num, vring_align));
272 }
273 if (!queue)
274 return NULL;
275
276 memset(queue, 0, vring_size(num, vring_align));
277 vring_init(&vring, num, queue, vring_align);
278
279 vq = __vring_new_virtqueue(index, vring, udev);
280 if (!vq) {
281 free(queue);
282 return NULL;
283 }
284 debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name,
285 queue, vq, num);
286
287 return vq;
288}
289
290void vring_del_virtqueue(struct virtqueue *vq)
291{
292 free(vq->vring.desc);
293 list_del(&vq->list);
294 free(vq);
295}
296
297unsigned int virtqueue_get_vring_size(struct virtqueue *vq)
298{
299 return vq->vring.num;
300}
301
302ulong virtqueue_get_desc_addr(struct virtqueue *vq)
303{
304 return (ulong)vq->vring.desc;
305}
306
307ulong virtqueue_get_avail_addr(struct virtqueue *vq)
308{
309 return (ulong)vq->vring.desc +
310 ((char *)vq->vring.avail - (char *)vq->vring.desc);
311}
312
313ulong virtqueue_get_used_addr(struct virtqueue *vq)
314{
315 return (ulong)vq->vring.desc +
316 ((char *)vq->vring.used - (char *)vq->vring.desc);
317}
318
319bool virtqueue_poll(struct virtqueue *vq, u16 last_used_idx)
320{
321 virtio_mb();
322
323 return last_used_idx != virtio16_to_cpu(vq->vdev, vq->vring.used->idx);
324}
325
326void virtqueue_dump(struct virtqueue *vq)
327{
328 unsigned int i;
329
330 printf("virtqueue %p for dev %s:\n", vq, vq->vdev->name);
331 printf("\tindex %u, phys addr %p num %u\n",
332 vq->index, vq->vring.desc, vq->vring.num);
333 printf("\tfree_head %u, num_added %u, num_free %u\n",
334 vq->free_head, vq->num_added, vq->num_free);
335 printf("\tlast_used_idx %u, avail_flags_shadow %u, avail_idx_shadow %u\n",
336 vq->last_used_idx, vq->avail_flags_shadow, vq->avail_idx_shadow);
337
338 printf("Descriptor dump:\n");
339 for (i = 0; i < vq->vring.num; i++) {
340 printf("\tdesc[%u] = { 0x%llx, len %u, flags %u, next %u }\n",
341 i, vq->vring.desc[i].addr, vq->vring.desc[i].len,
342 vq->vring.desc[i].flags, vq->vring.desc[i].next);
343 }
344
345 printf("Avail ring dump:\n");
346 printf("\tflags %u, idx %u\n",
347 vq->vring.avail->flags, vq->vring.avail->idx);
348 for (i = 0; i < vq->vring.num; i++) {
349 printf("\tavail[%u] = %u\n",
350 i, vq->vring.avail->ring[i]);
351 }
352
353 printf("Used ring dump:\n");
354 printf("\tflags %u, idx %u\n",
355 vq->vring.used->flags, vq->vring.used->idx);
356 for (i = 0; i < vq->vring.num; i++) {
357 printf("\tused[%u] = { %u, %u }\n", i,
358 vq->vring.used->ring[i].id, vq->vring.used->ring[i].len);
359 }
360}