xref: /qemu/pc-bios/s390-ccw/virtio-net.c (revision cf86770c7aa31ebd6e56f4eeb25c34107f92c51e)
100dde1e6SThomas Huth /*
200dde1e6SThomas Huth  * Virtio-net driver for the s390-ccw firmware
300dde1e6SThomas Huth  *
400dde1e6SThomas Huth  * Copyright 2017 Thomas Huth, Red Hat Inc.
500dde1e6SThomas Huth  *
600dde1e6SThomas Huth  * This code is free software; you can redistribute it and/or modify it
700dde1e6SThomas Huth  * under the terms of the GNU General Public License as published by the
800dde1e6SThomas Huth  * Free Software Foundation; either version 2 of the License, or (at your
900dde1e6SThomas Huth  * option) any later version.
1000dde1e6SThomas Huth  */
1100dde1e6SThomas Huth 
1200dde1e6SThomas Huth #include <stdint.h>
1300dde1e6SThomas Huth #include <stdbool.h>
1400dde1e6SThomas Huth #include <stdio.h>
1500dde1e6SThomas Huth #include <stdlib.h>
1600dde1e6SThomas Huth #include <string.h>
1700dde1e6SThomas Huth #include <unistd.h>
1800dde1e6SThomas Huth #include <sys/socket.h>
1900dde1e6SThomas Huth #include <ethernet.h>
2000dde1e6SThomas Huth #include "s390-ccw.h"
2100dde1e6SThomas Huth #include "virtio.h"
22e70bc57bSJanosch Frank #include "s390-time.h"
2312ea90dbSJanosch Frank #include "helper.h"
2400dde1e6SThomas Huth 
2500dde1e6SThomas Huth #ifndef DEBUG_VIRTIO_NET
2600dde1e6SThomas Huth #define DEBUG_VIRTIO_NET 0
2700dde1e6SThomas Huth #endif
2800dde1e6SThomas Huth 
2900dde1e6SThomas Huth #define VIRTIO_NET_F_MAC_BIT  (1 << 5)
3000dde1e6SThomas Huth 
3100dde1e6SThomas Huth #define VQ_RX 0         /* Receive queue */
3200dde1e6SThomas Huth #define VQ_TX 1         /* Transmit queue */
3300dde1e6SThomas Huth 
3400dde1e6SThomas Huth struct VirtioNetHdr {
3500dde1e6SThomas Huth     uint8_t flags;
3600dde1e6SThomas Huth     uint8_t gso_type;
3700dde1e6SThomas Huth     uint16_t hdr_len;
3800dde1e6SThomas Huth     uint16_t gso_size;
3900dde1e6SThomas Huth     uint16_t csum_start;
4000dde1e6SThomas Huth     uint16_t csum_offset;
4100dde1e6SThomas Huth     /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
4200dde1e6SThomas Huth };
4300dde1e6SThomas Huth typedef struct VirtioNetHdr VirtioNetHdr;
4400dde1e6SThomas Huth 
4500dde1e6SThomas Huth static uint16_t rx_last_idx;  /* Last index in receive queue "used" ring */
4600dde1e6SThomas Huth 
virtio_net_init(void * mac_addr)4700dde1e6SThomas Huth int virtio_net_init(void *mac_addr)
4800dde1e6SThomas Huth {
4900dde1e6SThomas Huth     VDev *vdev = virtio_get_device();
5000dde1e6SThomas Huth     VRing *rxvq = &vdev->vrings[VQ_RX];
5100dde1e6SThomas Huth     void *buf;
5200dde1e6SThomas Huth     int i;
5300dde1e6SThomas Huth 
546ba1f714SThomas Huth     rx_last_idx = 0;
556ba1f714SThomas Huth 
5600dde1e6SThomas Huth     vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
5700dde1e6SThomas Huth     virtio_setup_ccw(vdev);
5800dde1e6SThomas Huth 
59f1a2a6e4SJared Rossi     if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
60f1a2a6e4SJared Rossi         puts("virtio-net device does not support the MAC address feature");
61f1a2a6e4SJared Rossi         return -1;
62f1a2a6e4SJared Rossi     }
63f1a2a6e4SJared Rossi 
6400dde1e6SThomas Huth     memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
6500dde1e6SThomas Huth 
6600dde1e6SThomas Huth     for (i = 0; i < 64; i++) {
6700dde1e6SThomas Huth         buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
6800dde1e6SThomas Huth         IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
6900dde1e6SThomas Huth         vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
7000dde1e6SThomas Huth                        VRING_DESC_F_WRITE);
7100dde1e6SThomas Huth     }
7200dde1e6SThomas Huth     vring_notify(rxvq);
7300dde1e6SThomas Huth 
7400dde1e6SThomas Huth     return 0;
7500dde1e6SThomas Huth }
7600dde1e6SThomas Huth 
send(int fd,const void * buf,int len,int flags)7700dde1e6SThomas Huth int send(int fd, const void *buf, int len, int flags)
7800dde1e6SThomas Huth {
7900dde1e6SThomas Huth     VirtioNetHdr tx_hdr;
8000dde1e6SThomas Huth     VDev *vdev = virtio_get_device();
8100dde1e6SThomas Huth     VRing *txvq = &vdev->vrings[VQ_TX];
8200dde1e6SThomas Huth 
8300dde1e6SThomas Huth     /* Set up header - we do not use anything special, so simply clear it */
8400dde1e6SThomas Huth     memset(&tx_hdr, 0, sizeof(tx_hdr));
8500dde1e6SThomas Huth 
8600dde1e6SThomas Huth     vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
8700dde1e6SThomas Huth     vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
8800dde1e6SThomas Huth     while (!vr_poll(txvq)) {
8900dde1e6SThomas Huth         yield();
9000dde1e6SThomas Huth     }
9100dde1e6SThomas Huth     if (drain_irqs(txvq->schid)) {
9200dde1e6SThomas Huth         puts("send: drain irqs failed");
9300dde1e6SThomas Huth         return -1;
9400dde1e6SThomas Huth     }
9500dde1e6SThomas Huth 
9600dde1e6SThomas Huth     return len;
9700dde1e6SThomas Huth }
9800dde1e6SThomas Huth 
recv(int fd,void * buf,int maxlen,int flags)9900dde1e6SThomas Huth int recv(int fd, void *buf, int maxlen, int flags)
10000dde1e6SThomas Huth {
10100dde1e6SThomas Huth     VDev *vdev = virtio_get_device();
10200dde1e6SThomas Huth     VRing *rxvq = &vdev->vrings[VQ_RX];
10300dde1e6SThomas Huth     int len, id;
10400dde1e6SThomas Huth     uint8_t *pkt;
10500dde1e6SThomas Huth 
10600dde1e6SThomas Huth     if (rx_last_idx == rxvq->used->idx) {
10700dde1e6SThomas Huth         return 0;
10800dde1e6SThomas Huth     }
10900dde1e6SThomas Huth 
11000dde1e6SThomas Huth     len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
11100dde1e6SThomas Huth     if (len > maxlen) {
11200dde1e6SThomas Huth         puts("virtio-net: Receive buffer too small");
11300dde1e6SThomas Huth         len = maxlen;
11400dde1e6SThomas Huth     }
11500dde1e6SThomas Huth     id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
11600dde1e6SThomas Huth     pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
11700dde1e6SThomas Huth 
11800dde1e6SThomas Huth #if DEBUG_VIRTIO_NET   /* Dump packet */
11900dde1e6SThomas Huth     int i;
12000dde1e6SThomas Huth     printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len);
12100dde1e6SThomas Huth     for (i = 0; i < 64; i++) {
12200dde1e6SThomas Huth         printf(" %02x", pkt[i]);
12300dde1e6SThomas Huth         if ((i % 16) == 15) {
12400dde1e6SThomas Huth             printf("\n");
12500dde1e6SThomas Huth         }
12600dde1e6SThomas Huth     }
12700dde1e6SThomas Huth     printf("\n");
12800dde1e6SThomas Huth #endif
12900dde1e6SThomas Huth 
13000dde1e6SThomas Huth     /* Copy data to destination buffer */
13100dde1e6SThomas Huth     memcpy(buf, pkt, len);
13200dde1e6SThomas Huth 
13300dde1e6SThomas Huth     /* Mark buffer as available to the host again */
13400dde1e6SThomas Huth     rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
13500dde1e6SThomas Huth     rxvq->avail->idx = rxvq->avail->idx + 1;
13600dde1e6SThomas Huth     vring_notify(rxvq);
13700dde1e6SThomas Huth 
13800dde1e6SThomas Huth     /* Move index to next entry */
13900dde1e6SThomas Huth     rx_last_idx = rx_last_idx + 1;
14000dde1e6SThomas Huth 
14100dde1e6SThomas Huth     return len;
14200dde1e6SThomas Huth }
143*68c95ed1SThomas Huth 
virtio_net_deinit(void)144*68c95ed1SThomas Huth void virtio_net_deinit(void)
145*68c95ed1SThomas Huth {
146*68c95ed1SThomas Huth     virtio_reset(virtio_get_device());
147*68c95ed1SThomas Huth }
148