|
@@ -1,6 +1,9 @@
|
1
|
|
-/* virtio-net.c - etherboot driver for virtio network interface
|
|
1
|
+/*
|
|
2
|
+ * (c) Copyright 2010 Stefan Hajnoczi <stefanha@gmail.com>
|
|
3
|
+ *
|
|
4
|
+ * based on the Etherboot virtio-net driver
|
2
|
5
|
*
|
3
|
|
- * (c) Copyright 2008 Bull S.A.S.
|
|
6
|
+ * (c) Copyright 2008 Bull S.A.S.
|
4
|
7
|
*
|
5
|
8
|
* Author: Laurent Vivier <Laurent.Vivier@bull.net>
|
6
|
9
|
*
|
|
@@ -15,293 +18,397 @@
|
15
|
18
|
*
|
16
|
19
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
17
|
20
|
* See the COPYING file in the top-level directory.
|
18
|
|
- *
|
19
|
|
- *
|
20
|
21
|
*/
|
21
|
22
|
|
22
|
|
-#include "etherboot.h"
|
23
|
|
-#include "nic.h"
|
24
|
|
-#include "ipxe/virtio-ring.h"
|
25
|
|
-#include "ipxe/virtio-pci.h"
|
|
23
|
+FILE_LICENCE ( GPL2_OR_LATER );
|
|
24
|
+
|
|
25
|
+#include <errno.h>
|
|
26
|
+#include <stdlib.h>
|
|
27
|
+#include <ipxe/list.h>
|
|
28
|
+#include <ipxe/iobuf.h>
|
|
29
|
+#include <ipxe/netdevice.h>
|
|
30
|
+#include <ipxe/pci.h>
|
|
31
|
+#include <ipxe/if_ether.h>
|
|
32
|
+#include <ipxe/ethernet.h>
|
|
33
|
+#include <ipxe/virtio-ring.h>
|
|
34
|
+#include <ipxe/virtio-pci.h>
|
26
|
35
|
#include "virtio-net.h"
|
27
|
36
|
|
28
|
|
-#define BUG() do { \
|
29
|
|
- printf("BUG: failure at %s:%d/%s()!\n", \
|
30
|
|
- __FILE__, __LINE__, __FUNCTION__); \
|
31
|
|
- while(1); \
|
32
|
|
-} while (0)
|
33
|
|
-#define BUG_ON(condition) do { if (condition) BUG(); } while (0)
|
|
37
|
+/*
|
|
38
|
+ * Virtio network device driver
|
|
39
|
+ *
|
|
40
|
+ * Specification:
|
|
41
|
+ * http://ozlabs.org/~rusty/virtio-spec/
|
|
42
|
+ *
|
|
43
|
+ * The virtio network device is supported by Linux virtualization software
|
|
44
|
+ * including QEMU/KVM and lguest. This driver supports the virtio over PCI
|
|
45
|
+ * transport; virtual machines have one virtio-net PCI adapter per NIC.
|
|
46
|
+ *
|
|
47
|
+ * Virtio-net is different from hardware NICs because virtio devices
|
|
48
|
+ * communicate with the hypervisor via virtqueues, not traditional descriptor
|
|
49
|
+ * rings. Virtqueues are unordered queues, they support add_buf() and
|
|
50
|
+ * get_buf() operations. To transmit a packet, the driver has to add the
|
|
51
|
+ * packet buffer onto the virtqueue. To receive a packet, the driver must
|
|
52
|
+ * first add an empty buffer to the virtqueue and then get the filled packet
|
|
53
|
+ * buffer on completion.
|
|
54
|
+ *
|
|
55
|
+ * Virtqueues are an abstraction that is commonly implemented using the vring
|
|
56
|
+ * descriptor ring layout. The vring is the actual shared memory structure
|
|
57
|
+ * that allows the virtual machine to communicate buffers with the hypervisor.
|
|
58
|
+ * Because the vring layout is optimized for flexibility and performance rather
|
|
59
|
+ * than space, it is heavy-weight and allocated like traditional descriptor
|
|
60
|
+ * rings in the open() function of the driver and not in probe().
|
|
61
|
+ *
|
|
62
|
+ * There is no true interrupt enable/disable. Virtqueues have callback
|
|
63
|
+ * enable/disable flags but these are only hints. The hypervisor may still
|
|
64
|
+ * raise an interrupt. Nevertheless, this driver disables callbacks in the
|
|
65
|
+ * hopes of avoiding interrupts.
|
|
66
|
+ */
|
34
|
67
|
|
35
|
|
-/* Ethernet header */
|
|
68
|
+/* Driver types are declared here so virtio-net.h can be easily synced with its
|
|
69
|
+ * Linux source.
|
|
70
|
+ */
|
36
|
71
|
|
37
|
|
-struct eth_hdr {
|
38
|
|
- unsigned char dst_addr[ETH_ALEN];
|
39
|
|
- unsigned char src_addr[ETH_ALEN];
|
40
|
|
- unsigned short type;
|
|
72
|
+/* Virtqueue indicies */
|
|
73
|
+enum {
|
|
74
|
+ RX_INDEX = 0,
|
|
75
|
+ TX_INDEX,
|
|
76
|
+ QUEUE_NB
|
41
|
77
|
};
|
42
|
78
|
|
43
|
|
-struct eth_frame {
|
44
|
|
- struct eth_hdr hdr;
|
45
|
|
- unsigned char data[ETH_FRAME_LEN];
|
46
|
|
-};
|
|
79
|
+enum {
|
|
80
|
+ /** Max number of pending rx packets */
|
|
81
|
+ NUM_RX_BUF = 8,
|
47
|
82
|
|
48
|
|
-/* TX: virtio header and eth buffer */
|
|
83
|
+ /** Max Ethernet frame length, including FCS and VLAN tag */
|
|
84
|
+ RX_BUF_SIZE = 1522,
|
|
85
|
+};
|
49
|
86
|
|
50
|
|
-static struct virtio_net_hdr tx_virtio_hdr;
|
51
|
|
-static struct eth_frame tx_eth_frame;
|
|
87
|
+struct virtnet_nic {
|
|
88
|
+ /** Base pio register address */
|
|
89
|
+ unsigned long ioaddr;
|
52
|
90
|
|
53
|
|
-/* RX: virtio headers and buffers */
|
|
91
|
+ /** RX/TX virtqueues */
|
|
92
|
+ struct vring_virtqueue *virtqueue;
|
54
|
93
|
|
55
|
|
-#define RX_BUF_NB 6
|
56
|
|
-static struct virtio_net_hdr rx_hdr[RX_BUF_NB];
|
57
|
|
-static unsigned char rx_buffer[RX_BUF_NB][ETH_FRAME_LEN];
|
|
94
|
+ /** RX packets handed to the NIC waiting to be filled in */
|
|
95
|
+ struct list_head rx_iobufs;
|
58
|
96
|
|
59
|
|
-/* virtio queues and vrings */
|
|
97
|
+ /** Pending rx packet count */
|
|
98
|
+ unsigned int rx_num_iobufs;
|
60
|
99
|
|
61
|
|
-enum {
|
62
|
|
- RX_INDEX = 0,
|
63
|
|
- TX_INDEX,
|
64
|
|
- QUEUE_NB
|
|
100
|
+ /** Virtio net packet header, we only need one */
|
|
101
|
+ struct virtio_net_hdr empty_header;
|
65
|
102
|
};
|
66
|
103
|
|
67
|
|
-static struct vring_virtqueue virtqueue[QUEUE_NB];
|
68
|
|
-
|
69
|
|
-/*
|
70
|
|
- * virtnet_disable
|
|
104
|
+/** Add an iobuf to a virtqueue
|
71
|
105
|
*
|
72
|
|
- * Turn off ethernet interface
|
|
106
|
+ * @v netdev Network device
|
|
107
|
+ * @v vq_idx Virtqueue index (RX_INDEX or TX_INDEX)
|
|
108
|
+ * @v iobuf I/O buffer
|
73
|
109
|
*
|
|
110
|
+ * The virtqueue is kicked after the iobuf has been added.
|
74
|
111
|
*/
|
75
|
|
-
|
76
|
|
-static void virtnet_disable(struct nic *nic)
|
77
|
|
-{
|
78
|
|
- int i;
|
79
|
|
-
|
80
|
|
- for (i = 0; i < QUEUE_NB; i++) {
|
81
|
|
- vring_disable_cb(&virtqueue[i]);
|
82
|
|
- vp_del_vq(nic->ioaddr, i);
|
83
|
|
- }
|
84
|
|
- vp_reset(nic->ioaddr);
|
|
112
|
+static void virtnet_enqueue_iob ( struct net_device *netdev,
|
|
113
|
+ int vq_idx, struct io_buffer *iobuf ) {
|
|
114
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
115
|
+ struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
|
|
116
|
+ unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
|
|
117
|
+ unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
|
|
118
|
+ struct vring_list list[] = {
|
|
119
|
+ {
|
|
120
|
+ /* Share a single zeroed virtio net header between all
|
|
121
|
+ * rx and tx packets. This works because this driver
|
|
122
|
+ * does not use any advanced features so none of the
|
|
123
|
+ * header fields get used.
|
|
124
|
+ */
|
|
125
|
+ .addr = ( char* ) &virtnet->empty_header,
|
|
126
|
+ .length = sizeof ( virtnet->empty_header ),
|
|
127
|
+ },
|
|
128
|
+ {
|
|
129
|
+ .addr = ( char* ) iobuf->data,
|
|
130
|
+ .length = iob_len ( iobuf ),
|
|
131
|
+ },
|
|
132
|
+ };
|
|
133
|
+
|
|
134
|
+ DBGC ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n",
|
|
135
|
+ virtnet, iobuf, vq_idx );
|
|
136
|
+
|
|
137
|
+ vring_add_buf ( vq, list, out, in, iobuf, 0 );
|
|
138
|
+ vring_kick ( virtnet->ioaddr, vq, 1 );
|
85
|
139
|
}
|
86
|
140
|
|
87
|
|
-/*
|
88
|
|
- * virtnet_poll
|
89
|
|
- *
|
90
|
|
- * Wait for a frame
|
91
|
|
- *
|
92
|
|
- * return true if there is a packet ready to read
|
93
|
|
- *
|
94
|
|
- * nic->packet should contain data on return
|
95
|
|
- * nic->packetlen should contain length of data
|
|
141
|
+/** Try to keep rx virtqueue filled with iobufs
|
96
|
142
|
*
|
|
143
|
+ * @v netdev Network device
|
97
|
144
|
*/
|
98
|
|
-static int virtnet_poll(struct nic *nic, int retrieve)
|
99
|
|
-{
|
100
|
|
- unsigned int len;
|
101
|
|
- u16 token;
|
102
|
|
- struct virtio_net_hdr *hdr;
|
103
|
|
- struct vring_list list[2];
|
104
|
|
-
|
105
|
|
- if (!vring_more_used(&virtqueue[RX_INDEX]))
|
106
|
|
- return 0;
|
107
|
|
-
|
108
|
|
- if (!retrieve)
|
109
|
|
- return 1;
|
110
|
|
-
|
111
|
|
- token = vring_get_buf(&virtqueue[RX_INDEX], &len);
|
112
|
|
-
|
113
|
|
- BUG_ON(len > sizeof(struct virtio_net_hdr) + ETH_FRAME_LEN);
|
114
|
|
-
|
115
|
|
- hdr = &rx_hdr[token]; /* FIXME: check flags */
|
116
|
|
- len -= sizeof(struct virtio_net_hdr);
|
|
145
|
+static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) {
|
|
146
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
117
|
147
|
|
118
|
|
- nic->packetlen = len;
|
119
|
|
- memcpy(nic->packet, (char *)rx_buffer[token], nic->packetlen);
|
|
148
|
+ while ( virtnet->rx_num_iobufs < NUM_RX_BUF ) {
|
|
149
|
+ struct io_buffer *iobuf;
|
120
|
150
|
|
121
|
|
- /* add buffer to desc */
|
|
151
|
+ /* Try to allocate a buffer, stop for now if out of memory */
|
|
152
|
+ iobuf = alloc_iob ( RX_BUF_SIZE );
|
|
153
|
+ if ( ! iobuf )
|
|
154
|
+ break;
|
122
|
155
|
|
123
|
|
- list[0].addr = (char*)&rx_hdr[token];
|
124
|
|
- list[0].length = sizeof(struct virtio_net_hdr);
|
125
|
|
- list[1].addr = (char*)&rx_buffer[token];
|
126
|
|
- list[1].length = ETH_FRAME_LEN;
|
|
156
|
+ /* Keep track of iobuf so close() can free it */
|
|
157
|
+ list_add ( &iobuf->list, &virtnet->rx_iobufs );
|
127
|
158
|
|
128
|
|
- vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, token, 0);
|
129
|
|
- vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], 1);
|
|
159
|
+ /* Mark packet length until we know the actual size */
|
|
160
|
+ iob_put ( iobuf, RX_BUF_SIZE );
|
130
|
161
|
|
131
|
|
- return 1;
|
|
162
|
+ virtnet_enqueue_iob ( netdev, RX_INDEX, iobuf );
|
|
163
|
+ virtnet->rx_num_iobufs++;
|
|
164
|
+ }
|
132
|
165
|
}
|
133
|
166
|
|
134
|
|
-/*
|
135
|
|
- *
|
136
|
|
- * virtnet_transmit
|
137
|
|
- *
|
138
|
|
- * Transmit a frame
|
|
167
|
+/** Open network device
|
139
|
168
|
*
|
|
169
|
+ * @v netdev Network device
|
|
170
|
+ * @ret rc Return status code
|
140
|
171
|
*/
|
|
172
|
+static int virtnet_open ( struct net_device *netdev ) {
|
|
173
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
174
|
+ unsigned long ioaddr = virtnet->ioaddr;
|
|
175
|
+ u32 features;
|
|
176
|
+ int i;
|
|
177
|
+
|
|
178
|
+ /* Reset for sanity */
|
|
179
|
+ vp_reset ( ioaddr );
|
|
180
|
+
|
|
181
|
+ /* Allocate virtqueues */
|
|
182
|
+ virtnet->virtqueue = zalloc ( QUEUE_NB *
|
|
183
|
+ sizeof ( *virtnet->virtqueue ) );
|
|
184
|
+ if ( ! virtnet->virtqueue )
|
|
185
|
+ return -ENOMEM;
|
|
186
|
+
|
|
187
|
+ /* Initialize rx/tx virtqueues */
|
|
188
|
+ for ( i = 0; i < QUEUE_NB; i++ ) {
|
|
189
|
+ if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) {
|
|
190
|
+ DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n",
|
|
191
|
+ virtnet, i );
|
|
192
|
+ free ( virtnet->virtqueue );
|
|
193
|
+ virtnet->virtqueue = NULL;
|
|
194
|
+ return -ENOENT;
|
|
195
|
+ }
|
|
196
|
+ }
|
|
197
|
+
|
|
198
|
+ /* Initialize rx packets */
|
|
199
|
+ INIT_LIST_HEAD ( &virtnet->rx_iobufs );
|
|
200
|
+ virtnet->rx_num_iobufs = 0;
|
|
201
|
+ virtnet_refill_rx_virtqueue ( netdev );
|
|
202
|
+
|
|
203
|
+ /* Disable interrupts before starting */
|
|
204
|
+ netdev_irq ( netdev, 0 );
|
|
205
|
+
|
|
206
|
+ /* Driver is ready */
|
|
207
|
+ features = vp_get_features ( ioaddr );
|
|
208
|
+ vp_set_features ( ioaddr, features & ( 1 << VIRTIO_NET_F_MAC ) );
|
|
209
|
+ vp_set_status ( ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK );
|
|
210
|
+ return 0;
|
|
211
|
+}
|
141
|
212
|
|
142
|
|
-static void virtnet_transmit(struct nic *nic, const char *destaddr,
|
143
|
|
- unsigned int type, unsigned int len, const char *data)
|
144
|
|
-{
|
145
|
|
- struct vring_list list[2];
|
146
|
|
-
|
147
|
|
- /*
|
148
|
|
- * from http://www.etherboot.org/wiki/dev/devmanual :
|
149
|
|
- * "You do not need more than one transmit buffer."
|
150
|
|
- */
|
|
213
|
+/** Close network device
|
|
214
|
+ *
|
|
215
|
+ * @v netdev Network device
|
|
216
|
+ */
|
|
217
|
+static void virtnet_close ( struct net_device *netdev ) {
|
|
218
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
219
|
+ struct io_buffer *iobuf;
|
|
220
|
+ struct io_buffer *next_iobuf;
|
|
221
|
+
|
|
222
|
+ vp_reset ( virtnet->ioaddr );
|
|
223
|
+
|
|
224
|
+ /* Virtqueues can be freed now that NIC is reset */
|
|
225
|
+ free ( virtnet->virtqueue );
|
|
226
|
+ virtnet->virtqueue = NULL;
|
|
227
|
+
|
|
228
|
+ /* Free rx iobufs */
|
|
229
|
+ list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) {
|
|
230
|
+ free_iob ( iobuf );
|
|
231
|
+ }
|
|
232
|
+ INIT_LIST_HEAD ( &virtnet->rx_iobufs );
|
|
233
|
+ virtnet->rx_num_iobufs = 0;
|
|
234
|
+}
|
151
|
235
|
|
152
|
|
- /* FIXME: initialize header according to vp_get_features() */
|
|
236
|
+/** Transmit packet
|
|
237
|
+ *
|
|
238
|
+ * @v netdev Network device
|
|
239
|
+ * @v iobuf I/O buffer
|
|
240
|
+ * @ret rc Return status code
|
|
241
|
+ */
|
|
242
|
+static int virtnet_transmit ( struct net_device *netdev,
|
|
243
|
+ struct io_buffer *iobuf ) {
|
|
244
|
+ virtnet_enqueue_iob ( netdev, TX_INDEX, iobuf );
|
|
245
|
+ return 0;
|
|
246
|
+}
|
153
|
247
|
|
154
|
|
- tx_virtio_hdr.flags = 0;
|
155
|
|
- tx_virtio_hdr.csum_offset = 0;
|
156
|
|
- tx_virtio_hdr.csum_start = 0;
|
157
|
|
- tx_virtio_hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE;
|
158
|
|
- tx_virtio_hdr.gso_size = 0;
|
159
|
|
- tx_virtio_hdr.hdr_len = 0;
|
|
248
|
+/** Complete packet transmission
|
|
249
|
+ *
|
|
250
|
+ * @v netdev Network device
|
|
251
|
+ */
|
|
252
|
+static void virtnet_process_tx_packets ( struct net_device *netdev ) {
|
|
253
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
254
|
+ struct vring_virtqueue *tx_vq = &virtnet->virtqueue[TX_INDEX];
|
160
|
255
|
|
161
|
|
- /* add ethernet frame into vring */
|
|
256
|
+ while ( vring_more_used ( tx_vq ) ) {
|
|
257
|
+ struct io_buffer *iobuf = vring_get_buf ( tx_vq, NULL );
|
162
|
258
|
|
163
|
|
- BUG_ON(len > sizeof(tx_eth_frame.data));
|
|
259
|
+ DBGC ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n",
|
|
260
|
+ virtnet, iobuf );
|
164
|
261
|
|
165
|
|
- memcpy(tx_eth_frame.hdr.dst_addr, destaddr, ETH_ALEN);
|
166
|
|
- memcpy(tx_eth_frame.hdr.src_addr, nic->node_addr, ETH_ALEN);
|
167
|
|
- tx_eth_frame.hdr.type = htons(type);
|
168
|
|
- memcpy(tx_eth_frame.data, data, len);
|
|
262
|
+ netdev_tx_complete ( netdev, iobuf );
|
|
263
|
+ }
|
|
264
|
+}
|
169
|
265
|
|
170
|
|
- list[0].addr = (char*)&tx_virtio_hdr;
|
171
|
|
- list[0].length = sizeof(struct virtio_net_hdr);
|
172
|
|
- list[1].addr = (char*)&tx_eth_frame;
|
173
|
|
- list[1].length = ETH_FRAME_LEN;
|
|
266
|
+/** Complete packet reception
|
|
267
|
+ *
|
|
268
|
+ * @v netdev Network device
|
|
269
|
+ */
|
|
270
|
+static void virtnet_process_rx_packets ( struct net_device *netdev ) {
|
|
271
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
272
|
+ struct vring_virtqueue *rx_vq = &virtnet->virtqueue[RX_INDEX];
|
174
|
273
|
|
175
|
|
- vring_add_buf(&virtqueue[TX_INDEX], list, 2, 0, 0, 0);
|
|
274
|
+ while ( vring_more_used ( rx_vq ) ) {
|
|
275
|
+ unsigned int len;
|
|
276
|
+ struct io_buffer *iobuf = vring_get_buf ( rx_vq, &len );
|
176
|
277
|
|
177
|
|
- vring_kick(nic->ioaddr, &virtqueue[TX_INDEX], 1);
|
|
278
|
+ /* Release ownership of iobuf */
|
|
279
|
+ list_del ( &iobuf->list );
|
|
280
|
+ virtnet->rx_num_iobufs--;
|
178
|
281
|
|
179
|
|
- /*
|
180
|
|
- * http://www.etherboot.org/wiki/dev/devmanual
|
181
|
|
- *
|
182
|
|
- * "You should ensure the packet is fully transmitted
|
183
|
|
- * before returning from this routine"
|
184
|
|
- */
|
|
282
|
+ /* Update iobuf length */
|
|
283
|
+ iob_unput ( iobuf, RX_BUF_SIZE );
|
|
284
|
+ iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) );
|
185
|
285
|
|
186
|
|
- while (!vring_more_used(&virtqueue[TX_INDEX])) {
|
187
|
|
- mb();
|
188
|
|
- udelay(10);
|
189
|
|
- }
|
|
286
|
+ DBGC ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n",
|
|
287
|
+ virtnet, iobuf, iob_len ( iobuf ) );
|
190
|
288
|
|
191
|
|
- /* free desc */
|
|
289
|
+ /* Pass completed packet to the network stack */
|
|
290
|
+ netdev_rx ( netdev, iobuf );
|
|
291
|
+ }
|
192
|
292
|
|
193
|
|
- (void)vring_get_buf(&virtqueue[TX_INDEX], NULL);
|
|
293
|
+ virtnet_refill_rx_virtqueue ( netdev );
|
194
|
294
|
}
|
195
|
295
|
|
196
|
|
-static void virtnet_irq(struct nic *nic __unused, irq_action_t action)
|
197
|
|
-{
|
198
|
|
- switch ( action ) {
|
199
|
|
- case DISABLE :
|
200
|
|
- vring_disable_cb(&virtqueue[RX_INDEX]);
|
201
|
|
- vring_disable_cb(&virtqueue[TX_INDEX]);
|
202
|
|
- break;
|
203
|
|
- case ENABLE :
|
204
|
|
- vring_enable_cb(&virtqueue[RX_INDEX]);
|
205
|
|
- vring_enable_cb(&virtqueue[TX_INDEX]);
|
206
|
|
- break;
|
207
|
|
- case FORCE :
|
208
|
|
- break;
|
209
|
|
- }
|
|
296
|
+/** Poll for completed and received packets
|
|
297
|
+ *
|
|
298
|
+ * @v netdev Network device
|
|
299
|
+ */
|
|
300
|
+static void virtnet_poll ( struct net_device *netdev ) {
|
|
301
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
302
|
+
|
|
303
|
+ /* Acknowledge interrupt. This is necessary for UNDI operation and
|
|
304
|
+ * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being
|
|
305
|
+ * set (that flag is just a hint and the hypervisor not not have to
|
|
306
|
+ * honor it).
|
|
307
|
+ */
|
|
308
|
+ vp_get_isr ( virtnet->ioaddr );
|
|
309
|
+
|
|
310
|
+ virtnet_process_tx_packets ( netdev );
|
|
311
|
+ virtnet_process_rx_packets ( netdev );
|
210
|
312
|
}
|
211
|
313
|
|
212
|
|
-static void provide_buffers(struct nic *nic)
|
213
|
|
-{
|
214
|
|
- int i;
|
215
|
|
- struct vring_list list[2];
|
216
|
|
-
|
217
|
|
- for (i = 0; i < RX_BUF_NB; i++) {
|
218
|
|
- list[0].addr = (char*)&rx_hdr[i];
|
219
|
|
- list[0].length = sizeof(struct virtio_net_hdr);
|
220
|
|
- list[1].addr = (char*)&rx_buffer[i];
|
221
|
|
- list[1].length = ETH_FRAME_LEN;
|
222
|
|
- vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, i, i);
|
223
|
|
- }
|
224
|
|
-
|
225
|
|
- /* nofify */
|
226
|
|
-
|
227
|
|
- vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], i);
|
|
314
|
+/** Enable or disable interrupts
|
|
315
|
+ *
|
|
316
|
+ * @v netdev Network device
|
|
317
|
+ * @v enable Interrupts should be enabled
|
|
318
|
+ */
|
|
319
|
+static void virtnet_irq ( struct net_device *netdev, int enable ) {
|
|
320
|
+ struct virtnet_nic *virtnet = netdev->priv;
|
|
321
|
+ int i;
|
|
322
|
+
|
|
323
|
+ for ( i = 0; i < QUEUE_NB; i++ ) {
|
|
324
|
+ if ( enable )
|
|
325
|
+ vring_enable_cb ( &virtnet->virtqueue[i] );
|
|
326
|
+ else
|
|
327
|
+ vring_disable_cb ( &virtnet->virtqueue[i] );
|
|
328
|
+ }
|
228
|
329
|
}
|
229
|
330
|
|
230
|
|
-static struct nic_operations virtnet_operations = {
|
231
|
|
- .connect = dummy_connect,
|
232
|
|
- .poll = virtnet_poll,
|
|
331
|
+/** virtio-net device operations */
|
|
332
|
+static struct net_device_operations virtnet_operations = {
|
|
333
|
+ .open = virtnet_open,
|
|
334
|
+ .close = virtnet_close,
|
233
|
335
|
.transmit = virtnet_transmit,
|
|
336
|
+ .poll = virtnet_poll,
|
234
|
337
|
.irq = virtnet_irq,
|
235
|
338
|
};
|
236
|
339
|
|
237
|
|
-/*
|
238
|
|
- * virtnet_probe
|
239
|
|
- *
|
240
|
|
- * Look for a virtio network adapter
|
|
340
|
+/**
|
|
341
|
+ * Probe PCI device
|
241
|
342
|
*
|
|
343
|
+ * @v pci PCI device
|
|
344
|
+ * @v id PCI ID
|
|
345
|
+ * @ret rc Return status code
|
242
|
346
|
*/
|
|
347
|
+static int virtnet_probe ( struct pci_device *pci,
|
|
348
|
+ const struct pci_device_id *id __unused ) {
|
|
349
|
+ unsigned long ioaddr = pci->ioaddr;
|
|
350
|
+ struct net_device *netdev;
|
|
351
|
+ struct virtnet_nic *virtnet;
|
|
352
|
+ u32 features;
|
|
353
|
+ int rc;
|
|
354
|
+
|
|
355
|
+ /* Allocate and hook up net device */
|
|
356
|
+ netdev = alloc_etherdev ( sizeof ( *virtnet ) );
|
|
357
|
+ if ( ! netdev )
|
|
358
|
+ return -ENOMEM;
|
|
359
|
+ netdev_init ( netdev, &virtnet_operations );
|
|
360
|
+ virtnet = netdev->priv;
|
|
361
|
+ virtnet->ioaddr = ioaddr;
|
|
362
|
+ pci_set_drvdata ( pci, netdev );
|
|
363
|
+ netdev->dev = &pci->dev;
|
|
364
|
+
|
|
365
|
+ DBGC ( virtnet, "VIRTIO-NET %p busaddr=%s ioaddr=%#lx irq=%d\n",
|
|
366
|
+ virtnet, pci->dev.name, ioaddr, pci->irq );
|
|
367
|
+
|
|
368
|
+ /* Enable PCI bus master and reset NIC */
|
|
369
|
+ adjust_pci_device ( pci );
|
|
370
|
+ vp_reset ( ioaddr );
|
|
371
|
+
|
|
372
|
+ /* Load MAC address */
|
|
373
|
+ features = vp_get_features ( ioaddr );
|
|
374
|
+ if ( features & ( 1 << VIRTIO_NET_F_MAC ) ) {
|
|
375
|
+ vp_get ( ioaddr, offsetof ( struct virtio_net_config, mac ),
|
|
376
|
+ netdev->hw_addr, ETH_ALEN );
|
|
377
|
+ DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet,
|
|
378
|
+ eth_ntoa ( netdev->hw_addr ) );
|
|
379
|
+ }
|
|
380
|
+
|
|
381
|
+ /* Mark link as up, control virtqueue is not used */
|
|
382
|
+ netdev_link_up ( netdev );
|
|
383
|
+
|
|
384
|
+ if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
|
|
385
|
+ vp_reset ( ioaddr );
|
|
386
|
+ netdev_nullify ( netdev );
|
|
387
|
+ netdev_put ( netdev );
|
|
388
|
+ }
|
|
389
|
+ return rc;
|
|
390
|
+}
|
243
|
391
|
|
244
|
|
-static int virtnet_probe(struct nic *nic, struct pci_device *pci)
|
245
|
|
-{
|
246
|
|
- u32 features;
|
247
|
|
- int i;
|
248
|
|
-
|
249
|
|
- /* Mask the bit that says "this is an io addr" */
|
250
|
|
-
|
251
|
|
- nic->ioaddr = pci->ioaddr & ~3;
|
252
|
|
-
|
253
|
|
- /* Copy IRQ from PCI information */
|
254
|
|
-
|
255
|
|
- nic->irqno = pci->irq;
|
256
|
|
-
|
257
|
|
- printf("I/O address 0x%08x, IRQ #%d\n", nic->ioaddr, nic->irqno);
|
258
|
|
-
|
259
|
|
- adjust_pci_device(pci);
|
260
|
|
-
|
261
|
|
- vp_reset(nic->ioaddr);
|
262
|
|
-
|
263
|
|
- features = vp_get_features(nic->ioaddr);
|
264
|
|
- if (features & (1 << VIRTIO_NET_F_MAC)) {
|
265
|
|
- vp_get(nic->ioaddr, offsetof(struct virtio_net_config, mac),
|
266
|
|
- nic->node_addr, ETH_ALEN);
|
267
|
|
- printf("MAC address ");
|
268
|
|
- for (i = 0; i < ETH_ALEN; i++) {
|
269
|
|
- printf("%02x%c", nic->node_addr[i],
|
270
|
|
- (i == ETH_ALEN - 1) ? '\n' : ':');
|
271
|
|
- }
|
272
|
|
- }
|
273
|
|
-
|
274
|
|
- /* initialize emit/receive queue */
|
275
|
|
-
|
276
|
|
- for (i = 0; i < QUEUE_NB; i++) {
|
277
|
|
- virtqueue[i].free_head = 0;
|
278
|
|
- virtqueue[i].last_used_idx = 0;
|
279
|
|
- memset((char*)&virtqueue[i].queue, 0, sizeof(virtqueue[i].queue));
|
280
|
|
- if (vp_find_vq(nic->ioaddr, i, &virtqueue[i]) == -1)
|
281
|
|
- printf("Cannot register queue #%d\n", i);
|
282
|
|
- }
|
283
|
|
-
|
284
|
|
- /* provide some receive buffers */
|
285
|
|
-
|
286
|
|
- provide_buffers(nic);
|
287
|
|
-
|
288
|
|
- /* define NIC interface */
|
289
|
|
-
|
290
|
|
- nic->nic_op = &virtnet_operations;
|
291
|
|
-
|
292
|
|
- /* driver is ready */
|
293
|
|
-
|
294
|
|
- vp_set_features(nic->ioaddr, features & (1 << VIRTIO_NET_F_MAC));
|
295
|
|
- vp_set_status(nic->ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
|
|
392
|
+/**
|
|
393
|
+ * Remove device
|
|
394
|
+ *
|
|
395
|
+ * @v pci PCI device
|
|
396
|
+ */
|
|
397
|
+static void virtnet_remove ( struct pci_device *pci ) {
|
|
398
|
+ struct net_device *netdev = pci_get_drvdata ( pci );
|
296
|
399
|
|
297
|
|
- return 1;
|
|
400
|
+ unregister_netdev ( netdev );
|
|
401
|
+ netdev_nullify ( netdev );
|
|
402
|
+ netdev_put ( netdev );
|
298
|
403
|
}
|
299
|
404
|
|
300
|
405
|
static struct pci_device_id virtnet_nics[] = {
|
301
|
|
-PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0),
|
|
406
|
+PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0),
|
302
|
407
|
};
|
303
|
408
|
|
304
|
|
-PCI_DRIVER ( virtnet_driver, virtnet_nics, PCI_NO_CLASS );
|
305
|
|
-
|
306
|
|
-DRIVER ( "VIRTIO-NET", nic_driver, pci_driver, virtnet_driver,
|
307
|
|
- virtnet_probe, virtnet_disable );
|
|
409
|
+struct pci_driver virtnet_driver __pci_driver = {
|
|
410
|
+ .ids = virtnet_nics,
|
|
411
|
+ .id_count = ( sizeof ( virtnet_nics ) / sizeof ( virtnet_nics[0] ) ),
|
|
412
|
+ .probe = virtnet_probe,
|
|
413
|
+ .remove = virtnet_remove,
|
|
414
|
+};
|