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" 23*12ea90dbSJanosch 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 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 5400dde1e6SThomas Huth vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT; 5500dde1e6SThomas Huth virtio_setup_ccw(vdev); 5600dde1e6SThomas Huth 5700dde1e6SThomas Huth IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT, 5800dde1e6SThomas Huth "virtio-net device does not support the MAC address feature"); 5900dde1e6SThomas Huth memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN); 6000dde1e6SThomas Huth 6100dde1e6SThomas Huth for (i = 0; i < 64; i++) { 6200dde1e6SThomas Huth buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr)); 6300dde1e6SThomas Huth IPL_assert(buf != NULL, "Can not allocate memory for receive buffers"); 6400dde1e6SThomas Huth vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr), 6500dde1e6SThomas Huth VRING_DESC_F_WRITE); 6600dde1e6SThomas Huth } 6700dde1e6SThomas Huth vring_notify(rxvq); 6800dde1e6SThomas Huth 6900dde1e6SThomas Huth return 0; 7000dde1e6SThomas Huth } 7100dde1e6SThomas Huth 7200dde1e6SThomas Huth int send(int fd, const void *buf, int len, int flags) 7300dde1e6SThomas Huth { 7400dde1e6SThomas Huth VirtioNetHdr tx_hdr; 7500dde1e6SThomas Huth VDev *vdev = virtio_get_device(); 7600dde1e6SThomas Huth VRing *txvq = &vdev->vrings[VQ_TX]; 7700dde1e6SThomas Huth 7800dde1e6SThomas Huth /* Set up header - we do not use anything special, so simply clear it */ 7900dde1e6SThomas Huth memset(&tx_hdr, 0, sizeof(tx_hdr)); 8000dde1e6SThomas Huth 8100dde1e6SThomas Huth vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT); 8200dde1e6SThomas Huth vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN); 8300dde1e6SThomas Huth while (!vr_poll(txvq)) { 8400dde1e6SThomas Huth yield(); 8500dde1e6SThomas Huth } 8600dde1e6SThomas Huth if (drain_irqs(txvq->schid)) { 8700dde1e6SThomas Huth puts("send: drain irqs failed"); 8800dde1e6SThomas Huth return -1; 8900dde1e6SThomas Huth } 9000dde1e6SThomas Huth 9100dde1e6SThomas Huth return len; 9200dde1e6SThomas Huth } 9300dde1e6SThomas Huth 9400dde1e6SThomas Huth int recv(int fd, void *buf, int maxlen, int flags) 9500dde1e6SThomas Huth { 9600dde1e6SThomas Huth VDev *vdev = virtio_get_device(); 9700dde1e6SThomas Huth VRing *rxvq = &vdev->vrings[VQ_RX]; 9800dde1e6SThomas Huth int len, id; 9900dde1e6SThomas Huth uint8_t *pkt; 10000dde1e6SThomas Huth 10100dde1e6SThomas Huth if (rx_last_idx == rxvq->used->idx) { 10200dde1e6SThomas Huth return 0; 10300dde1e6SThomas Huth } 10400dde1e6SThomas Huth 10500dde1e6SThomas Huth len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr); 10600dde1e6SThomas Huth if (len > maxlen) { 10700dde1e6SThomas Huth puts("virtio-net: Receive buffer too small"); 10800dde1e6SThomas Huth len = maxlen; 10900dde1e6SThomas Huth } 11000dde1e6SThomas Huth id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num; 11100dde1e6SThomas Huth pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr)); 11200dde1e6SThomas Huth 11300dde1e6SThomas Huth #if DEBUG_VIRTIO_NET /* Dump packet */ 11400dde1e6SThomas Huth int i; 11500dde1e6SThomas Huth printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len); 11600dde1e6SThomas Huth for (i = 0; i < 64; i++) { 11700dde1e6SThomas Huth printf(" %02x", pkt[i]); 11800dde1e6SThomas Huth if ((i % 16) == 15) { 11900dde1e6SThomas Huth printf("\n"); 12000dde1e6SThomas Huth } 12100dde1e6SThomas Huth } 12200dde1e6SThomas Huth printf("\n"); 12300dde1e6SThomas Huth #endif 12400dde1e6SThomas Huth 12500dde1e6SThomas Huth /* Copy data to destination buffer */ 12600dde1e6SThomas Huth memcpy(buf, pkt, len); 12700dde1e6SThomas Huth 12800dde1e6SThomas Huth /* Mark buffer as available to the host again */ 12900dde1e6SThomas Huth rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id; 13000dde1e6SThomas Huth rxvq->avail->idx = rxvq->avail->idx + 1; 13100dde1e6SThomas Huth vring_notify(rxvq); 13200dde1e6SThomas Huth 13300dde1e6SThomas Huth /* Move index to next entry */ 13400dde1e6SThomas Huth rx_last_idx = rx_last_idx + 1; 13500dde1e6SThomas Huth 13600dde1e6SThomas Huth return len; 13700dde1e6SThomas Huth } 138