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.


Clear code
[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_LINUX_VIRTIO_VNET_DEBUG
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[1500];
82 };
83
84 static int virtio_reset(struct virtio_vnet_state * vnet_state) {
85
86     memset(vnet_state->queue, 0, sizeof(struct virtio_queue) * 2);
87
88     vnet_state->cur_queue = &(vnet_state->queue[0]);
89
90     vnet_state->virtio_cfg.status = 0;
91     vnet_state->virtio_cfg.pci_isr = 0;
92
93     vnet_state->queue[0].queue_size = QUEUE_SIZE;
94     vnet_state->queue[1].queue_size = QUEUE_SIZE;
95     vnet_state->queue[2].queue_size = QUEUE_SIZE;
96
97     memset(&(vnet_state->vnet_cfg), 0, sizeof(struct vnet_config));
98     v3_lock_init(&(vnet_state->lock));
99
100     return 0;
101 }
102
103
104
105 static int get_desc_count(struct virtio_queue * q, int index) {
106     struct vring_desc * tmp_desc = &(q->desc[index]);
107     int cnt = 1;
108     
109     while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
110         tmp_desc = &(q->desc[tmp_desc->next]);
111         cnt++;
112     }
113
114     return cnt;
115 }
116
117
118
119
120 static int handle_cmd_kick(struct guest_info * core, struct virtio_vnet_state * vnet_state) {
121     struct virtio_queue * q = &(vnet_state->queue[0]);
122     
123     PrintDebug("VNET Bridge: Handling command  queue\n");
124
125     while (q->cur_avail_idx != q->avail->index) {
126         struct vring_desc * hdr_desc = NULL;
127         struct vring_desc * buf_desc = NULL;
128         struct vring_desc * status_desc = NULL;
129         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
130         uint16_t desc_cnt = get_desc_count(q, desc_idx);
131         struct vnet_ctrl_hdr * hdr = NULL;
132         int i;
133         int xfer_len = 0;
134         uint8_t * status_ptr = NULL;
135         uint8_t status = 0;
136
137
138         PrintDebug("VNET Bridge: CMD: Descriptor Count=%d, index=%d, desc_idx=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE, desc_idx);
139
140         if (desc_cnt < 3) {
141             PrintError("VNET Bridge cmd must include at least 3 descriptors (cnt=%d)\n", desc_cnt);
142             return -1;
143         }
144         
145         hdr_desc = &(q->desc[desc_idx]);
146
147         if (guest_pa_to_host_va(core, hdr_desc->addr_gpa, (addr_t *)&hdr) == -1) {
148             PrintError("Could not translate VirtioVNET header address\n");
149             return -1;
150         }
151
152         desc_idx = hdr_desc->next;
153         
154         if (hdr->cmd_type == VNET_ADD_ROUTE) {
155             
156             for (i = 0; i < hdr->num_cmds; i++) {
157                 uint8_t tmp_status = 0;
158                 struct v3_vnet_route * route = NULL;
159                 
160                 buf_desc = &(q->desc[desc_idx]);
161
162                 if (guest_pa_to_host_va(core, buf_desc->addr_gpa, (addr_t *)&(route)) == -1) {
163                     PrintError("Could not translate route address\n");
164                     return -1;
165                 }
166
167                 // add route
168                 PrintDebug("VNET Bridge: Adding VNET Route\n");
169
170                 tmp_status = v3_vnet_add_route(*route);
171
172                 PrintDebug("VNET Route Added\n");
173
174                 if (tmp_status != 0) {
175                     PrintError("Error adding VNET ROUTE\n");
176                     status = tmp_status;
177                 }
178
179                 xfer_len += buf_desc->length;
180                 desc_idx = buf_desc->next;
181             }
182
183         } 
184
185
186
187         status_desc = &(q->desc[desc_idx]);
188
189         if (guest_pa_to_host_va(core, status_desc->addr_gpa, (addr_t *)&status_ptr) == -1) {
190             PrintError("VirtioVNET Error could not translate status address\n");
191             return -1;
192         }
193
194         xfer_len += status_desc->length;
195         *status_ptr = status;
196
197         PrintDebug("Transferred %d bytes (xfer_len)\n", xfer_len);
198         q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
199         q->used->ring[q->used->index % QUEUE_SIZE].length = xfer_len; // set to total inbound xfer length
200
201         q->used->index++;
202         q->cur_avail_idx++;
203     }
204
205
206     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
207         PrintDebug("Raising IRQ %d\n",  vnet_state->pci_dev->config_header.intr_line);
208         v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
209         vnet_state->virtio_cfg.pci_isr = 1;
210     }
211
212
213     return 0;
214 }
215
216
217 static int vnet_pkt_input_cb(struct v3_vm_info * vm,  struct v3_vnet_pkt * pkt,  void * private_data){
218     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
219     struct virtio_queue * q = &(vnet_state->queue[RECV_QUEUE]);
220     int ret_val = -1;
221     unsigned long flags;
222
223     flags = v3_lock_irqsave(vnet_state->lock);
224         
225     PrintDebug("VNET Bridge: RX: pkt sent to guest size: %d\n, pkt_header_len: %d\n", data_len, pkt_head_len);
226
227     if (q->ring_avail_addr == 0) {
228         PrintError("Queue is not set\n");
229         goto exit;
230     }
231
232
233     if (q->cur_avail_idx != q->avail->index) {
234         uint16_t pkt_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
235         struct vring_desc * pkt_desc = NULL;
236         struct vnet_virtio_pkt * virtio_pkt = NULL;
237
238
239         pkt_desc = &(q->desc[pkt_idx]);
240         PrintDebug("VNET Bridge RX: buffer desc len: %d\n", pkt_desc->length);
241
242         if (guest_pa_to_host_va(&(vm->cores[0]), pkt_desc->addr_gpa, (addr_t *)&(virtio_pkt)) == -1) {
243             PrintError("Could not translate buffer address\n");
244             return -1;
245         }
246
247         // Fill in dst packet buffer
248         virtio_pkt->link_id = pkt->dst_id;
249         virtio_pkt->pkt_size = pkt->size;
250         memcpy(virtio_pkt->pkt, pkt->data, pkt->size);
251
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\n");
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
269     ret_val = 0;
270
271 exit:
272     v3_unlock_irqrestore(vnet_state->lock, flags);
273  
274     return ret_val;
275 }
276
277
278 static int handle_pkt_kick(struct guest_info *core, struct virtio_vnet_state * vnet_state) 
279 {
280     struct virtio_queue * q = &(vnet_state->queue[XMIT_QUEUE]);
281
282     PrintDebug("VNET Bridge Device: Handle TX\n");
283
284     while (q->cur_avail_idx != q->avail->index) {
285         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
286         struct vring_desc * pkt_desc = NULL;
287         struct vnet_virtio_pkt * virtio_pkt = NULL;
288
289         pkt_desc = &(q->desc[desc_idx]);
290
291         PrintDebug("VNET Bridge: Handle TX buf_len: %d\n", pkt_desc->length);
292
293         if (guest_pa_to_host_va(core, pkt_desc->addr_gpa, (addr_t *)&(virtio_pkt)) == -1) {
294             PrintError("Could not translate buffer address\n");
295             return -1;
296         }
297
298         //TODO:  SETUP VNET PACKET data structure
299
300         /*
301           if (v3_vnet_send_pkt(pkt, (void *)core) == -1) {
302             PrintError("Error sending packet to vnet\n");
303             return -1;
304         }       
305         */
306         q->used->ring[q->used->index % q->queue_size].id = q->avail->ring[q->cur_avail_idx % q->queue_size];
307         q->used->ring[q->used->index % q->queue_size].length = pkt_desc->length; // What do we set this to????
308         q->used->index++;
309
310         q->cur_avail_idx++;
311     }
312
313     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
314         v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
315         vnet_state->virtio_cfg.pci_isr = 0x1;
316     }
317
318     return 0;
319 }
320
321 static int virtio_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * private_data) {
322     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
323     int port_idx = port % vnet_state->io_range_size;
324
325     PrintDebug("VNET Bridge: VIRTIO VNET Write for port %d len=%d, value=%x\n", 
326                port, length, *(uint32_t *)src);
327     PrintDebug("VNET Bridge: port idx=%d\n", port_idx);
328
329
330     switch (port_idx) {
331         case GUEST_FEATURES_PORT:
332             if (length != 4) {
333                 PrintError("Illegal write length for guest features\n");
334                 return -1;
335             }    
336             vnet_state->virtio_cfg.guest_features = *(uint32_t *)src;
337
338             break;
339         case VRING_PG_NUM_PORT:
340             if (length == 4) {
341                 addr_t pfn = *(uint32_t *)src;
342                 addr_t page_addr = (pfn << VIRTIO_PAGE_SHIFT);
343
344                 vnet_state->cur_queue->pfn = pfn;
345                 
346                 vnet_state->cur_queue->ring_desc_addr = page_addr ;
347                 vnet_state->cur_queue->ring_avail_addr = page_addr + (QUEUE_SIZE * sizeof(struct vring_desc));
348                 vnet_state->cur_queue->ring_used_addr = ( vnet_state->cur_queue->ring_avail_addr + \
349                                                  sizeof(struct vring_avail)    + \
350                                                  (QUEUE_SIZE * sizeof(uint16_t)));
351                 
352                 // round up to next page boundary.
353                 vnet_state->cur_queue->ring_used_addr = (vnet_state->cur_queue->ring_used_addr + 0xfff) & ~0xfff;
354
355                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_desc_addr, (addr_t *)&(vnet_state->cur_queue->desc)) == -1) {
356                     PrintError("Could not translate ring descriptor address\n");
357                     return -1;
358                 }
359
360                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_avail_addr, (addr_t *)&(vnet_state->cur_queue->avail)) == -1) {
361                     PrintError("Could not translate ring available address\n");
362                     return -1;
363                 }
364
365                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_used_addr, (addr_t *)&(vnet_state->cur_queue->used)) == -1) {
366                     PrintError("Could not translate ring used address\n");
367                     return -1;
368                 }
369
370                 PrintDebug("VNET Bridge: RingDesc_addr=%p, Avail_addr=%p, Used_addr=%p\n",
371                            (void *)(vnet_state->cur_queue->ring_desc_addr),
372                            (void *)(vnet_state->cur_queue->ring_avail_addr),
373                            (void *)(vnet_state->cur_queue->ring_used_addr));
374
375                 PrintDebug("VNET Bridge: RingDesc=%p, Avail=%p, Used=%p\n", 
376                            vnet_state->cur_queue->desc, vnet_state->cur_queue->avail, vnet_state->cur_queue->used);
377
378             } else {
379                 PrintError("Illegal write length for page frame number\n");
380                 return -1;
381             }
382             break;
383         case VRING_Q_SEL_PORT:
384             vnet_state->virtio_cfg.vring_queue_selector = *(uint16_t *)src;
385
386             if (vnet_state->virtio_cfg.vring_queue_selector > NUM_QUEUES) {
387                 PrintError("VNET Bridge device has no qeueues. Selected %d\n", 
388                            vnet_state->virtio_cfg.vring_queue_selector);
389                 return -1;
390             }
391             
392             vnet_state->cur_queue = &(vnet_state->queue[vnet_state->virtio_cfg.vring_queue_selector]);
393
394             break;
395         case VRING_Q_NOTIFY_PORT: {
396             uint16_t queue_idx = *(uint16_t *)src;
397
398             PrintDebug("VNET Bridge: Handling Kick\n");
399
400             if (queue_idx == 0) {
401                 if (handle_cmd_kick(core, vnet_state) == -1) {
402                     PrintError("Could not handle Virtio VNET Control command\n");
403                     return -1;
404                 }
405             } else if (queue_idx == 1) {
406                 if (handle_pkt_kick(core, vnet_state) == -1){
407                     PrintError("Could not handle Virtio VNET TX\n");
408                     return -1;
409                 }
410             } else if (queue_idx == 2) {
411                 PrintDebug("VNET Bridge: receive kick on RX Queue\n");
412             } else {
413                 PrintError("VNET Bridge: Kick on invalid queue (%d)\n", queue_idx);
414                 return -1;
415             }
416
417             break;
418         }
419         case VIRTIO_STATUS_PORT:
420             vnet_state->virtio_cfg.status = *(uint8_t *)src;
421
422             if (vnet_state->virtio_cfg.status == 0) {
423                 PrintDebug("VNET Bridge: Resetting device\n");
424                 virtio_reset(vnet_state);
425             }
426
427             break;
428
429         case VIRTIO_ISR_PORT:
430             vnet_state->virtio_cfg.pci_isr = *(uint8_t *)src;
431             break;
432         default:
433             return -1;
434             break;
435     }
436
437     return length;
438 }
439
440
441 static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * private_data) {
442
443     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
444     int port_idx = port % vnet_state->io_range_size;
445
446 /*
447     PrintDebug("VirtioVNET: VIRTIO SYMBIOTIC Read  for port %d (index =%d), length=%d\n", 
448                port, port_idx, length);
449 */
450     switch (port_idx) {
451         case HOST_FEATURES_PORT:
452             if (length != 4) {
453                 PrintError("Illegal read length for host features\n");
454                 return -1;
455             }
456
457             *(uint32_t *)dst = vnet_state->virtio_cfg.host_features;
458         
459             break;
460         case VRING_PG_NUM_PORT:
461             if (length != 4) {
462                 PrintError("Illegal read length for page frame number\n");
463                 return -1;
464             }
465
466             *(uint32_t *)dst = vnet_state->cur_queue->pfn;
467
468             break;
469         case VRING_SIZE_PORT:
470             if (length != 2) {
471                 PrintError("Illegal read length for vring size\n");
472                 return -1;
473             }
474                 
475             *(uint16_t *)dst = vnet_state->cur_queue->queue_size;
476
477             break;
478
479         case VIRTIO_STATUS_PORT:
480             if (length != 1) {
481                 PrintError("Illegal read length for status\n");
482                 return -1;
483             }
484
485             *(uint8_t *)dst = vnet_state->virtio_cfg.status;
486             break;
487
488         case VIRTIO_ISR_PORT:
489             *(uint8_t *)dst = vnet_state->virtio_cfg.pci_isr;
490             vnet_state->virtio_cfg.pci_isr = 0;
491             v3_pci_lower_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
492             break;
493
494         default:
495             if ( (port_idx >= sizeof(struct virtio_config)) && 
496                  (port_idx < (sizeof(struct virtio_config) + sizeof(struct vnet_config))) ) {
497                 int cfg_offset = port_idx - sizeof(struct virtio_config);
498                 uint8_t * cfg_ptr = (uint8_t *)&(vnet_state->vnet_cfg);
499
500                 memcpy(dst, cfg_ptr + cfg_offset, length);
501                 
502             } else {
503                 PrintError("Read of Unhandled Virtio Read\n");
504                 return -1;
505             }
506           
507             break;
508     }
509
510     return length;
511 }
512
513
514
515 static struct v3_device_ops dev_ops = {
516     .free = NULL,
517     .reset = NULL,
518     .start = NULL,
519     .stop = NULL,
520 };
521
522
523 static int dev_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
524     struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
525     struct virtio_vnet_state * vnet_state = NULL;
526     struct pci_device * pci_dev = NULL;
527     char * name = v3_cfg_val(cfg, "name");
528
529     PrintDebug("VNET Bridge: Initializing VNET Bridge Control device: %s\n", name);
530
531     if (pci_bus == NULL) {
532         PrintError("VNET Bridge device require a PCI Bus");
533         return -1;
534     }
535     
536     vnet_state  = (struct virtio_vnet_state *)V3_Malloc(sizeof(struct virtio_vnet_state));
537     memset(vnet_state, 0, sizeof(struct virtio_vnet_state));
538         
539     vnet_state->vm = vm;
540
541     struct vm_device * dev = v3_allocate_device(name, &dev_ops, vnet_state);
542
543     if (v3_attach_device(vm, dev) == -1) {
544         PrintError("Could not attach device %s\n", name);
545         return -1;
546     }
547
548
549     // PCI initialization
550     {
551         struct v3_pci_bar bars[6];
552         int num_ports = sizeof(struct virtio_config) + sizeof(struct vnet_config);
553         int tmp_ports = num_ports;
554         int i;
555
556         // This gets the number of ports, rounded up to a power of 2
557         vnet_state->io_range_size = 1; // must be a power of 2
558
559         while (tmp_ports > 0) {
560             tmp_ports >>= 1;
561             vnet_state->io_range_size <<= 1;
562         }
563         
564         // this is to account for any low order bits being set in num_ports
565         // if there are none, then num_ports was already a power of 2 so we shift right to reset it
566         if ((num_ports & ((vnet_state->io_range_size >> 1) - 1)) == 0) {
567             vnet_state->io_range_size >>= 1;
568         }
569
570         for (i = 0; i < 6; i++) {
571             bars[i].type = PCI_BAR_NONE;
572         }
573
574         bars[0].type = PCI_BAR_IO;
575         bars[0].default_base_port = -1;
576         bars[0].num_ports = vnet_state->io_range_size;
577         bars[0].io_read = virtio_io_read;
578         bars[0].io_write = virtio_io_write;
579         bars[0].private_data = vnet_state;
580
581         pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
582                                          0, PCI_AUTO_DEV_NUM, 0,
583                                          "LNX_VIRTIO_VNET", bars,
584                                          NULL, NULL, NULL, vnet_state);
585
586         if (!pci_dev) {
587             PrintError("Could not register PCI Device\n");
588             return -1;
589         }
590         
591         pci_dev->config_header.vendor_id = VIRTIO_VENDOR_ID;
592         pci_dev->config_header.subsystem_vendor_id = VIRTIO_SUBVENDOR_ID;
593         pci_dev->config_header.device_id = VIRTIO_VNET_DEV_ID;
594         pci_dev->config_header.class = PCI_CLASS_MEMORY;
595         pci_dev->config_header.subclass = PCI_MEM_SUBCLASS_RAM;
596         pci_dev->config_header.subsystem_id = VIRTIO_VNET_SUBDEVICE_ID;
597         pci_dev->config_header.intr_pin = 1;
598         pci_dev->config_header.max_latency = 1; // ?? (qemu does it...)
599
600
601         vnet_state->pci_dev = pci_dev;
602         vnet_state->pci_bus = pci_bus;
603     }
604
605     virtio_reset(vnet_state);
606
607     V3_Print("Registering Virtio device as vnet bridge\n");
608     v3_vnet_add_bridge(vm, vnet_pkt_input_cb, (void *)vnet_state);
609
610     return 0;
611 }
612
613
614 device_register("LNX_VIRTIO_VNET", dev_init)