96ef5e644765755d935fa29372f139d8ad596a31
[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                 /* The virtio_net header comes first.  We'll assume they didn't
93                  * break the header across IOVs, but earlier versions of Linux
94                  * did break out the payload into the second IOV. */
95                 net_header = iov[0].iov_base;
96                 assert(iov[0].iov_len >= VIRTIO_HEADER_SIZE);
97                 iov_strip_bytes(iov, ilen, VIRTIO_HEADER_SIZE);
98
99                 num_read = vnet_receive_packet(iov, ilen);
100                 if (num_read < 0) {
101                         free(iov);
102                         VIRTIO_DEV_ERRX(vq->vqdev,
103                                 "Encountered an error trying to read input from the ethernet device.");
104                 }
105
106                 /* See virtio spec virtio-v1.0-cs04 s5.1.6.3.2 Device
107                  * Requirements: Setting Up Receive Buffers
108                  *
109                  * VIRTIO_NET_F_MRG_RXBUF is not currently negotiated.
110                  * num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not
111                  * negotiated.
112                  */
113                 net_header->num_buffers = 1;
114                 net_header->flags = 0;
115                 net_header->gso_type = VIRTIO_NET_HDR_GSO_NONE;
116                 virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE);
117
118                 virtio_mmio_set_vring_irq(dev);
119                 dev->poke_guest(dev->vec, dev->dest);
120         }
121         return 0;
122 }
123
124 /* net_transmitq_fn transmits packets from the guest through the virtio
125  * networking device through the _vq virtio queue.
126  */
127 void *net_transmitq_fn(void *_vq)
128 {
129         struct virtio_vq *vq = _vq;
130         uint32_t head;
131         uint32_t olen, ilen;
132         struct iovec *iov;
133         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
134         void *stripped;
135
136         iov = malloc(vq->qnum_max * sizeof(struct iovec));
137         assert(iov != NULL);
138
139         if (!dev->poke_guest) {
140                 free(iov);
141                 VIRTIO_DEV_ERRX(vq->vqdev,
142                         "The 'poke_guest' function pointer was not set.");
143         }
144
145         for (;;) {
146                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
147
148                 if (ilen) {
149                         free(iov);
150                         VIRTIO_DRI_ERRX(vq->vqdev,
151                                         "The driver placed a device-writeable buffer in the network device's transmitq.\n"
152                                     "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
153                 }
154
155                 /* Strip off the virtio header (the first 12 bytes), as it is
156                  * not a part of the actual ethernet frame. */
157                 iov_strip_bytes(iov, olen, VIRTIO_HEADER_SIZE);
158                 vnet_transmit_packet(iov, olen);
159
160                 virtio_add_used_desc(vq, head, 0);
161
162                 virtio_mmio_set_vring_irq(dev);
163                 dev->poke_guest(dev->vec, dev->dest);
164         }
165         return 0;
166 }