master
c 148 lines 3.72 KB
Raw
1 /*
2 * Virtio-net driver for the s390-ccw firmware
3 *
4 * Copyright 2017 Thomas Huth, Red Hat Inc.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12 #include <stdint.h>
13 #include <stdbool.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/socket.h>
19 #include <ethernet.h>
20 #include "s390-ccw.h"
21 #include "virtio.h"
22 #include "virtio-ccw.h"
23 #include "s390-time.h"
24 #include "helper.h"
25
26 #ifndef DEBUG_VIRTIO_NET
27 #define DEBUG_VIRTIO_NET 0
28 #endif
29
30 #define VIRTIO_NET_F_MAC_BIT (1 << 5)
31
32 #define VQ_RX 0 /* Receive queue */
33 #define VQ_TX 1 /* Transmit queue */
34
35 struct VirtioNetHdr {
36 uint8_t flags;
37 uint8_t gso_type;
38 uint16_t hdr_len;
39 uint16_t gso_size;
40 uint16_t csum_start;
41 uint16_t csum_offset;
42 /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
43 };
44 typedef struct VirtioNetHdr VirtioNetHdr;
45
46 static uint16_t rx_last_idx; /* Last index in receive queue "used" ring */
47
48 int virtio_net_init(void *mac_addr)
49 {
50 VDev *vdev = virtio_get_device();
51 VRing *rxvq = &vdev->vrings[VQ_RX];
52 void *buf;
53 int i;
54
55 rx_last_idx = 0;
56
57 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
58 virtio_ccw_setup(vdev);
59
60 if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
61 puts("virtio-net device does not support the MAC address feature");
62 return -1;
63 }
64
65 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
66
67 for (i = 0; i < 64; i++) {
68 buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
69 IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
70 vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
71 VRING_DESC_F_WRITE);
72 }
73 vring_notify(rxvq);
74
75 return 0;
76 }
77
78 int send(int fd, const void *buf, int len, int flags)
79 {
80 VirtioNetHdr tx_hdr;
81 VDev *vdev = virtio_get_device();
82 VRing *txvq = &vdev->vrings[VQ_TX];
83
84 /* Set up header - we do not use anything special, so simply clear it */
85 memset(&tx_hdr, 0, sizeof(tx_hdr));
86
87 vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
88 vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
89 while (!vr_poll(txvq)) {
90 yield();
91 }
92 if (drain_irqs()) {
93 puts("send: drain irqs failed");
94 return -1;
95 }
96
97 return len;
98 }
99
100 int recv(int fd, void *buf, int maxlen, int flags)
101 {
102 VDev *vdev = virtio_get_device();
103 VRing *rxvq = &vdev->vrings[VQ_RX];
104 int len, id;
105 uint8_t *pkt;
106
107 if (rx_last_idx == rxvq->used->idx) {
108 return 0;
109 }
110
111 len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
112 if (len > maxlen) {
113 puts("virtio-net: Receive buffer too small");
114 len = maxlen;
115 }
116 id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
117 pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
118
119 #if DEBUG_VIRTIO_NET /* Dump packet */
120 int i;
121 printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len);
122 for (i = 0; i < 64; i++) {
123 printf(" %02x", pkt[i]);
124 if ((i % 16) == 15) {
125 printf("\n");
126 }
127 }
128 printf("\n");
129 #endif
130
131 /* Copy data to destination buffer */
132 memcpy(buf, pkt, len);
133
134 /* Mark buffer as available to the host again */
135 rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
136 rxvq->avail->idx = rxvq->avail->idx + 1;
137 vring_notify(rxvq);
138
139 /* Move index to next entry */
140 rx_last_idx = rx_last_idx + 1;
141
142 return len;
143 }
144
145 void virtio_net_deinit(void)
146 {
147 virtio_reset(virtio_get_device());
148 }