Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Minimize the copies for vnet and Virtio bridge
[palacios.git] / palacios / src / devices / lnx_virtio_vnet.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22 #include <palacios/vm_guest_mem.h>
23 #include <devices/lnx_virtio_pci.h>
24 #include <palacios/vmm_vnet.h>
25 #include <palacios/vmm_sprintf.h>
26 #include <devices/pci.h>
27
28
29 #ifndef CONFIG_DEBUG_LINUX_VIRTIO_VNET
30 #undef PrintDebug
31 #define PrintDebug(fmt, args...)
32 #endif
33
34
35 #define QUEUE_SIZE 128
36 #define NUM_QUEUES 3
37
38 struct vnet_config {
39     uint32_t num_devs;
40     uint32_t num_routes;
41 } __attribute__((packed));
42
43
44 #define CTRL_QUEUE 0
45 #define XMIT_QUEUE 1
46 #define RECV_QUEUE 2
47
48 struct virtio_vnet_state {
49     struct v3_vm_info * vm;
50     struct vnet_config vnet_cfg;
51     struct virtio_config virtio_cfg;
52
53     struct vm_device * pci_bus;
54     struct pci_device * pci_dev;
55         
56     struct virtio_queue queue[NUM_QUEUES];
57
58     struct virtio_queue * cur_queue;
59
60     int io_range_size;
61     v3_lock_t lock;
62 };
63
64 #define VNET_GET_ROUTES 10
65 #define VNET_ADD_ROUTE 11
66 #define VNET_DEL_ROUTE 12
67
68 #define VNET_GET_LINKS 20
69 #define VNET_ADD_LINK 21
70 #define VNET_DEL_LINK 22
71
72 // structure of the vnet command header
73 struct vnet_ctrl_hdr {
74     uint8_t cmd_type;
75     uint32_t num_cmds;
76 } __attribute__((packed));
77
78 struct vnet_virtio_pkt {
79     uint32_t link_id;
80     uint32_t pkt_size;
81     uint8_t pkt[ETHERNET_PACKET_LEN];
82 }__attribute__((packed));
83
84
85 static int virtio_reset(struct virtio_vnet_state * vnet_state) {
86
87     memset(vnet_state->queue, 0, sizeof(struct virtio_queue) * 2);
88
89     vnet_state->cur_queue = &(vnet_state->queue[0]);
90
91     vnet_state->virtio_cfg.status = 0;
92     vnet_state->virtio_cfg.pci_isr = 0;
93
94     vnet_state->queue[0].queue_size = QUEUE_SIZE;
95     vnet_state->queue[1].queue_size = QUEUE_SIZE;
96     vnet_state->queue[2].queue_size = QUEUE_SIZE;
97
98     memset(&(vnet_state->vnet_cfg), 0, sizeof(struct vnet_config));
99     v3_lock_init(&(vnet_state->lock));
100
101     return 0;
102 }
103
104
105
106 static int get_desc_count(struct virtio_queue * q, int index) {
107     struct vring_desc * tmp_desc = &(q->desc[index]);
108     int cnt = 1;
109     
110     while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
111         tmp_desc = &(q->desc[tmp_desc->next]);
112         cnt++;
113     }
114
115     return cnt;
116 }
117
118
119
120
121 static int handle_cmd_kick(struct guest_info * core, struct virtio_vnet_state * vnet_state) {
122     struct virtio_queue * q = &(vnet_state->queue[0]);
123     
124     PrintDebug("VNET Bridge: Handling command  queue\n");
125
126     while (q->cur_avail_idx != q->avail->index) {
127         struct vring_desc * hdr_desc = NULL;
128         struct vring_desc * buf_desc = NULL;
129         struct vring_desc * status_desc = NULL;
130         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
131         uint16_t desc_cnt = get_desc_count(q, desc_idx);
132         struct vnet_ctrl_hdr * hdr = NULL;
133         int i;
134         int xfer_len = 0;
135         uint8_t * status_ptr = NULL;
136         uint8_t status = 0;
137
138
139         PrintDebug("VNET Bridge: CMD: Descriptor Count=%d, index=%d, desc_idx=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE, desc_idx);
140
141         if (desc_cnt < 3) {
142             PrintError("VNET Bridge cmd must include at least 3 descriptors (cnt=%d)\n", desc_cnt);
143             return -1;
144         }
145         
146         hdr_desc = &(q->desc[desc_idx]);
147
148         if (guest_pa_to_host_va(core, hdr_desc->addr_gpa, (addr_t *)&hdr) == -1) {
149             PrintError("Could not translate VirtioVNET header address\n");
150             return -1;
151         }
152
153         desc_idx = hdr_desc->next;
154         
155         if (hdr->cmd_type == VNET_ADD_ROUTE) {
156             
157             for (i = 0; i < hdr->num_cmds; i++) {
158                 uint8_t tmp_status = 0;
159                 struct v3_vnet_route * route = NULL;
160                 
161                 buf_desc = &(q->desc[desc_idx]);
162
163                 if (guest_pa_to_host_va(core, buf_desc->addr_gpa, (addr_t *)&(route)) == -1) {
164                     PrintError("Could not translate route address\n");
165                     return -1;
166                 }
167
168                 // add route
169                 PrintDebug("VNET Bridge: Adding VNET Route\n");
170
171                 tmp_status = v3_vnet_add_route(*route);
172
173                 PrintDebug("VNET Route Added\n");
174
175                 if (tmp_status != 0) {
176                     PrintError("Error adding VNET ROUTE\n");
177                     status = tmp_status;
178                 }
179
180                 xfer_len += buf_desc->length;
181                 desc_idx = buf_desc->next;
182             }
183
184         } 
185
186
187
188         status_desc = &(q->desc[desc_idx]);
189
190         if (guest_pa_to_host_va(core, status_desc->addr_gpa, (addr_t *)&status_ptr) == -1) {
191             PrintError("VirtioVNET Error could not translate status address\n");
192             return -1;
193         }
194
195         xfer_len += status_desc->length;
196         *status_ptr = status;
197
198         PrintDebug("Transferred %d bytes (xfer_len)\n", xfer_len);
199         q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
200         q->used->ring[q->used->index % QUEUE_SIZE].length = xfer_len; // set to total inbound xfer length
201
202         q->used->index++;
203         q->cur_avail_idx++;
204     }
205
206
207     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
208         PrintDebug("Raising IRQ %d\n",  vnet_state->pci_dev->config_header.intr_line);
209         v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
210         vnet_state->virtio_cfg.pci_isr = 1;
211     }
212
213
214     return 0;
215 }
216
217
218 static int vnet_pkt_input_cb(struct v3_vm_info * vm,  struct v3_vnet_pkt * pkt,  void * private_data){
219     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
220     struct virtio_queue * q = &(vnet_state->queue[RECV_QUEUE]);
221     int ret_val = -1;
222     unsigned long flags;
223
224     flags = v3_lock_irqsave(vnet_state->lock);
225         
226     if (q->ring_avail_addr == 0) {
227         PrintError("Queue is not set\n");
228         goto exit;
229     }
230
231
232     if (q->cur_avail_idx != q->avail->index) {
233         uint16_t pkt_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
234         struct vring_desc * pkt_desc = NULL;
235         struct vnet_virtio_pkt * virtio_pkt = NULL;
236
237
238         pkt_desc = &(q->desc[pkt_idx]);
239         PrintDebug("VNET Bridge RX: buffer desc len: %d\n", pkt_desc->length);
240
241         if (guest_pa_to_host_va(&(vm->cores[0]), pkt_desc->addr_gpa, (addr_t *)&(virtio_pkt)) == -1) {
242             PrintError("Could not translate buffer address\n");
243             return -1;
244         }
245
246         PrintDebug("VNET Bridge: RX: pkt sent to guest pkt size: %d, dst link: %d\n", pkt->size, pkt->dst_id);
247
248         // Fill in dst packet buffer
249         virtio_pkt->link_id = pkt->dst_id;
250         virtio_pkt->pkt_size = pkt->size;
251         memcpy(virtio_pkt->pkt, pkt->data, pkt->size);
252         
253         q->used->ring[q->used->index % q->queue_size].id = q->avail->ring[q->cur_avail_idx % q->queue_size];
254         q->used->ring[q->used->index % q->queue_size].length = sizeof(struct vnet_virtio_pkt); // This should be the total length of data sent to guest (header+pkt_data)
255
256         q->used->index++;
257         q->cur_avail_idx++;
258     } else {
259         PrintError("Packet buffer overflow in the guest: cur_avai_idx %d, idx: %d\n", q->cur_avail_idx, q->avail->index);
260     }
261
262     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
263         v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
264         vnet_state->virtio_cfg.pci_isr = 0x1;
265         PrintDebug("Raising IRQ %d\n",  vnet_state->pci_dev->config_header.intr_line);
266     }
267
268     ret_val = 0;
269
270 exit:
271     v3_unlock_irqrestore(vnet_state->lock, flags);
272  
273     return ret_val;
274 }
275
276
277 static int handle_pkt_kick(struct guest_info *core, struct virtio_vnet_state * vnet_state) 
278 {
279     struct virtio_queue * q = &(vnet_state->queue[XMIT_QUEUE]);
280     struct v3_vnet_pkt pkt;
281
282     while (q->cur_avail_idx != q->avail->index) {
283         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
284         struct vring_desc * pkt_desc = NULL;
285         struct vnet_virtio_pkt * virtio_pkt = NULL;
286
287         pkt_desc = &(q->desc[desc_idx]);
288
289         PrintDebug("VNET Bridge: Handle TX desc buf_len: %d\n", pkt_desc->length);
290
291         //PrintDebug("q: %p, %p, %p, %d\n", q, vnet_state->queue, &vnet_state->queue[XMIT_QUEUE], XMIT_QUEUE);
292         //PrintDebug("q->used: %p\n", q->used);
293         //PrintDebug("q->used->ring %p\n", q->used->ring);
294
295         if (guest_pa_to_host_va(core, pkt_desc->addr_gpa, (addr_t *)&(virtio_pkt)) == -1) {
296             PrintError("Could not translate buffer address\n");
297             return -1;
298         }
299
300         PrintDebug("VNET Bridge: TX: pkt size: %d, dst link: %d\n", virtio_pkt->pkt_size, virtio_pkt->link_id);
301
302         pkt.size = virtio_pkt->pkt_size;
303         pkt.src_id = virtio_pkt->link_id;
304         pkt.src_type = LINK_EDGE;
305         memcpy(pkt.header, virtio_pkt->pkt, ETHERNET_HEADER_LEN);
306         pkt.data = virtio_pkt->pkt;
307
308         v3_vnet_send_pkt(&pkt, NULL);
309
310         q = (struct virtio_queue *)((addr_t)vnet_state->queue + XMIT_QUEUE*sizeof(struct virtio_queue));
311
312         PrintDebug("After q: %p, , %p, %p, %d\n", q, vnet_state->queue, &(vnet_state->queue[XMIT_QUEUE]), XMIT_QUEUE);
313         //PrintDebug("After q->used: %p\n", q->used);
314         //PrintDebug("After q->used->ring %p\n", q->used->ring);
315         
316         q->used->ring[q->used->index % q->queue_size].id = q->avail->ring[q->cur_avail_idx % q->queue_size];
317         q->used->ring[q->used->index % q->queue_size].length = pkt_desc->length; // What do we set this to????
318         q->used->index++;
319
320         q->cur_avail_idx++;
321     }
322
323     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
324         v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
325         vnet_state->virtio_cfg.pci_isr = 0x1;
326     }
327
328     return 0;
329 }
330
331 static int virtio_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * private_data) {
332     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
333     int port_idx = port % vnet_state->io_range_size;
334
335     PrintDebug("VNET Bridge: VIRTIO VNET Write for port %d len=%d, value=%x\n", 
336                port, length, *(uint32_t *)src);
337     PrintDebug("VNET Bridge: port idx=%d\n", port_idx);
338
339
340     switch (port_idx) {
341         case GUEST_FEATURES_PORT:
342             if (length != 4) {
343                 PrintError("Illegal write length for guest features\n");
344                 return -1;
345             }    
346             vnet_state->virtio_cfg.guest_features = *(uint32_t *)src;
347
348             break;
349         case VRING_PG_NUM_PORT:
350             if (length == 4) {
351                 addr_t pfn = *(uint32_t *)src;
352                 addr_t page_addr = (pfn << VIRTIO_PAGE_SHIFT);
353
354                 vnet_state->cur_queue->pfn = pfn;
355                 
356                 vnet_state->cur_queue->ring_desc_addr = page_addr ;
357                 vnet_state->cur_queue->ring_avail_addr = page_addr + (QUEUE_SIZE * sizeof(struct vring_desc));
358                 vnet_state->cur_queue->ring_used_addr = ( vnet_state->cur_queue->ring_avail_addr + \
359                                                  sizeof(struct vring_avail)    + \
360                                                  (QUEUE_SIZE * sizeof(uint16_t)));
361                 
362                 // round up to next page boundary.
363                 vnet_state->cur_queue->ring_used_addr = (vnet_state->cur_queue->ring_used_addr + 0xfff) & ~0xfff;
364
365                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_desc_addr, (addr_t *)&(vnet_state->cur_queue->desc)) == -1) {
366                     PrintError("Could not translate ring descriptor address\n");
367                     return -1;
368                 }
369
370                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_avail_addr, (addr_t *)&(vnet_state->cur_queue->avail)) == -1) {
371                     PrintError("Could not translate ring available address\n");
372                     return -1;
373                 }
374
375                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_used_addr, (addr_t *)&(vnet_state->cur_queue->used)) == -1) {
376                     PrintError("Could not translate ring used address\n");
377                     return -1;
378                 }
379
380                 PrintDebug("VNET Bridge: RingDesc_addr=%p, Avail_addr=%p, Used_addr=%p\n",
381                            (void *)(vnet_state->cur_queue->ring_desc_addr),
382                            (void *)(vnet_state->cur_queue->ring_avail_addr),
383                            (void *)(vnet_state->cur_queue->ring_used_addr));
384
385                 PrintDebug("VNET Bridge: RingDesc=%p, Avail=%p, Used=%p\n", 
386                            vnet_state->cur_queue->desc, vnet_state->cur_queue->avail, vnet_state->cur_queue->used);
387
388             } else {
389                 PrintError("Illegal write length for page frame number\n");
390                 return -1;
391             }
392             break;
393         case VRING_Q_SEL_PORT:
394             vnet_state->virtio_cfg.vring_queue_selector = *(uint16_t *)src;
395
396             if (vnet_state->virtio_cfg.vring_queue_selector > NUM_QUEUES) {
397                 PrintError("VNET Bridge device has no qeueues. Selected %d\n", 
398                            vnet_state->virtio_cfg.vring_queue_selector);
399                 return -1;
400             }
401             
402             vnet_state->cur_queue = &(vnet_state->queue[vnet_state->virtio_cfg.vring_queue_selector]);
403
404             break;
405         case VRING_Q_NOTIFY_PORT: {
406             uint16_t queue_idx = *(uint16_t *)src;
407
408             PrintDebug("VNET Bridge: Handling Kick\n");
409
410             if (queue_idx == 0) {
411                 if (handle_cmd_kick(core, vnet_state) == -1) {
412                     PrintError("Could not handle Virtio VNET Control command\n");
413                     return -1;
414                 }
415             } else if (queue_idx == 1) {
416                 if (handle_pkt_kick(core, vnet_state) == -1){
417                     PrintError("Could not handle Virtio VNET TX\n");
418                     return -1;
419                 }
420             } else if (queue_idx == 2) {
421                 PrintDebug("VNET Bridge: receive kick on RX Queue\n");
422             } else {
423                 PrintError("VNET Bridge: Kick on invalid queue (%d)\n", queue_idx);
424                 return -1;
425             }
426
427             break;
428         }
429         case VIRTIO_STATUS_PORT:
430             vnet_state->virtio_cfg.status = *(uint8_t *)src;
431
432             if (vnet_state->virtio_cfg.status == 0) {
433                 PrintDebug("VNET Bridge: Resetting device\n");
434                 virtio_reset(vnet_state);
435             }
436
437             break;
438
439         case VIRTIO_ISR_PORT:
440             vnet_state->virtio_cfg.pci_isr = *(uint8_t *)src;
441             break;
442         default:
443             return -1;
444             break;
445     }
446
447     return length;
448 }
449
450
451 static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * private_data) {
452
453     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
454     int port_idx = port % vnet_state->io_range_size;
455
456 /*
457     PrintDebug("VirtioVNET: VIRTIO SYMBIOTIC Read  for port %d (index =%d), length=%d\n", 
458                port, port_idx, length);
459 */
460     switch (port_idx) {
461         case HOST_FEATURES_PORT:
462             if (length != 4) {
463                 PrintError("Illegal read length for host features\n");
464                 return -1;
465             }
466
467             *(uint32_t *)dst = vnet_state->virtio_cfg.host_features;
468         
469             break;
470         case VRING_PG_NUM_PORT:
471             if (length != 4) {
472                 PrintError("Illegal read length for page frame number\n");
473                 return -1;
474             }
475
476             *(uint32_t *)dst = vnet_state->cur_queue->pfn;
477
478             break;
479         case VRING_SIZE_PORT:
480             if (length != 2) {
481                 PrintError("Illegal read length for vring size\n");
482                 return -1;
483             }
484                 
485             *(uint16_t *)dst = vnet_state->cur_queue->queue_size;
486
487             break;
488
489         case VIRTIO_STATUS_PORT:
490             if (length != 1) {
491                 PrintError("Illegal read length for status\n");
492                 return -1;
493             }
494
495             *(uint8_t *)dst = vnet_state->virtio_cfg.status;
496             break;
497
498         case VIRTIO_ISR_PORT:
499             *(uint8_t *)dst = vnet_state->virtio_cfg.pci_isr;
500             vnet_state->virtio_cfg.pci_isr = 0;
501             v3_pci_lower_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
502             break;
503
504         default:
505             if ( (port_idx >= sizeof(struct virtio_config)) && 
506                  (port_idx < (sizeof(struct virtio_config) + sizeof(struct vnet_config))) ) {
507                 int cfg_offset = port_idx - sizeof(struct virtio_config);
508                 uint8_t * cfg_ptr = (uint8_t *)&(vnet_state->vnet_cfg);
509
510                 memcpy(dst, cfg_ptr + cfg_offset, length);
511                 
512             } else {
513                 PrintError("Read of Unhandled Virtio Read\n");
514                 return -1;
515             }
516           
517             break;
518     }
519
520     return length;
521 }
522
523
524
525 static struct v3_device_ops dev_ops = {
526     .free = NULL,
527     .reset = NULL,
528     .start = NULL,
529     .stop = NULL,
530 };
531
532
533 static int dev_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
534     struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
535     struct virtio_vnet_state * vnet_state = NULL;
536     struct pci_device * pci_dev = NULL;
537     char * name = v3_cfg_val(cfg, "name");
538
539     PrintDebug("VNET Bridge: Initializing VNET Bridge Control device: %s\n", name);
540
541     if (pci_bus == NULL) {
542         PrintError("VNET Bridge device require a PCI Bus");
543         return -1;
544     }
545     
546     vnet_state  = (struct virtio_vnet_state *)V3_Malloc(sizeof(struct virtio_vnet_state));
547     memset(vnet_state, 0, sizeof(struct virtio_vnet_state));
548         
549     vnet_state->vm = vm;
550
551     struct vm_device * dev = v3_allocate_device(name, &dev_ops, vnet_state);
552
553     if (v3_attach_device(vm, dev) == -1) {
554         PrintError("Could not attach device %s\n", name);
555         return -1;
556     }
557
558
559     // PCI initialization
560     {
561         struct v3_pci_bar bars[6];
562         int num_ports = sizeof(struct virtio_config) + sizeof(struct vnet_config);
563         int tmp_ports = num_ports;
564         int i;
565
566         // This gets the number of ports, rounded up to a power of 2
567         vnet_state->io_range_size = 1; // must be a power of 2
568
569         while (tmp_ports > 0) {
570             tmp_ports >>= 1;
571             vnet_state->io_range_size <<= 1;
572         }
573         
574         // this is to account for any low order bits being set in num_ports
575         // if there are none, then num_ports was already a power of 2 so we shift right to reset it
576         if ((num_ports & ((vnet_state->io_range_size >> 1) - 1)) == 0) {
577             vnet_state->io_range_size >>= 1;
578         }
579
580         for (i = 0; i < 6; i++) {
581             bars[i].type = PCI_BAR_NONE;
582         }
583
584         bars[0].type = PCI_BAR_IO;
585         bars[0].default_base_port = -1;
586         bars[0].num_ports = vnet_state->io_range_size;
587         bars[0].io_read = virtio_io_read;
588         bars[0].io_write = virtio_io_write;
589         bars[0].private_data = vnet_state;
590
591         pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
592                                          0, PCI_AUTO_DEV_NUM, 0,
593                                          "LNX_VIRTIO_VNET", bars,
594                                          NULL, NULL, NULL, vnet_state);
595
596         if (!pci_dev) {
597             PrintError("Could not register PCI Device\n");
598             return -1;
599         }
600         
601         pci_dev->config_header.vendor_id = VIRTIO_VENDOR_ID;
602         pci_dev->config_header.subsystem_vendor_id = VIRTIO_SUBVENDOR_ID;
603         pci_dev->config_header.device_id = VIRTIO_VNET_DEV_ID;
604         pci_dev->config_header.class = PCI_CLASS_MEMORY;
605         pci_dev->config_header.subclass = PCI_MEM_SUBCLASS_RAM;
606         pci_dev->config_header.subsystem_id = VIRTIO_VNET_SUBDEVICE_ID;
607         pci_dev->config_header.intr_pin = 1;
608         pci_dev->config_header.max_latency = 1; // ?? (qemu does it...)
609
610
611         vnet_state->pci_dev = pci_dev;
612         vnet_state->pci_bus = pci_bus;
613     }
614
615     virtio_reset(vnet_state);
616
617     V3_Print("Registering Virtio device as vnet bridge\n");
618     v3_vnet_add_bridge(vm, vnet_pkt_input_cb, (void *)vnet_state);
619
620     return 0;
621 }
622
623
624 device_register("LNX_VIRTIO_VNET", dev_init)