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 rx_last_idx = 0; 55 56 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT; 57 virtio_setup_ccw(vdev); 58 59 if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) { 60 puts("virtio-net device does not support the MAC address feature"); 61 return -1; 62 } 63 64 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN); 65 66 for (i = 0; i < 64; i++) { 67 buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr)); 68 IPL_assert(buf != NULL, "Can not allocate memory for receive buffers"); 69 vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr), 70 VRING_DESC_F_WRITE); 71 } 72 vring_notify(rxvq); 73 74 return 0; 75 } 76 77 int send(int fd, const void *buf, int len, int flags) 78 { 79 VirtioNetHdr tx_hdr; 80 VDev *vdev = virtio_get_device(); 81 VRing *txvq = &vdev->vrings[VQ_TX]; 82 83 /* Set up header - we do not use anything special, so simply clear it */ 84 memset(&tx_hdr, 0, sizeof(tx_hdr)); 85 86 vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT); 87 vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN); 88 while (!vr_poll(txvq)) { 89 yield(); 90 } 91 if (drain_irqs(txvq->schid)) { 92 puts("send: drain irqs failed"); 93 return -1; 94 } 95 96 return len; 97 } 98 99 int recv(int fd, void *buf, int maxlen, int flags) 100 { 101 VDev *vdev = virtio_get_device(); 102 VRing *rxvq = &vdev->vrings[VQ_RX]; 103 int len, id; 104 uint8_t *pkt; 105 106 if (rx_last_idx == rxvq->used->idx) { 107 return 0; 108 } 109 110 len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr); 111 if (len > maxlen) { 112 puts("virtio-net: Receive buffer too small"); 113 len = maxlen; 114 } 115 id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num; 116 pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr)); 117 118 #if DEBUG_VIRTIO_NET /* Dump packet */ 119 int i; 120 printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len); 121 for (i = 0; i < 64; i++) { 122 printf(" %02x", pkt[i]); 123 if ((i % 16) == 15) { 124 printf("\n"); 125 } 126 } 127 printf("\n"); 128 #endif 129 130 /* Copy data to destination buffer */ 131 memcpy(buf, pkt, len); 132 133 /* Mark buffer as available to the host again */ 134 rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id; 135 rxvq->avail->idx = rxvq->avail->idx + 1; 136 vring_notify(rxvq); 137 138 /* Move index to next entry */ 139 rx_last_idx = rx_last_idx + 1; 140 141 return len; 142 } 143