slab: Update the ctor/dtor interface
[akaros.git] / user / vmm / virtio_net.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  *  Kyle Milka <kmilka@google.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 <unistd.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <vmm/virtio.h>
33 #include <vmm/virtio_mmio.h>
34 #include <vmm/virtio_net.h>
35
36 #define VIRTIO_HEADER_SIZE      12
37
38 static int ctlfd;
39 static int etherfd;
40 static char data_path[128];
41 static char clone_path[64];
42
43 void net_init_fn(struct virtio_vq_dev *vqdev, int nic)
44 {
45         char type[] = "connect -1";
46         char buf[8];
47         char addr_path[32];
48         char addr_buf[3];
49         int addr_fd;
50         uint8_t addr_bytes;
51         int num_read;
52         int total_read = 0;
53
54         snprintf(addr_path, sizeof(addr_path), "/net/ether%d/addr", nic);
55         addr_fd = open(addr_path, O_RDONLY);
56         if (addr_fd < 0)
57                 VIRTIO_DEV_ERRX(vqdev, "Bad addr_fd\n");
58
59         for (int i = 0; i < ETH_ALEN; ++i) {
60                 assert(read(addr_fd, addr_buf, 2) == 2);
61                 addr_buf[2] = 0;
62                 addr_bytes = (uint8_t)(strtol(addr_buf, 0, 16));
63                 ((struct virtio_net_config *)(vqdev->cfg))->mac[i] = addr_bytes;
64                 ((struct virtio_net_config *)(vqdev->cfg_d))->mac[i] = addr_bytes;
65         }
66
67         snprintf(clone_path, sizeof(clone_path), "/net/ether%d/clone", nic);
68         ctlfd = open(clone_path, O_RDWR);
69         if (ctlfd < 0)
70                 VIRTIO_DEV_ERRX(vqdev, "%s", clone_path);
71
72         do {
73                 num_read = read(ctlfd, buf + total_read, sizeof(buf) - total_read);
74                 total_read += num_read;
75         } while (num_read > 0);
76
77         etherfd = strtol(buf, 0, 10);
78         if (etherfd < 0)
79                 VIRTIO_DEV_ERRX(vqdev, "bad etherfd %d (%s)", etherfd, buf);
80
81         snprintf(data_path, sizeof(data_path),
82                  "/net/ether%d/%d/data", nic, etherfd);
83
84         if (write(ctlfd, type, sizeof(type)) != sizeof(type))
85                 VIRTIO_DEV_ERRX(vqdev, "write to ctlfd failed");
86 }
87
88 /* net_receiveq_fn receives packets for the guest through the virtio networking
89  * device and the _vq virtio queue.
90  */
91 void net_receiveq_fn(void *_vq)
92 {
93         struct virtio_vq *vq = _vq;
94         uint32_t head;
95         uint32_t olen, ilen;
96         int num_read;
97         struct iovec *iov;
98         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
99         int fd;
100         struct virtio_net_hdr_v1 *net_header;
101
102         fd = open(data_path, O_RDWR);
103         if (fd == -1)
104                 VIRTIO_DEV_ERRX(vq->vqdev, "Could not open data file for ether1.");
105
106         if (!vq)
107                 VIRTIO_DEV_ERRX(vq->vqdev,
108                         "\n  %s:%d\n"
109                         "  Virtio device: (not sure which one): Error, device behavior.\n"
110                         "  The device must provide a valid virtio_vq as an argument to %s."
111                         , __FILE__, __LINE__, __func__);
112
113         if (vq->qready == 0x0)
114                 VIRTIO_DEV_ERRX(vq->vqdev,
115                         "The service function for queue '%s' was launched before the driver set QueueReady to 0x1.",
116                         vq->name);
117
118         iov = malloc(vq->qnum_max * sizeof(struct iovec));
119         assert(iov != NULL);
120
121         if (!dev->poke_guest) {
122                 free(iov);
123                 VIRTIO_DEV_ERRX(vq->vqdev,
124                                 "The 'poke_guest' function pointer was not set.");
125         }
126
127         for (;;) {
128                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
129                 if (olen) {
130                         free(iov);
131                         VIRTIO_DRI_ERRX(vq->vqdev,
132                                 "The driver placed a device-readable buffer in the net device's receiveq.\n"
133                                 "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
134                 }
135
136                 /* For receive the virtio header is in iov[0], so we only want
137                  * the packet to be read into iov[1] and above.
138                  */
139                 num_read = readv(fd, iov + 1, ilen - 1);
140                 if (num_read < 0) {
141                         free(iov);
142                         VIRTIO_DEV_ERRX(vq->vqdev,
143                                 "Encountered an error trying to read input from the ethernet device.");
144                 }
145
146                 /* See virtio spec virtio-v1.0-cs04 s5.1.6.3.2 Device Requirements:
147                  * Setting Up Receive Buffers
148                  *
149                  * VIRTIO_NET_F_MRG_RXBUF is not currently negotiated.
150                  * num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not
151                  * negotiated.
152                  */
153                 net_header = iov[0].iov_base;
154                 net_header->num_buffers = 1;
155                 virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE);
156
157                 virtio_mmio_set_vring_irq(dev);
158                 dev->poke_guest(dev->vec);
159         }
160 }
161
162 /* net_transmitq_fn transmits packets from the guest through the virtio
163  * networking device through the _vq virtio queue.
164  */
165 void net_transmitq_fn(void *_vq)
166 {
167         struct virtio_vq *vq = _vq;
168         uint32_t head;
169         uint32_t olen, ilen;
170         struct iovec *iov;
171         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
172         void *stripped;
173         int ret;
174         int fd = open(data_path, O_RDWR);
175         int bytestostrip;
176
177         if (fd == -1)
178                 VIRTIO_DEV_ERRX(vq->vqdev, "Could not open data file for ether1.");
179
180         iov = malloc(vq->qnum_max * sizeof(struct iovec));
181         assert(iov != NULL);
182
183         if (!dev->poke_guest) {
184                 free(iov);
185                 VIRTIO_DEV_ERRX(vq->vqdev,
186                                 "The 'poke_guest' function pointer was not set.");
187         }
188
189         for (;;) {
190                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
191
192                 if (ilen) {
193                         free(iov);
194                         VIRTIO_DRI_ERRX(vq->vqdev,
195                                         "The driver placed a device-writeable buffer in the network device's transmitq.\n"
196                                     "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
197                 }
198
199                 /* Strip off the virtio header (the first 12 bytes), as it is
200                  * not a part of the actual ethernet frame.
201                  */
202
203                 /* Find the beginning of the ethernet frame. */
204                 bytestostrip = VIRTIO_HEADER_SIZE;
205                 for (int i = 0; i < olen; i++) {
206                         /* This conditional also catches cases when iov_len is 0 */
207                         if (iov[i].iov_len <= bytestostrip) {
208                                 /* The virtio net header is bigger or equal to the current iov.
209                                  * We exhaust this iov and move on. */
210                                 bytestostrip -= iov[i].iov_len;
211                                 continue;
212                         }
213                         /* The virtio net header is smaller than the full iov.
214                          * We stop advancing here and write. */
215                         ret = write(fd, iov[i].iov_base + bytestostrip,
216                                     iov[i].iov_len - bytestostrip);
217                         assert(ret == iov[i].iov_len - bytestostrip);
218                         bytestostrip = 0;
219                 }
220                 assert(bytestostrip == 0);
221
222                 virtio_add_used_desc(vq, head, 0);
223
224                 virtio_mmio_set_vring_irq(dev);
225                 dev->poke_guest(dev->vec);
226         }
227 }