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.
18 #include <sys/socket.h>
23 #ifndef DEBUG_VIRTIO_NET
24 #define DEBUG_VIRTIO_NET 0
27 #define VIRTIO_NET_F_MAC_BIT (1 << 5)
29 #define VQ_RX 0 /* Receive queue */
30 #define VQ_TX 1 /* Transmit queue */
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
];
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
),
70 int send(int fd
, const void *buf
, int len
, int flags
)
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
)) {
84 if (drain_irqs(txvq
->schid
)) {
85 puts("send: drain irqs failed");
92 int recv(int fd
, void *buf
, int maxlen
, int flags
)
94 VDev
*vdev
= virtio_get_device();
95 VRing
*rxvq
= &vdev
->vrings
[VQ_RX
];
99 if (rx_last_idx
== rxvq
->used
->idx
) {
103 len
= rxvq
->used
->ring
[rx_last_idx
% rxvq
->num
].len
- sizeof(VirtioNetHdr
);
105 puts("virtio-net: Receive buffer too small");
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 */
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) {
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;
131 /* Move index to next entry */
132 rx_last_idx
= rx_last_idx
+ 1;