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>
22 #include "s390-time.h"
25 #ifndef DEBUG_VIRTIO_NET
26 #define DEBUG_VIRTIO_NET 0
29 #define VIRTIO_NET_F_MAC_BIT (1 << 5)
31 #define VQ_RX 0 /* Receive queue */
32 #define VQ_TX 1 /* Transmit queue */
41 /*uint16_t num_buffers;*/ /* Only with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
43 typedef struct VirtioNetHdr VirtioNetHdr
;
45 static uint16_t rx_last_idx
; /* Last index in receive queue "used" ring */
47 int virtio_net_init(void *mac_addr
)
49 VDev
*vdev
= virtio_get_device();
50 VRing
*rxvq
= &vdev
->vrings
[VQ_RX
];
54 vdev
->guest_features
[0] = VIRTIO_NET_F_MAC_BIT
;
55 virtio_setup_ccw(vdev
);
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
);
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
),
72 int send(int fd
, const void *buf
, int len
, int flags
)
75 VDev
*vdev
= virtio_get_device();
76 VRing
*txvq
= &vdev
->vrings
[VQ_TX
];
78 /* Set up header - we do not use anything special, so simply clear it */
79 memset(&tx_hdr
, 0, sizeof(tx_hdr
));
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
)) {
86 if (drain_irqs(txvq
->schid
)) {
87 puts("send: drain irqs failed");
94 int recv(int fd
, void *buf
, int maxlen
, int flags
)
96 VDev
*vdev
= virtio_get_device();
97 VRing
*rxvq
= &vdev
->vrings
[VQ_RX
];
101 if (rx_last_idx
== rxvq
->used
->idx
) {
105 len
= rxvq
->used
->ring
[rx_last_idx
% rxvq
->num
].len
- sizeof(VirtioNetHdr
);
107 puts("virtio-net: Receive buffer too small");
110 id
= rxvq
->used
->ring
[rx_last_idx
% rxvq
->num
].id
% rxvq
->num
;
111 pkt
= (uint8_t *)(rxvq
->desc
[id
].addr
+ sizeof(VirtioNetHdr
));
113 #if DEBUG_VIRTIO_NET /* Dump packet */
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) {
125 /* Copy data to destination buffer */
126 memcpy(buf
, pkt
, len
);
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;
133 /* Move index to next entry */
134 rx_last_idx
= rx_last_idx
+ 1;