blob: 2fcb0a58c5b03de108342d00c189caf4d331dfed [file] [log] [blame]
Thomas Huth00dde1e2017-07-12 14:49:52 +02001/*
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"
Janosch Franke70bc572020-06-24 03:52:16 -040022#include "s390-time.h"
Janosch Frank12ea90d2020-06-24 03:52:17 -040023#include "helper.h"
Thomas Huth00dde1e2017-07-12 14:49:52 +020024
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
34struct 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};
43typedef struct VirtioNetHdr VirtioNetHdr;
44
45static uint16_t rx_last_idx; /* Last index in receive queue "used" ring */
46
47int 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
72int 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
94int 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}