Treat tabs as having eight spaces instead of four
[akaros.git] / user / vmm / virtio_lguest_console.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 #include <stdlib.h>
29 #include <err.h>
30 #include <sys/eventfd.h>
31 #include <sys/uio.h>
32 #include <vmm/virtio.h>
33 #include <vmm/virtio_mmio.h>
34 #include <parlib/stdio.h>
35
36 void *cons_receiveq_fn(void *_vq) // host -> guest
37 {
38         struct virtio_vq *vq = _vq;
39         uint32_t head;
40         uint32_t olen, ilen;
41         uint32_t i, j;
42         int num_read;
43         struct iovec *iov;
44         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
45
46         if (!vq)
47                 errx(1,
48                         "\n  %s:%d\n"
49                         "  Virtio device: (not sure which one): Error, device behavior.\n"
50                         "  The device must provide a valid virtio_vq as an argument to %s."
51                         , __FILE__, __LINE__, __func__);
52
53         // NOTE: The virtio_next_avail_vq_desc will not write more than
54         //       vq->vring.num entries to iov, and the device implementation
55         //       (virtio_mmio.c) will not allow the driver to set vq->vring.num
56         //       to a value greater than QueueNumMax (vq->qnum_max), so you are
57         //       safe as long as your iov is at least vq->qnum_max iovecs in
58         //       size.
59         iov = malloc(vq->qnum_max * sizeof(struct iovec));
60
61         if (vq->qready == 0x0)
62                 VIRTIO_DEV_ERRX(vq->vqdev,
63                         "The service function for queue '%s' was launched before the driver set QueueReady to 0x1."
64                         , vq->name);
65
66         // NOTE: This will block in 2 places:
67         //       - reading from stdin
68         //       - reading from eventfd in virtio_next_avail_vq_desc
69         while (1) {
70                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
71
72                 if (olen) {
73                         // virtio-v1.0-cs04 s5.3.6.1 Device Operation (console
74                         // section)
75                         VIRTIO_DRI_ERRX(vq->vqdev,
76                                 "The driver placed a device-readable buffer in the console device's receiveq.\n"
77                                 "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
78                 }
79
80                 // TODO: We may want to add some sort of console abort
81                 //       (e.g. type q and enter to quit)
82                 // readv from stdin as much as we can (to end of bufs or end of
83                 // input)
84                 num_read = readv(0, iov, ilen);
85                 if (num_read < 0)
86                         VIRTIO_DEV_ERRX(vq->vqdev,
87                                 "Encountered an error trying to read input from stdin (fd 0).");
88
89                 if (num_read == 0) {
90                         VIRTIO_DEV_WARNX(vq->vqdev,
91                                 "Encountered EOF reading from stdin; exiting input thread.");
92                         break;
93                 }
94
95                 // You pass the number of bytes written to virtio_add_used_desc
96                 virtio_add_used_desc(vq, head, num_read);
97
98                 // Poke the guest however the mmio transport prefers
99                 // NOTE: assuming that the mmio transport was used for now.
100                 virtio_mmio_set_vring_irq(dev);
101                 if (dev->poke_guest)
102                         dev->poke_guest(dev->vec, dev->dest);
103                 else
104                         VIRTIO_DEV_ERRX(vq->vqdev,
105                                 "The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set.");
106         }
107         free(iov);
108         return 0;
109 }
110
111 void *cons_transmitq_fn(void *_vq) // guest -> host
112 {
113         struct virtio_vq *vq = _vq;
114         uint32_t head;
115         uint32_t olen, ilen;
116         uint32_t i, j;
117         struct iovec *iov;
118         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
119
120         if (!vq)
121                 errx(1,
122                         "\n  %s:%d\n"
123                         "  Virtio device: (not sure which one): Error, device behavior.\n"
124                         "  The device must provide a valid virtio_vq as an argument to %s."
125                         , __FILE__, __LINE__, __func__);
126
127         // NOTE: The virtio_next_avail_vq_desc will not write more than
128         //       vq->vring.num entries to iov, and the device implementation
129         //       (virtio_mmio.c) will not allow the driver to set vq->vring.num
130         //       to a value greater than QueueNumMax (vq->qnum_max), so you are
131         //       safe as long as your iov is at least vq->qnum_max iovecs in
132         //       size.
133         iov = malloc(vq->qnum_max * sizeof(struct iovec));
134
135         if (vq->qready == 0x0)
136                 VIRTIO_DEV_ERRX(vq->vqdev,
137                         "The service function for queue '%s' was launched before the driver set QueueReady to 0x1."
138                         , vq->name);
139
140         while (1) {
141                 // Get the buffers:
142                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
143
144                 if (ilen) {
145                         // virtio-v1.0-cs04 s5.3.6.1 Device Operation (console
146                         // section)
147                         VIRTIO_DRI_ERRX(vq->vqdev,
148                                 "The driver placed a device-writeable buffer in the console device's transmitq.\n"
149                                 "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
150                 }
151                 // Process the buffers:
152                 for (i = 0; i < olen; ++i) {
153                         for (j = 0; j < iov[i].iov_len; ++j)
154                                 printf("%c", ((char *)iov[i].iov_base)[j]);
155                 }
156                 fflush(stdout);
157
158                 // Add all the buffers to the used ring.
159                 // Pass 0 because we wrote nothing.
160                 virtio_add_used_desc(vq, head, 0);
161
162                 // Poke the guest however the mmio transport prefers
163                 // NOTE: assuming that the mmio transport was used for now
164                 virtio_mmio_set_vring_irq(dev);
165                 if (dev->poke_guest)
166                         dev->poke_guest(dev->vec, dev->dest);
167                 else
168                         VIRTIO_DEV_ERRX(vq->vqdev,
169                                 "The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set.");
170         }
171         free(iov);
172         return 0;
173 }