Quellcode durchsuchen

[virtio] Add legacy driver for KVM virtio-net

This patch adds support for the virtio-net adapter provided by KVM.

Written by Laurent Vivier <Laurent.Vivier@bull.net> for Etherboot.
Wrapped as legacy driver for gPXE by Stefan Hajnoczi
<stefanha@gmail.com>.
tags/v0.9.4
Stefan Hajnoczi vor 16 Jahren
Ursprung
Commit
50babca5da

+ 492
- 0
src/drivers/net/virtio-net.c Datei anzeigen

@@ -0,0 +1,492 @@
1
+/* virtio-net.c - etherboot driver for virtio network interface
2
+ *
3
+ * (c) Copyright 2008 Bull S.A.S.
4
+ *
5
+ *  Author: Laurent Vivier <Laurent.Vivier@bull.net>
6
+ *
7
+ * some parts from Linux Virtio PCI driver
8
+ *
9
+ *  Copyright IBM Corp. 2007
10
+ *  Authors: Anthony Liguori  <aliguori@us.ibm.com>
11
+ *
12
+ *  some parts from Linux Virtio Ring
13
+ *
14
+ *  Copyright Rusty Russell IBM Corporation 2007
15
+ *
16
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
17
+ * See the COPYING file in the top-level directory.
18
+ *
19
+ *
20
+ */
21
+
22
+#include "etherboot.h"
23
+#include "nic.h"
24
+#include "virtio-ring.h"
25
+#include "virtio-pci.h"
26
+#include "virtio-net.h"
27
+
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)
34
+
35
+/* Ethernet header */
36
+
37
+struct eth_hdr {
38
+   unsigned char dst_addr[ETH_ALEN];
39
+   unsigned char src_addr[ETH_ALEN];
40
+   unsigned short type;
41
+};
42
+
43
+struct eth_frame {
44
+   struct eth_hdr hdr;
45
+   unsigned char data[ETH_FRAME_LEN];
46
+};
47
+
48
+typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)];
49
+
50
+/* TX: virtio header and eth buffer */
51
+
52
+static struct virtio_net_hdr tx_virtio_hdr;
53
+static struct eth_frame tx_eth_frame;
54
+
55
+/* RX: virtio headers and buffers */
56
+
57
+#define RX_BUF_NB  6
58
+static struct virtio_net_hdr rx_hdr[RX_BUF_NB];
59
+static unsigned char rx_buffer[RX_BUF_NB][ETH_FRAME_LEN];
60
+
61
+/* virtio queues and vrings */
62
+
63
+enum {
64
+   RX_INDEX = 0,
65
+   TX_INDEX,
66
+   QUEUE_NB
67
+};
68
+
69
+static virtio_queue_t queue[QUEUE_NB];
70
+static struct vring vring[QUEUE_NB];
71
+static u16 free_head[QUEUE_NB];
72
+static u16 last_used_idx[QUEUE_NB];
73
+static u16 vdata[QUEUE_NB][MAX_QUEUE_NUM];
74
+
75
+/*
76
+ * Virtio PCI interface
77
+ *
78
+ */
79
+
80
+static int vp_find_vq(struct nic *nic, int queue_index)
81
+{
82
+   struct vring * vr = &vring[queue_index];
83
+   u16 num;
84
+
85
+   /* select the queue */
86
+
87
+   outw(queue_index, nic->ioaddr + VIRTIO_PCI_QUEUE_SEL);
88
+
89
+   /* check if the queue is available */
90
+
91
+   num = inw(nic->ioaddr + VIRTIO_PCI_QUEUE_NUM);
92
+   if (!num) {
93
+           printf("ERROR: queue size is 0\n");
94
+           return -1;
95
+   }
96
+
97
+   if (num > MAX_QUEUE_NUM) {
98
+           printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
99
+           return -1;
100
+   }
101
+
102
+   /* check if the queue is already active */
103
+
104
+   if (inl(nic->ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
105
+           printf("ERROR: queue already active\n");
106
+           return -1;
107
+   }
108
+
109
+   /* initialize the queue */
110
+
111
+   vring_init(vr, num, (unsigned char*)&queue[queue_index]);
112
+
113
+   /* activate the queue
114
+    *
115
+    * NOTE: vr->desc is initialized by vring_init()
116
+    */
117
+
118
+   outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT,
119
+        nic->ioaddr + VIRTIO_PCI_QUEUE_PFN);
120
+
121
+   return num;
122
+}
123
+
124
+/*
125
+ * Virtual ring management
126
+ *
127
+ */
128
+
129
+static void vring_enable_cb(int queue_index)
130
+{
131
+   vring[queue_index].avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
132
+}
133
+
134
+static void vring_disable_cb(int queue_index)
135
+{
136
+   vring[queue_index].avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
137
+}
138
+
139
+/*
140
+ * vring_free
141
+ *
142
+ * put at the begin of the free list the current desc[head]
143
+ */
144
+
145
+static void vring_detach(int queue_index, unsigned int head)
146
+{
147
+   struct vring *vr = &vring[queue_index];
148
+        unsigned int i;
149
+
150
+        /* find end of given descriptor */
151
+
152
+   i = head;
153
+   while (vr->desc[i].flags & VRING_DESC_F_NEXT)
154
+           i = vr->desc[i].next;
155
+
156
+   /* link it with free list and point to it */
157
+
158
+   vr->desc[i].next = free_head[queue_index];
159
+   wmb();
160
+   free_head[queue_index] = head;
161
+}
162
+
163
+/*
164
+ * vring_more_used
165
+ *
166
+ * is there some used buffers ?
167
+ *
168
+ */
169
+
170
+static inline int vring_more_used(int queue_index)
171
+{
172
+   wmb();
173
+   return last_used_idx[queue_index] != vring[queue_index].used->idx;
174
+}
175
+
176
+/*
177
+ * vring_get_buf
178
+ *
179
+ * get a buffer from the used list
180
+ *
181
+ */
182
+
183
+static int vring_get_buf(int queue_index, unsigned int *len)
184
+{
185
+   struct vring *vr = &vring[queue_index];
186
+   struct vring_used_elem *elem;
187
+   u32 id;
188
+   int ret;
189
+
190
+   elem = &vr->used->ring[last_used_idx[queue_index] % vr->num];
191
+   wmb();
192
+   id = elem->id;
193
+   if (len != NULL)
194
+           *len = elem->len;
195
+
196
+   ret = vdata[queue_index][id];
197
+
198
+   vring_detach(queue_index, id);
199
+
200
+   last_used_idx[queue_index]++;
201
+
202
+   return ret;
203
+}
204
+
205
+static void vring_add_buf(int queue_index, int index, int num_added)
206
+{
207
+   struct vring *vr = &vring[queue_index];
208
+   int i, avail, head;
209
+
210
+   BUG_ON(queue_index >= QUEUE_NB);
211
+
212
+   head = free_head[queue_index];
213
+   i = head;
214
+
215
+   if (queue_index == TX_INDEX) {
216
+
217
+           BUG_ON(index != 0);
218
+
219
+           /* add header into vring */
220
+
221
+           vr->desc[i].flags = VRING_DESC_F_NEXT;
222
+           vr->desc[i].addr = (u64)virt_to_phys(&tx_virtio_hdr);
223
+           vr->desc[i].len = sizeof(struct virtio_net_hdr);
224
+           i = vr->desc[i].next;
225
+
226
+           /* add frame buffer into vring */
227
+
228
+           vr->desc[i].flags = 0;
229
+           vr->desc[i].addr = (u64)virt_to_phys(&tx_eth_frame);
230
+           vr->desc[i].len = ETH_FRAME_LEN;
231
+           i = vr->desc[i].next;
232
+
233
+   } else if (queue_index == RX_INDEX) {
234
+
235
+           BUG_ON(index >= RX_BUF_NB);
236
+
237
+           /* add header into vring */
238
+
239
+           vr->desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
240
+           vr->desc[i].addr = (u64)virt_to_phys(&rx_hdr[index]);
241
+           vr->desc[i].len = sizeof(struct virtio_net_hdr);
242
+           i = vr->desc[i].next;
243
+
244
+           /* add frame buffer into vring */
245
+
246
+           vr->desc[i].flags = VRING_DESC_F_WRITE;
247
+           vr->desc[i].addr = (u64)virt_to_phys(&rx_buffer[index]);
248
+           vr->desc[i].len = ETH_FRAME_LEN;
249
+           i = vr->desc[i].next;
250
+   }
251
+
252
+   free_head[queue_index] = i;
253
+
254
+   vdata[queue_index][head] = index;
255
+
256
+   avail = (vr->avail->idx + num_added) % vr->num;
257
+   vr->avail->ring[avail] = head;
258
+   wmb();
259
+}
260
+
261
+static void vring_kick(struct nic *nic, int queue_index, int num_added)
262
+{
263
+   struct vring *vr = &vring[queue_index];
264
+
265
+   wmb();
266
+   vr->avail->idx += num_added;
267
+
268
+   mb();
269
+   if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY))
270
+           vp_notify(nic, queue_index);
271
+}
272
+
273
+/*
274
+ * virtnet_disable
275
+ *
276
+ * Turn off ethernet interface
277
+ *
278
+ */
279
+
280
+static void virtnet_disable(struct nic *nic)
281
+{
282
+   int i;
283
+
284
+   for (i = 0; i < QUEUE_NB; i++) {
285
+           vring_disable_cb(i);
286
+           vp_del_vq(nic, i);
287
+   }
288
+   vp_reset(nic);
289
+}
290
+
291
+/*
292
+ * virtnet_poll
293
+ *
294
+ * Wait for a frame
295
+ *
296
+ * return true if there is a packet ready to read
297
+ *
298
+ * nic->packet should contain data on return
299
+ * nic->packetlen should contain length of data
300
+ *
301
+ */
302
+static int virtnet_poll(struct nic *nic, int retrieve)
303
+{
304
+   unsigned int len;
305
+   u16 token;
306
+   struct virtio_net_hdr *hdr;
307
+
308
+   if (!vring_more_used(RX_INDEX))
309
+           return 0;
310
+
311
+   if (!retrieve)
312
+           return 1;
313
+
314
+   token = vring_get_buf(RX_INDEX, &len);
315
+
316
+   BUG_ON(len > sizeof(struct virtio_net_hdr) + ETH_FRAME_LEN);
317
+
318
+   hdr = &rx_hdr[token];   /* FIXME: check flags */
319
+   len -= sizeof(struct virtio_net_hdr);
320
+
321
+        nic->packetlen = len;
322
+   memcpy(nic->packet, (char *)rx_buffer[token], nic->packetlen);
323
+
324
+   /* add buffer to desc */
325
+
326
+   vring_add_buf(RX_INDEX, token, 0);
327
+   vring_kick(nic, RX_INDEX, 1);
328
+
329
+   return 1;
330
+}
331
+
332
+/*
333
+ *
334
+ * virtnet_transmit
335
+ *
336
+ * Transmit a frame
337
+ *
338
+ */
339
+
340
+static void virtnet_transmit(struct nic *nic, const char *destaddr,
341
+        unsigned int type, unsigned int len, const char *data)
342
+{
343
+   /*
344
+    * from http://www.etherboot.org/wiki/dev/devmanual :
345
+    *     "You do not need more than one transmit buffer."
346
+    */
347
+
348
+   /* FIXME: initialize header according to vp_get_features() */
349
+
350
+   tx_virtio_hdr.flags = 0;
351
+   tx_virtio_hdr.csum_offset = 0;
352
+   tx_virtio_hdr.csum_start = 0;
353
+   tx_virtio_hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE;
354
+   tx_virtio_hdr.gso_size = 0;
355
+   tx_virtio_hdr.hdr_len = 0;
356
+
357
+   /* add ethernet frame into vring */
358
+
359
+   BUG_ON(len > sizeof(tx_eth_frame.data));
360
+
361
+   memcpy(tx_eth_frame.hdr.dst_addr, destaddr, ETH_ALEN);
362
+   memcpy(tx_eth_frame.hdr.src_addr, nic->node_addr, ETH_ALEN);
363
+   tx_eth_frame.hdr.type = htons(type);
364
+   memcpy(tx_eth_frame.data, data, len);
365
+
366
+   vring_add_buf(TX_INDEX, 0, 0);
367
+
368
+   /*
369
+    * http://www.etherboot.org/wiki/dev/devmanual
370
+    *
371
+    *   "You should ensure the packet is fully transmitted
372
+    *    before returning from this routine"
373
+    */
374
+
375
+   while (vring_more_used(TX_INDEX)) {
376
+           mb();
377
+           udelay(10);
378
+   }
379
+
380
+   vring_kick(nic, TX_INDEX, 1);
381
+
382
+   /* free desc */
383
+
384
+   (void)vring_get_buf(TX_INDEX, NULL);
385
+}
386
+
387
+static void virtnet_irq(struct nic *nic __unused, irq_action_t action)
388
+{
389
+   switch ( action ) {
390
+   case DISABLE :
391
+           vring_disable_cb(RX_INDEX);
392
+           vring_disable_cb(TX_INDEX);
393
+           break;
394
+   case ENABLE :
395
+           vring_enable_cb(RX_INDEX);
396
+           vring_enable_cb(TX_INDEX);
397
+           break;
398
+   case FORCE :
399
+           break;
400
+   }
401
+}
402
+
403
+static void provide_buffers(struct nic *nic)
404
+{
405
+   int i;
406
+
407
+   for (i = 0; i < RX_BUF_NB; i++)
408
+           vring_add_buf(RX_INDEX, i, i);
409
+
410
+   /* nofify */
411
+
412
+   vring_kick(nic, RX_INDEX, i);
413
+}
414
+
415
+static struct nic_operations virtnet_operations = {
416
+	.connect = dummy_connect,
417
+	.poll = virtnet_poll,
418
+	.transmit = virtnet_transmit,
419
+	.irq = virtnet_irq,
420
+};
421
+
422
+/*
423
+ * virtnet_probe
424
+ *
425
+ * Look for a virtio network adapter
426
+ *
427
+ */
428
+
429
+static int virtnet_probe(struct nic *nic, struct pci_device *pci)
430
+{
431
+   u32 features;
432
+   int i;
433
+
434
+   /* Mask the bit that says "this is an io addr" */
435
+
436
+   nic->ioaddr = pci->ioaddr & ~3;
437
+
438
+   /* Copy IRQ from PCI information */
439
+
440
+   nic->irqno = pci->irq;
441
+
442
+   printf("I/O address 0x%08x, IRQ #%d\n", nic->ioaddr, nic->irqno);
443
+
444
+   adjust_pci_device(pci);
445
+
446
+   vp_reset(nic);
447
+
448
+   features = vp_get_features(nic);
449
+   if (features & (1 << VIRTIO_NET_F_MAC)) {
450
+           vp_get(nic, offsetof(struct virtio_net_config, mac),
451
+                  nic->node_addr, ETH_ALEN);
452
+           printf("MAC address ");
453
+	   for (i = 0; i < ETH_ALEN; i++) {
454
+                   printf("%02x%c", nic->node_addr[i],
455
+                          (i == ETH_ALEN - 1) ? '\n' : ':');
456
+           }
457
+   }
458
+
459
+   /* initialize emit/receive queue */
460
+
461
+   for (i = 0; i < QUEUE_NB; i++) {
462
+           free_head[i] = 0;
463
+           last_used_idx[i] = 0;
464
+           memset((char*)&queue[i], 0, sizeof(queue[i]));
465
+           if (vp_find_vq(nic, i) == -1)
466
+                   printf("Cannot register queue #%d\n", i);
467
+   }
468
+
469
+   /* provide some receive buffers */
470
+
471
+    provide_buffers(nic);
472
+
473
+   /* define NIC interface */
474
+
475
+    nic->nic_op = &virtnet_operations;
476
+
477
+   /* driver is ready */
478
+
479
+   vp_set_features(nic, features & (1 << VIRTIO_NET_F_MAC));
480
+   vp_set_status(nic, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
481
+
482
+   return 1;
483
+}
484
+
485
+static struct pci_device_id virtnet_nics[] = {
486
+PCI_ROM(0x1af4, 0x1000, "virtio-net",              "Virtio Network Interface"),
487
+};
488
+
489
+PCI_DRIVER ( virtnet_driver, virtnet_nics, PCI_NO_CLASS );
490
+
491
+DRIVER ( "VIRTIO-NET", nic_driver, pci_driver, virtnet_driver,
492
+	 virtnet_probe, virtnet_disable );

+ 44
- 0
src/drivers/net/virtio-net.h Datei anzeigen

@@ -0,0 +1,44 @@
1
+#ifndef _VIRTIO_NET_H_
2
+# define _VIRTIO_NET_H_
3
+
4
+/* The feature bitmap for virtio net */
5
+#define VIRTIO_NET_F_CSUM       0       /* Host handles pkts w/ partial csum */
6
+#define VIRTIO_NET_F_GUEST_CSUM 1       /* Guest handles pkts w/ partial csum */
7
+#define VIRTIO_NET_F_MAC        5       /* Host has given MAC address. */
8
+#define VIRTIO_NET_F_GSO        6       /* Host handles pkts w/ any GSO type */
9
+#define VIRTIO_NET_F_GUEST_TSO4 7       /* Guest can handle TSOv4 in. */
10
+#define VIRTIO_NET_F_GUEST_TSO6 8       /* Guest can handle TSOv6 in. */
11
+#define VIRTIO_NET_F_GUEST_ECN  9       /* Guest can handle TSO[6] w/ ECN in. */
12
+#define VIRTIO_NET_F_GUEST_UFO  10      /* Guest can handle UFO in. */
13
+#define VIRTIO_NET_F_HOST_TSO4  11      /* Host can handle TSOv4 in. */
14
+#define VIRTIO_NET_F_HOST_TSO6  12      /* Host can handle TSOv6 in. */
15
+#define VIRTIO_NET_F_HOST_ECN   13      /* Host can handle TSO[6] w/ ECN in. */
16
+#define VIRTIO_NET_F_HOST_UFO   14      /* Host can handle UFO in. */
17
+
18
+struct virtio_net_config
19
+{
20
+   /* The config defining mac address (if VIRTIO_NET_F_MAC) */
21
+   u8 mac[6];
22
+} __attribute__((packed));
23
+
24
+/* This is the first element of the scatter-gather list.  If you don't
25
+ * specify GSO or CSUM features, you can simply ignore the header. */
26
+
27
+struct virtio_net_hdr
28
+{
29
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM     1       // Use csum_start, csum_offset
30
+   uint8_t flags;
31
+#define VIRTIO_NET_HDR_GSO_NONE         0       // Not a GSO frame
32
+#define VIRTIO_NET_HDR_GSO_TCPV4        1       // GSO frame, IPv4 TCP (TSO)
33
+/* FIXME: Do we need this?  If they said they can handle ECN, do they care? */
34
+#define VIRTIO_NET_HDR_GSO_TCPV4_ECN    2       // GSO frame, IPv4 TCP w/ ECN
35
+#define VIRTIO_NET_HDR_GSO_UDP          3       // GSO frame, IPv4 UDP (UFO)
36
+#define VIRTIO_NET_HDR_GSO_TCPV6        4       // GSO frame, IPv6 TCP
37
+#define VIRTIO_NET_HDR_GSO_ECN          0x80    // TCP has ECN set
38
+   uint8_t gso_type;
39
+   uint16_t hdr_len;
40
+   uint16_t gso_size;
41
+   uint16_t csum_start;
42
+   uint16_t csum_offset;
43
+};
44
+#endif /* _VIRTIO_NET_H_ */

+ 94
- 0
src/drivers/net/virtio-pci.h Datei anzeigen

@@ -0,0 +1,94 @@
1
+#ifndef _VIRTIO_PCI_H_
2
+# define _VIRTIO_PCI_H_
3
+
4
+/* A 32-bit r/o bitmask of the features supported by the host */
5
+#define VIRTIO_PCI_HOST_FEATURES        0
6
+
7
+/* A 32-bit r/w bitmask of features activated by the guest */
8
+#define VIRTIO_PCI_GUEST_FEATURES       4
9
+
10
+/* A 32-bit r/w PFN for the currently selected queue */
11
+#define VIRTIO_PCI_QUEUE_PFN            8
12
+
13
+/* A 16-bit r/o queue size for the currently selected queue */
14
+#define VIRTIO_PCI_QUEUE_NUM            12
15
+
16
+/* A 16-bit r/w queue selector */
17
+#define VIRTIO_PCI_QUEUE_SEL            14
18
+
19
+/* A 16-bit r/w queue notifier */
20
+#define VIRTIO_PCI_QUEUE_NOTIFY         16
21
+
22
+/* An 8-bit device status register.  */
23
+#define VIRTIO_PCI_STATUS               18
24
+
25
+/* An 8-bit r/o interrupt status register.  Reading the value will return the
26
+ * current contents of the ISR and will also clear it.  This is effectively
27
+ * a read-and-acknowledge. */
28
+#define VIRTIO_PCI_ISR                  19
29
+
30
+/* The bit of the ISR which indicates a device configuration change. */
31
+#define VIRTIO_PCI_ISR_CONFIG           0x2
32
+
33
+/* The remaining space is defined by each driver as the per-driver
34
+ * configuration space */
35
+#define VIRTIO_PCI_CONFIG               20
36
+
37
+/* Virtio ABI version, this must match exactly */
38
+#define VIRTIO_PCI_ABI_VERSION          0
39
+
40
+static inline u32 vp_get_features(struct nic *nic)
41
+{
42
+   return inl(nic->ioaddr + VIRTIO_PCI_HOST_FEATURES);
43
+}
44
+
45
+static inline void vp_set_features(struct nic *nic, u32 features)
46
+{
47
+        outl(features, nic->ioaddr + VIRTIO_PCI_GUEST_FEATURES);
48
+}
49
+
50
+static inline void vp_get(struct nic *nic, unsigned offset,
51
+                     void *buf, unsigned len)
52
+{
53
+   u8 *ptr = buf;
54
+   unsigned i;
55
+
56
+   for (i = 0; i < len; i++)
57
+           ptr[i] = inb(nic->ioaddr + VIRTIO_PCI_CONFIG + offset + i);
58
+}
59
+
60
+static inline u8 vp_get_status(struct nic *nic)
61
+{
62
+   return inb(nic->ioaddr + VIRTIO_PCI_STATUS);
63
+}
64
+
65
+static inline void vp_set_status(struct nic *nic, u8 status)
66
+{
67
+   if (status == 0)        /* reset */
68
+           return;
69
+        outb(status, nic->ioaddr + VIRTIO_PCI_STATUS);
70
+}
71
+
72
+
73
+static inline void vp_reset(struct nic *nic)
74
+{
75
+   outb(0, nic->ioaddr + VIRTIO_PCI_STATUS);
76
+   (void)inb(nic->ioaddr + VIRTIO_PCI_ISR);
77
+}
78
+
79
+static inline void vp_notify(struct nic *nic, int queue_index)
80
+{
81
+   outw(queue_index, nic->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
82
+}
83
+
84
+static inline void vp_del_vq(struct nic *nic, int queue_index)
85
+{
86
+   /* select the queue */
87
+
88
+   outw(queue_index, nic->ioaddr + VIRTIO_PCI_QUEUE_SEL);
89
+
90
+   /* deactivate the queue */
91
+
92
+   outl(0, nic->ioaddr + VIRTIO_PCI_QUEUE_PFN);
93
+}
94
+#endif /* _VIRTIO_PCI_H_ */

+ 93
- 0
src/drivers/net/virtio-ring.h Datei anzeigen

@@ -0,0 +1,93 @@
1
+#ifndef _VIRTIO_RING_H_
2
+# define _VIRTIO_RING_H_
3
+#define PAGE_SHIFT (12)
4
+#define PAGE_SIZE  (1<<PAGE_SHIFT)
5
+#define PAGE_MASK  (PAGE_SIZE-1)
6
+
7
+/* Status byte for guest to report progress, and synchronize features. */
8
+/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
9
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE     1
10
+/* We have found a driver for the device. */
11
+#define VIRTIO_CONFIG_S_DRIVER          2
12
+/* Driver has used its parts of the config, and is happy */
13
+#define VIRTIO_CONFIG_S_DRIVER_OK       4
14
+/* We've given up on this device. */
15
+#define VIRTIO_CONFIG_S_FAILED          0x80
16
+
17
+#define MAX_QUEUE_NUM      (512)
18
+
19
+#define VRING_DESC_F_NEXT  1
20
+#define VRING_DESC_F_WRITE 2
21
+
22
+#define VRING_AVAIL_F_NO_INTERRUPT 1
23
+
24
+#define VRING_USED_F_NO_NOTIFY     1
25
+
26
+struct vring_desc
27
+{
28
+   u64 addr;
29
+   u32 len;
30
+   u16 flags;
31
+   u16 next;
32
+};
33
+
34
+struct vring_avail
35
+{
36
+   u16 flags;
37
+   u16 idx;
38
+   u16 ring[0];
39
+};
40
+
41
+struct vring_used_elem
42
+{
43
+   u32 id;
44
+   u32 len;
45
+};
46
+
47
+struct vring_used
48
+{
49
+   u16 flags;
50
+   u16 idx;
51
+   struct vring_used_elem ring[];
52
+};
53
+
54
+struct vring {
55
+   unsigned int num;
56
+   struct vring_desc *desc;
57
+   struct vring_avail *avail;
58
+   struct vring_used *used;
59
+};
60
+
61
+static inline void vring_init(struct vring *vr,
62
+                         unsigned int num, unsigned char *queue)
63
+{
64
+   unsigned int i;
65
+   unsigned long pa;
66
+
67
+        vr->num = num;
68
+
69
+   /* physical address of desc must be page aligned */
70
+
71
+   pa = virt_to_phys(queue);
72
+   pa = (pa + PAGE_MASK) & ~PAGE_MASK;
73
+   vr->desc = phys_to_virt(pa);
74
+
75
+        vr->avail = (struct vring_avail *)&vr->desc[num];
76
+
77
+   /* physical address of used must be page aligned */
78
+
79
+   pa = virt_to_phys(&vr->avail->ring[num]);
80
+   pa = (pa + PAGE_MASK) & ~PAGE_MASK;
81
+        vr->used = phys_to_virt(pa);
82
+
83
+   for (i = 0; i < num - 1; i++)
84
+           vr->desc[i].next = i + 1;
85
+   vr->desc[i].next = 0;
86
+}
87
+
88
+#define vring_size(num) \
89
+   (((((sizeof(struct vring_desc) * num) + \
90
+      (sizeof(struct vring_avail) + sizeof(u16) * num)) \
91
+         + PAGE_MASK) & ~PAGE_MASK) + \
92
+         (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num))
93
+#endif /* _VIRTIO_RING_H_ */

Laden…
Abbrechen
Speichern