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