xref: /openbmc/qemu/pc-bios/s390-ccw/virtio-net.c (revision 79e49005)
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 "s390-time.h"
23 #include "helper.h"
24 
25 #ifndef DEBUG_VIRTIO_NET
26 #define DEBUG_VIRTIO_NET 0
27 #endif
28 
29 #define VIRTIO_NET_F_MAC_BIT  (1 << 5)
30 
31 #define VQ_RX 0         /* Receive queue */
32 #define VQ_TX 1         /* Transmit queue */
33 
34 struct VirtioNetHdr {
35     uint8_t flags;
36     uint8_t gso_type;
37     uint16_t hdr_len;
38     uint16_t gso_size;
39     uint16_t csum_start;
40     uint16_t csum_offset;
41     /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
42 };
43 typedef struct VirtioNetHdr VirtioNetHdr;
44 
45 static uint16_t rx_last_idx;  /* Last index in receive queue "used" ring */
46 
47 int virtio_net_init(void *mac_addr)
48 {
49     VDev *vdev = virtio_get_device();
50     VRing *rxvq = &vdev->vrings[VQ_RX];
51     void *buf;
52     int i;
53 
54     vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
55     virtio_setup_ccw(vdev);
56 
57     if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
58         puts("virtio-net device does not support the MAC address feature");
59         return -1;
60     }
61 
62     memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
63 
64     for (i = 0; i < 64; i++) {
65         buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
66         IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
67         vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
68                        VRING_DESC_F_WRITE);
69     }
70     vring_notify(rxvq);
71 
72     return 0;
73 }
74 
75 int send(int fd, const void *buf, int len, int flags)
76 {
77     VirtioNetHdr tx_hdr;
78     VDev *vdev = virtio_get_device();
79     VRing *txvq = &vdev->vrings[VQ_TX];
80 
81     /* Set up header - we do not use anything special, so simply clear it */
82     memset(&tx_hdr, 0, sizeof(tx_hdr));
83 
84     vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
85     vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
86     while (!vr_poll(txvq)) {
87         yield();
88     }
89     if (drain_irqs(txvq->schid)) {
90         puts("send: drain irqs failed");
91         return -1;
92     }
93 
94     return len;
95 }
96 
97 int recv(int fd, void *buf, int maxlen, int flags)
98 {
99     VDev *vdev = virtio_get_device();
100     VRing *rxvq = &vdev->vrings[VQ_RX];
101     int len, id;
102     uint8_t *pkt;
103 
104     if (rx_last_idx == rxvq->used->idx) {
105         return 0;
106     }
107 
108     len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
109     if (len > maxlen) {
110         puts("virtio-net: Receive buffer too small");
111         len = maxlen;
112     }
113     id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
114     pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
115 
116 #if DEBUG_VIRTIO_NET   /* Dump packet */
117     int i;
118     printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len);
119     for (i = 0; i < 64; i++) {
120         printf(" %02x", pkt[i]);
121         if ((i % 16) == 15) {
122             printf("\n");
123         }
124     }
125     printf("\n");
126 #endif
127 
128     /* Copy data to destination buffer */
129     memcpy(buf, pkt, len);
130 
131     /* Mark buffer as available to the host again */
132     rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
133     rxvq->avail->idx = rxvq->avail->idx + 1;
134     vring_notify(rxvq);
135 
136     /* Move index to next entry */
137     rx_last_idx = rx_last_idx + 1;
138 
139     return len;
140 }
141