Couldn't actually wrap around the vring.
[akaros.git] / user / vmm / virtio_lguest_helpers.c
1 /* Virtio helper functions from linux/tools/lguest/lguest.c
2  *
3  * Copyright (C) 1991-2016, the Linux Kernel authors
4  * Copyright (c) 2016 Google Inc.
5  *
6  * Author:
7  *  Rusty Russell <rusty@rustcorp.com.au>
8  *  Michael Taufen <mtaufen@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of
13  * the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * The code from lguest.c has been modified for Akaros.
21  *
22  * Original linux/tools/lguest/lguest.c:
23  *   https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
24  *   most recent hash on the file as of v4.5 tag:
25  *     e523caa601f4a7c2fa1ecd040db921baf7453798
26  */
27
28
29 #include <sys/eventfd.h>
30 #include <sys/uio.h>
31 #include <ros/common.h>
32 #include <ros/arch/membar.h>
33 #include <ros/arch/mmu.h>
34 #include <vmm/virtio.h>
35 #include <vmm/virtio_ids.h>
36 #include <vmm/virtio_config.h>
37
38 // The purpose is to make sure the addresses provided by the guest are not
39 // outside the bounds of the guest's memory.
40 // Based on _check_pointer in Linux's lguest.c, which came with
41 // the following comment:
42 /*L:200
43  * Device Handling.
44  *
45  * When the Guest gives us a buffer, it sends an array of addresses and sizes.
46  * We need to make sure it's not trying to reach into the Launcher itself, so
47  * we have a convenient routine which checks it and exits with an error message
48  * if something funny is going on:
49  */
50 void *virtio_check_pointer(struct virtio_vq *vq, uint64_t addr,
51                            uint32_t size, char *file, uint32_t line)
52 {
53         // We check that the pointer + the size doesn't wrap around, and that the
54         // pointer + the size isn't past the top of the guest's address space.
55         // UVPT is the top of the guest's address space, and is included from
56         // ros/arch/mmu64.h via ros/arch/mmu.h.
57         if ((addr + size) < addr || (addr + size) > UVPT)
58                 VIRTIO_DRI_ERRX(vq->vqdev,
59                         "Driver provided an invalid address or size (addr:0x%x sz:%u).\n"
60                         "  Location: %s:%d", addr, size, file, line);
61
62         return (void *)addr;
63 }
64
65
66 // For traversing the chain of descriptors
67 // Based on next_desc Linux's lguest.c, which came with the following comment:
68 /*
69  * Each buffer in the virtqueues is actually a chain of descriptors.  This
70  * function returns the next descriptor in the chain, or vq->vring.num if we're
71  * at the end.
72  */
73 static uint32_t next_desc(struct vring_desc *desc, uint32_t i, uint32_t max,
74                 struct virtio_vq *vq) // The vq is just for the error message.
75 {
76         uint32_t next;
77
78         // No more in the chain, so return max to signal that we reached the end
79         if (!(desc[i].flags & VRING_DESC_F_NEXT))
80                 return max;
81
82         // TODO: Closely audit the decision to use ACCESS_ONCE() instead of wmb().
83         //       If we encounter strange problems in the future, it might be worth
84         //       changing it back to a wmb() to see what happens. For the record,
85         //       the virtio console doesn't seem to have any issues running without
86         //       the ACCESS_ONCE() or the wmb(). But they had a barrier here, so
87         //       I'm hesitant to do anything less than ACCESS_ONCE().
88
89         // Based on the original comment from lguest:
90         // "Make sure compiler knows to grab that: we don't want it changing!",
91         // it seems that they used a memory barrier after `next = desc[i].next`
92         // to prevent the compiler from returning a `next` that differs from
93         // the `next` that is compared to max. An ACCESS_ONCE() should suffice
94         // to prevent this (see ros/common.h and http://lwn.net/Articles/508991/).
95         next = ACCESS_ONCE(desc[i].next);
96
97         if (next >= max)
98                 VIRTIO_DRI_ERRX(vq->vqdev,
99                         "The next descriptor index in the chain provided by the driver is outside the bounds of the maximum allowed size of its queue.");
100
101         return next;
102 }
103
104 // Adds descriptor chain to the used ring of the vq
105 // Based on add_used in Linux's lguest.c, which came with the following comment:
106 /*
107  * After we've used one of their buffers, we tell the Guest about it.  Sometime
108  * later we'll want to send them an interrupt using trigger_irq(); note that
109  * wait_for_vq_desc() does that for us if it has to wait.
110  */
111 void virtio_add_used_desc(struct virtio_vq *vq, uint32_t head, uint32_t len)
112 {
113         // virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout
114         if (!vq->qready)
115                 VIRTIO_DEV_ERRX(vq->vqdev,
116                         "The device may not process queues with QueueReady set to 0x0.\n"
117                         "  See virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout");
118
119         // NOTE: len is the total length of the descriptor chain (in bytes)
120         //       that was written to.
121         //       So you should pass 0 if you didn't write anything, and pass
122         //       the number of bytes you wrote otherwise.
123         vq->vring.used->ring[vq->vring.used->idx % vq->vring.num].id = head;
124         vq->vring.used->ring[vq->vring.used->idx % vq->vring.num].len = len;
125
126         // virtio-v1.0-cs04 s2.4.8.2 The Virtqueue Used Ring
127         // TODO: Take note, Barret is thinking about renaming our wmb() to wwmb()
128         // The device MUST set len prior to updating the used idx, hence wmb()
129         wmb();
130         vq->vring.used->idx++;
131 }
132
133 // Based on wait_for_vq_desc in Linux's'lguest.c, which came with
134 // the following comment:
135 /*
136  * This looks in the virtqueue for the first available buffer, and converts
137  * it to an iovec for convenient access.  Since descriptors consist of some
138  * number of output then some number of input descriptors, it's actually two
139  * iovecs, but we pack them into one and note how many of each there were.
140  *
141  * This function waits if necessary, and returns the descriptor number found.
142  */
143 uint32_t virtio_next_avail_vq_desc(struct virtio_vq *vq, struct iovec iov[],
144                             uint32_t *olen, uint32_t *ilen)
145 {
146 // TODO: Need to make sure we don't overflow iov. Right now we're just kind of
147 //       trusting that whoever provided the iov made it at least as big as
148 //       qnum_max, but maybe we shouldn't be that trusting.
149         uint32_t i, head, max;
150         struct vring_desc *desc;
151         eventfd_t event;
152
153         while (vq->last_avail == vq->vring.avail->idx) {
154                 // We know the ring has updated when idx advances. We check == because
155                 // idx is allowed to wrap around eventually.
156
157                 // NOTE: lguest kicks the guest with an irq before they wait on the
158                 //       eventfd. Instead, I delegate that responsibility to the
159                 //       queue service functions.
160
161                 // We're about to wait on the eventfd, so we need to tell the guest
162                 // that we want a notification when it adds new buffers for
163                 // us to process.
164                 vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
165
166                 // If the guest added an available buffer while we were unsetting
167                 // the VRING_USED_F_NO_NOTIFY flag, we'll break out here and process
168                 // the new buffer.
169                 wrmb();
170                 if (vq->last_avail != vq->vring.avail->idx) {
171                         vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
172                         break;
173                 }
174
175                 if (eventfd_read(vq->eventfd, &event))
176                         VIRTIO_DEV_ERRX(vq->vqdev,
177                                 "eventfd read failed while waiting for available descriptors\n");
178
179                 // We don't need the guest to notify us about new buffers unless
180                 // we're waiting on the eventfd, because we will detect the
181                 // updated vq->vring.avail->idx.
182                 vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
183         }
184
185         // NOTE: lguest is a bit cryptic about why they check for this.
186         //       They just say, "Check it isn't doing very strange things with
187         //       descriptor numbers."
188         //       I added the reason I believe they do it in this comment and
189         //       the below error message.
190         // The guest can't have incremented idx more than vring.num times since
191         // we last incremented vq->last_avail, because it would have run out of
192         // places to put descriptors after incrementing exactly vring.num times
193         // (prior to our next vq->last_avail++)
194         if ((uint16_t)(vq->vring.avail->idx - vq->last_avail) > vq->vring.num)
195                 VIRTIO_DRI_ERRX(vq->vqdev,
196                                 "vq index increased from %u to %u, exceeded capacity %u\n",
197                                 vq->last_avail, vq->vring.avail->idx, vq->vring.num);
198
199         // lguest says here:
200         /*
201          * Make sure we read the descriptor number *after* we read the ring
202          * update; don't let the cpu or compiler change the order.
203          */
204         rmb();
205
206         // Mod because it's a *ring*. lguest said:
207         /*
208          * Grab the next descriptor number they're advertising, and increment
209          * the index we've seen.
210          */
211         head = vq->vring.avail->ring[vq->last_avail % vq->vring.num];
212         vq->last_avail++;
213
214         if (head >= vq->vring.num)
215                 VIRTIO_DRI_ERRX(vq->vqdev,
216                         "The index of the head of the descriptor chain provided by the driver is after the end of the queue.");
217
218         // Don't know how many output buffers or input buffers there are yet,
219         // this depends on the descriptor chain.
220         *olen = *ilen = 0;
221
222         // Since vring.num is the size of the queue, max is the max descriptors
223         // that should be in a descriptor chain. If we find more than that, the
224         // driver is doing something wrong.
225         max = vq->vring.num;
226         desc = vq->vring.desc;
227         i = head;
228
229         // NOTE: (from lguest)
230         /*
231          * We have to read the descriptor after we read the descriptor number,
232          * but there's a data dependency there so the CPU shouldn't reorder
233          * that: no rmb() required.
234          */
235         // Mike: The descriptor number is stored in i; what lguest means is
236         //       that data must flow from avail_ring to head to i before i
237         //       is used to index into desc.
238
239         do {
240                 // If it's an indirect descriptor, it points at a table of descriptors
241                 // provided by the guest driver. The descriptors in that table are
242                 // still chained, so we can ultimately handle them the same way as
243                 // direct descriptors.
244                 if (desc[i].flags & VRING_DESC_F_INDIRECT) {
245
246                         // virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors
247                         if (!(vq->vqdev->dri_feat & (1<<VIRTIO_RING_F_INDIRECT_DESC)))
248                                 VIRTIO_DRI_ERRX(vq->vqdev,
249                                         "The driver must not set the INDIRECT flag on a descriptor if the INDIRECT_DESC feature was not negotiated.\n"
250                                         "  See virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors");
251
252                         // NOTE: desc is only modified when we detect an indirect
253                         //       descriptor, so our implementation works whether there is an
254                         //       indirect descriptor at the very beginning OR at the very
255                         //       end of the chain (virtio-v1.0-cs04 s2.4.5.3.2 compliant)
256                         // virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors
257                         if (desc != vq->vring.desc)
258                                 VIRTIO_DRI_ERRX(vq->vqdev,
259                                         "The driver must not set the INDIRECT flag on a descriptor within an indirect descriptor.\n"
260                                         "  See virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors");
261
262                         // virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors
263                         if (desc[i].flags & VRING_DESC_F_NEXT)
264                                 VIRTIO_DRI_ERRX(vq->vqdev,
265                                         "The driver must not set both the INDIRECT and NEXT flags on a descriptor.\n"
266                                         "  See virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors");
267
268                         // nonzero mod indicates wrong table size
269                         if (desc[i].len % sizeof(struct vring_desc))
270                                 VIRTIO_DRI_ERRX(vq->vqdev,
271                                         "The size of a vring descriptor does not evenly divide the length of the indirect table provided by the driver. Bad table size.");
272
273                         // NOTE: virtio-v1.0-cs04 s2.4.5.3.2 Indirect Descriptors
274                         //       says that the device MUST ignore the write-only flag in the
275                         //       descriptor that refers to an indirect table. So we ignore.
276
277                         max = desc[i].len / sizeof(struct vring_desc);
278                         desc = virtio_check_pointer(vq, desc[i].addr, desc[i].len,
279                                                     __FILE__, __LINE__);
280
281                         // Now that desc is pointing at the table of indirect descriptors,
282                         // we set i to 0 so that we can start walking that table
283                         i = 0;
284
285                         // virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors
286                         if (max > vq->vring.num) {
287                                 VIRTIO_DRI_ERRX(vq->vqdev,
288                                         "The driver must not create a descriptor chain longer than the queue size of the device.\n"
289                                         "  See virtio-v1.0-cs04 s2.4.5.3.1 Indirect Descriptors");
290                         }
291                 }
292
293                 // Now build the scatterlist of buffers for the device to process
294                 iov[*olen + *ilen].iov_len = desc[i].len;
295                 iov[*olen + *ilen].iov_base = virtio_check_pointer(vq, desc[i].addr,
296                                                                    desc[i].len,
297                                                                    __FILE__, __LINE__);
298
299                 if (desc[i].flags & VRING_DESC_F_WRITE) {
300                         // input descriptor, increment *ilen
301                         (*ilen)++;
302                 }
303                 else {
304                         // virtio-v1.0-cs04 s2.4.4.2 Message Framing
305                         if (*ilen) {
306                                 VIRTIO_DRI_ERRX(vq->vqdev,
307                                         "Device detected an output descriptor after an input descriptor. The driver must place any device-writeable descriptor elements after all device-readable descriptor elements.\n"
308                                         "  See virtio-v1.0-cs04 s2.4.4.2 Message Framing");
309                         }
310
311                         (*olen)++;
312                 }
313
314                 // virtio-v1.0-cs04 s2.4.5.2 The Virtqueue Descriptor Table
315                 if (*olen + *ilen > max) {
316                         VIRTIO_DRI_ERRX(vq->vqdev,
317                                 "The driver must ensure that there are no loops in the descriptor chain it provides! The combined length of readable and writeable buffers was greater than the number of elements in the queue.\n"
318                                 "  See virtio-v1.0-cs04 s2.4.5.2 The Virtqueue Descriptor Table");
319                 }
320
321
322         } while ((i = next_desc(desc, i, max, vq)) != max);
323
324         return head;
325
326 }
327
328 // Based on check_virtqueue from lguest.c
329 // We call this when the driver writes 0x1 to QueueReady
330 void virtio_check_vring(struct virtio_vq *vq)
331 {
332         // First make sure that the pointers on the vring are all valid:
333         virtio_check_pointer(vq, (uint64_t)vq->vring.desc,
334                              sizeof(*vq->vring.desc) * vq->vring.num,
335                              __FILE__, __LINE__);
336         virtio_check_pointer(vq, (uint64_t)vq->vring.avail,
337                              sizeof(*vq->vring.avail) * vq->vring.num,
338                              __FILE__, __LINE__);
339         virtio_check_pointer(vq, (uint64_t)vq->vring.used,
340                              sizeof(*vq->vring.used) * vq->vring.num,
341                              __FILE__, __LINE__);
342
343
344         // virtio-v1.0-cs04 s2.4.9.1 Virtqueue Notification Suppression
345         if (vq->vring.used->flags != 0)
346                 VIRTIO_DRI_ERRX(vq->vqdev,
347                         "The driver must initialize the flags field of the used ring to 0 when allocating the used ring.\n"
348                         "  See virtio-v1.0-cs04 s2.4.9.1 Virtqueue Notification Suppression");
349 }