Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180629' into...
[qemu.git] / pc-bios / s390-ccw / virtio-net.c
blobff7f4dad2597eeae9f98d0abeae9dccbe1ce63ea
1 /*
2 * Virtio-net driver for the s390-ccw firmware
4 * Copyright 2017 Thomas Huth, Red Hat Inc.
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.
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"
23 #ifndef DEBUG_VIRTIO_NET
24 #define DEBUG_VIRTIO_NET 0
25 #endif
27 #define VIRTIO_NET_F_MAC_BIT (1 << 5)
29 #define VQ_RX 0 /* Receive queue */
30 #define VQ_TX 1 /* Transmit queue */
32 struct VirtioNetHdr {
33 uint8_t flags;
34 uint8_t gso_type;
35 uint16_t hdr_len;
36 uint16_t gso_size;
37 uint16_t csum_start;
38 uint16_t csum_offset;
39 /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
41 typedef struct VirtioNetHdr VirtioNetHdr;
43 static uint16_t rx_last_idx; /* Last index in receive queue "used" ring */
45 int virtio_net_init(void *mac_addr)
47 VDev *vdev = virtio_get_device();
48 VRing *rxvq = &vdev->vrings[VQ_RX];
49 void *buf;
50 int i;
52 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
53 virtio_setup_ccw(vdev);
55 IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
56 "virtio-net device does not support the MAC address feature");
57 memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
59 for (i = 0; i < 64; i++) {
60 buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
61 IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
62 vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
63 VRING_DESC_F_WRITE);
65 vring_notify(rxvq);
67 return 0;
70 int send(int fd, const void *buf, int len, int flags)
72 VirtioNetHdr tx_hdr;
73 VDev *vdev = virtio_get_device();
74 VRing *txvq = &vdev->vrings[VQ_TX];
76 /* Set up header - we do not use anything special, so simply clear it */
77 memset(&tx_hdr, 0, sizeof(tx_hdr));
79 vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
80 vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
81 while (!vr_poll(txvq)) {
82 yield();
84 if (drain_irqs(txvq->schid)) {
85 puts("send: drain irqs failed");
86 return -1;
89 return len;
92 int recv(int fd, void *buf, int maxlen, int flags)
94 VDev *vdev = virtio_get_device();
95 VRing *rxvq = &vdev->vrings[VQ_RX];
96 int len, id;
97 uint8_t *pkt;
99 if (rx_last_idx == rxvq->used->idx) {
100 return 0;
103 len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
104 if (len > maxlen) {
105 puts("virtio-net: Receive buffer too small");
106 len = maxlen;
108 id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
109 pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
111 #if DEBUG_VIRTIO_NET /* Dump packet */
112 int i;
113 printf("\nbuf %p: len=%i\n", (void *)rxvq->desc[id].addr, len);
114 for (i = 0; i < 64; i++) {
115 printf(" %02x", pkt[i]);
116 if ((i % 16) == 15) {
117 printf("\n");
120 printf("\n");
121 #endif
123 /* Copy data to destination buffer */
124 memcpy(buf, pkt, len);
126 /* Mark buffer as available to the host again */
127 rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
128 rxvq->avail->idx = rxvq->avail->idx + 1;
129 vring_notify(rxvq);
131 /* Move index to next entry */
132 rx_last_idx = rx_last_idx + 1;
134 return len;