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