1
2
3
4
5
6
7
8
9
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
23#ifndef DEBUG_VIRTIO_NET
24#define DEBUG_VIRTIO_NET 0
25#endif
26
27#define VIRTIO_NET_F_MAC_BIT (1 << 5)
28
29#define VQ_RX 0
30#define VQ_TX 1
31
32struct 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
40};
41typedef struct VirtioNetHdr VirtioNetHdr;
42
43static uint16_t rx_last_idx;
44
45int virtio_net_init(void *mac_addr)
46{
47 VDev *vdev = virtio_get_device();
48 VRing *rxvq = &vdev->vrings[VQ_RX];
49 void *buf;
50 int i;
51
52 vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
53 virtio_setup_ccw(vdev);
54
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);
58
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);
64 }
65 vring_notify(rxvq);
66
67 return 0;
68}
69
70int send(int fd, const void *buf, int len, int flags)
71{
72 VirtioNetHdr tx_hdr;
73 VDev *vdev = virtio_get_device();
74 VRing *txvq = &vdev->vrings[VQ_TX];
75
76
77 memset(&tx_hdr, 0, sizeof(tx_hdr));
78
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();
83 }
84 if (drain_irqs(txvq->schid)) {
85 puts("send: drain irqs failed");
86 return -1;
87 }
88
89 return len;
90}
91
92int recv(int fd, void *buf, int maxlen, int flags)
93{
94 VDev *vdev = virtio_get_device();
95 VRing *rxvq = &vdev->vrings[VQ_RX];
96 int len, id;
97 uint8_t *pkt;
98
99 if (rx_last_idx == rxvq->used->idx) {
100 return 0;
101 }
102
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;
107 }
108 id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
109 pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
110
111#if DEBUG_VIRTIO_NET
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");
118 }
119 }
120 printf("\n");
121#endif
122
123
124 memcpy(buf, pkt, len);
125
126
127 rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
128 rxvq->avail->idx = rxvq->avail->idx + 1;
129 vring_notify(rxvq);
130
131
132 rx_last_idx = rx_last_idx + 1;
133
134 return len;
135}
136