vmm: Increase the vmthread stack size
[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 <vmm/net.h>
36 #include <parlib/iovec.h>
37 #include <iplib/iplib.h>
38
39 #define VIRTIO_HEADER_SIZE      12
40
41 void virtio_net_set_mac(struct virtio_vq_dev *vqdev, uint8_t *guest_mac)
42 {
43         memcpy(((struct virtio_net_config*)(vqdev->cfg))->mac, guest_mac,
44                ETH_ADDR_LEN);
45         memcpy(((struct virtio_net_config*)(vqdev->cfg_d))->mac, guest_mac,
46                ETH_ADDR_LEN);
47 }
48
49 /* net_receiveq_fn receives packets for the guest through the virtio networking
50  * device and the _vq virtio queue.
51  */
52 void net_receiveq_fn(void *_vq)
53 {
54         struct virtio_vq *vq = _vq;
55         uint32_t head;
56         uint32_t olen, ilen;
57         int num_read;
58         struct iovec *iov;
59         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
60         struct virtio_net_hdr_v1 *net_header;
61
62         if (!vq)
63                 VIRTIO_DEV_ERRX(vq->vqdev,
64                         "\n  %s:%d\n"
65                         "  Virtio device: (not sure which one): Error, device behavior.\n"
66                         "  The device must provide a valid virtio_vq as an argument to %s."
67                         , __FILE__, __LINE__, __func__);
68
69         if (vq->qready == 0x0)
70                 VIRTIO_DEV_ERRX(vq->vqdev,
71                         "The service function for queue '%s' was launched before the driver set QueueReady to 0x1.",
72                         vq->name);
73
74         iov = malloc(vq->qnum_max * sizeof(struct iovec));
75         assert(iov != NULL);
76
77         if (!dev->poke_guest) {
78                 free(iov);
79                 VIRTIO_DEV_ERRX(vq->vqdev,
80                                 "The 'poke_guest' function pointer was not set.");
81         }
82
83         for (;;) {
84                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
85                 if (olen) {
86                         free(iov);
87                         VIRTIO_DRI_ERRX(vq->vqdev,
88                                 "The driver placed a device-readable buffer in the net device's receiveq.\n"
89                                 "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
90                 }
91
92                 /* For receive the virtio header is in iov[0], so we only want
93                  * the packet to be read into iov[1] and above.
94                  */
95                 num_read = vnet_receive_packet(iov + 1, ilen - 1);
96                 if (num_read < 0) {
97                         free(iov);
98                         VIRTIO_DEV_ERRX(vq->vqdev,
99                                 "Encountered an error trying to read input from the ethernet device.");
100                 }
101
102                 /* See virtio spec virtio-v1.0-cs04 s5.1.6.3.2 Device Requirements:
103                  * Setting Up Receive Buffers
104                  *
105                  * VIRTIO_NET_F_MRG_RXBUF is not currently negotiated.
106                  * num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not
107                  * negotiated.
108                  */
109                 net_header = iov[0].iov_base;
110                 net_header->num_buffers = 1;
111                 virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE);
112
113                 virtio_mmio_set_vring_irq(dev);
114                 dev->poke_guest(dev->vec, dev->dest);
115         }
116 }
117
118 /* net_transmitq_fn transmits packets from the guest through the virtio
119  * networking device through the _vq virtio queue.
120  */
121 void net_transmitq_fn(void *_vq)
122 {
123         struct virtio_vq *vq = _vq;
124         uint32_t head;
125         uint32_t olen, ilen;
126         struct iovec *iov;
127         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
128         void *stripped;
129
130         iov = malloc(vq->qnum_max * sizeof(struct iovec));
131         assert(iov != NULL);
132
133         if (!dev->poke_guest) {
134                 free(iov);
135                 VIRTIO_DEV_ERRX(vq->vqdev,
136                                 "The 'poke_guest' function pointer was not set.");
137         }
138
139         for (;;) {
140                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
141
142                 if (ilen) {
143                         free(iov);
144                         VIRTIO_DRI_ERRX(vq->vqdev,
145                                         "The driver placed a device-writeable buffer in the network device's transmitq.\n"
146                                     "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
147                 }
148
149                 /* Strip off the virtio header (the first 12 bytes), as it is
150                  * not a part of the actual ethernet frame. */
151                 iov_strip_bytes(iov, olen, VIRTIO_HEADER_SIZE);
152                 vnet_transmit_packet(iov, olen);
153
154                 virtio_add_used_desc(vq, head, 0);
155
156                 virtio_mmio_set_vring_irq(dev);
157                 dev->poke_guest(dev->vec, dev->dest);
158         }
159 }