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.


vnet cleanup
[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 <devices/pci.h>
26
27
28 #define QUEUE_SIZE 128
29 #define NUM_QUEUES 3
30
31 struct vnet_config {
32     uint32_t num_links;
33     uint32_t num_routes;
34 } __attribute__((packed));
35
36
37
38 struct virtio_vnet_state {
39     struct vnet_config vnet_cfg;
40     struct virtio_config virtio_cfg;
41
42
43     struct vm_device * pci_bus;
44     struct pci_device * pci_dev;
45
46
47 #define CTRL_QUEUE 0
48 #define RECV_QUEUE 1
49 #define XMIT_QUEUE 2
50     struct virtio_queue queue[NUM_QUEUES];
51
52     struct virtio_queue * cur_queue;
53
54
55     int io_range_size;
56 };
57
58 #define VNET_GET_ROUTES 10
59 #define VNET_ADD_ROUTE 11
60 #define VNET_DEL_ROUTE 12
61
62 #define VNET_GET_LINKS 20
63 #define VNET_ADD_LINK 21
64 #define VNET_DEL_LINK 22
65
66 // structure of the vnet command header
67 struct vnet_ctrl_hdr {
68     uint8_t cmd_type;
69     uint32_t num_cmds;
70 } __attribute__((packed));
71
72
73 static int virtio_reset(struct virtio_vnet_state * virtio) {
74
75     memset(virtio->queue, 0, sizeof(struct virtio_queue) * 2);
76
77     virtio->cur_queue = &(virtio->queue[0]);
78
79     virtio->virtio_cfg.status = 0;
80     virtio->virtio_cfg.pci_isr = 0;
81
82     virtio->queue[0].queue_size = QUEUE_SIZE;
83     virtio->queue[1].queue_size = QUEUE_SIZE;
84     virtio->queue[2].queue_size = QUEUE_SIZE;
85
86     memset(&(virtio->vnet_cfg), 0, sizeof(struct vnet_config));
87
88     return 0;
89 }
90
91
92
93 static int get_desc_count(struct virtio_queue * q, int index) {
94     struct vring_desc * tmp_desc = &(q->desc[index]);
95     int cnt = 1;
96     
97     while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
98         tmp_desc = &(q->desc[tmp_desc->next]);
99         cnt++;
100     }
101
102     return cnt;
103 }
104
105
106
107
108 static int handle_cmd_kick(struct guest_info * core, struct virtio_vnet_state * vnet_state) {
109     struct virtio_queue * q = &(vnet_state->queue[0]);
110     
111     PrintDebug("VirtioVNET: Virtio Kick on command  queue\n");
112
113     while (q->cur_avail_idx < q->avail->index) {
114         struct vring_desc * hdr_desc = NULL;
115         struct vring_desc * buf_desc = NULL;
116         struct vring_desc * status_desc = NULL;
117         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
118         uint16_t desc_cnt = get_desc_count(q, desc_idx);
119         struct vnet_ctrl_hdr * hdr = NULL;
120         int i;
121         int xfer_len = 0;
122         uint8_t * status_ptr = NULL;
123         uint8_t status = 0;
124
125
126         PrintDebug("Descriptor Count=%d, index=%d, desc_idx=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE, desc_idx);
127
128         if (desc_cnt < 3) {
129             PrintError("VirtioVNET cmd must include at least 3 descriptors (cnt=%d)\n", desc_cnt);
130             return -1;
131         }
132         
133         hdr_desc = &(q->desc[desc_idx]);
134
135         if (guest_pa_to_host_va(core, hdr_desc->addr_gpa, (addr_t *)&hdr) == -1) {
136             PrintError("Could not translate VirtioVNET header address\n");
137             return -1;
138         }
139
140         desc_idx = hdr_desc->next;
141         
142         if (hdr->cmd_type == VNET_ADD_ROUTE) {
143             
144             for (i = 0; i < hdr->num_cmds; i++) {
145                 uint8_t tmp_status = 0;
146                 struct v3_vnet_route * route = NULL;
147                 
148                 buf_desc = &(q->desc[desc_idx]);
149
150                 if (guest_pa_to_host_va(core, buf_desc->addr_gpa, (addr_t *)&(route)) == -1) {
151                     PrintError("Could not translate route address\n");
152                     return -1;
153                 }
154
155                 // add route
156                 PrintDebug("Adding VNET Route\n");
157
158                 tmp_status = v3_vnet_add_route(*route);
159
160                 PrintDebug("VNET Route Added\n");
161
162                 if (tmp_status != 0) {
163                     PrintError("Error adding VNET ROUTE\n");
164                     status = tmp_status;
165                 }
166
167                 xfer_len += buf_desc->length;
168                 desc_idx = buf_desc->next;
169             }
170
171         } else if (hdr->cmd_type == VNET_ADD_LINK) {
172
173
174         }
175
176
177
178         status_desc = &(q->desc[desc_idx]);
179
180         if (guest_pa_to_host_va(core, status_desc->addr_gpa, (addr_t *)&status_ptr) == -1) {
181             PrintError("VirtioVNET Error could not translate status address\n");
182             return -1;
183         }
184
185         xfer_len += status_desc->length;
186         *status_ptr = status;
187
188         PrintDebug("Transferred %d bytes (xfer_len)\n", xfer_len);
189         q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
190         q->used->ring[q->used->index % QUEUE_SIZE].length = xfer_len; // set to total inbound xfer length
191
192         q->used->index++;
193         q->cur_avail_idx++;
194     }
195
196
197     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
198         PrintDebug("Raising IRQ %d\n",  vnet_state->pci_dev->config_header.intr_line);
199         v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
200         vnet_state->virtio_cfg.pci_isr = 1;
201     }
202
203
204     return 0;
205 }
206
207
208 static int virtio_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * private_data) {
209     struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
210     int port_idx = port % vnet_state->io_range_size;
211
212
213     PrintDebug("VirtioVNET: VIRTIO VNET Write for port %d len=%d, value=%x\n", 
214                port, length, *(uint32_t *)src);
215     PrintDebug("VirtioVNET: port idx=%d\n", port_idx);
216
217
218     switch (port_idx) {
219         case GUEST_FEATURES_PORT:
220             if (length != 4) {
221                 PrintError("Illegal write length for guest features\n");
222                 return -1;
223             }
224             
225             vnet_state->virtio_cfg.guest_features = *(uint32_t *)src;
226
227             break;
228         case VRING_PG_NUM_PORT:
229             if (length == 4) {
230                 addr_t pfn = *(uint32_t *)src;
231                 addr_t page_addr = (pfn << VIRTIO_PAGE_SHIFT);
232
233                 vnet_state->cur_queue->pfn = pfn;
234                 
235                 vnet_state->cur_queue->ring_desc_addr = page_addr ;
236                 vnet_state->cur_queue->ring_avail_addr = page_addr + (QUEUE_SIZE * sizeof(struct vring_desc));
237                 vnet_state->cur_queue->ring_used_addr = ( vnet_state->cur_queue->ring_avail_addr + \
238                                                  sizeof(struct vring_avail)    + \
239                                                  (QUEUE_SIZE * sizeof(uint16_t)));
240                 
241                 // round up to next page boundary.
242                 vnet_state->cur_queue->ring_used_addr = (vnet_state->cur_queue->ring_used_addr + 0xfff) & ~0xfff;
243
244                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_desc_addr, (addr_t *)&(vnet_state->cur_queue->desc)) == -1) {
245                     PrintError("Could not translate ring descriptor address\n");
246                     return -1;
247                 }
248
249
250                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_avail_addr, (addr_t *)&(vnet_state->cur_queue->avail)) == -1) {
251                     PrintError("Could not translate ring available address\n");
252                     return -1;
253                 }
254
255
256                 if (guest_pa_to_host_va(core, vnet_state->cur_queue->ring_used_addr, (addr_t *)&(vnet_state->cur_queue->used)) == -1) {
257                     PrintError("Could not translate ring used address\n");
258                     return -1;
259                 }
260
261                 PrintDebug("VirtioVNET: RingDesc_addr=%p, Avail_addr=%p, Used_addr=%p\n",
262                            (void *)(vnet_state->cur_queue->ring_desc_addr),
263                            (void *)(vnet_state->cur_queue->ring_avail_addr),
264                            (void *)(vnet_state->cur_queue->ring_used_addr));
265
266                 PrintDebug("VirtioVNET: RingDesc=%p, Avail=%p, Used=%p\n", 
267                            vnet_state->cur_queue->desc, vnet_state->cur_queue->avail, vnet_state->cur_queue->used);
268
269             } else {
270                 PrintError("Illegal write length for page frame number\n");
271                 return -1;
272             }
273             break;
274         case VRING_Q_SEL_PORT:
275             vnet_state->virtio_cfg.vring_queue_selector = *(uint16_t *)src;
276
277             if (vnet_state->virtio_cfg.vring_queue_selector > NUM_QUEUES) {
278                 PrintError("Virtio Symbiotic device has no qeueues. Selected %d\n", 
279                            vnet_state->virtio_cfg.vring_queue_selector);
280                 return -1;
281             }
282             
283             vnet_state->cur_queue = &(vnet_state->queue[vnet_state->virtio_cfg.vring_queue_selector]);
284
285             break;
286         case VRING_Q_NOTIFY_PORT: {
287             uint16_t queue_idx = *(uint16_t *)src;
288
289             PrintDebug("VirtioVNET: Handling Kick\n");
290
291             if (queue_idx == 0) {
292                 if (handle_cmd_kick(core, vnet_state) == -1) {
293                     PrintError("Could not handle VNET Control command\n");
294                     return -1;
295                 }
296             } else if (queue_idx == 1) {
297
298                 // down queue
299             } else if (queue_idx == 2) {
300                 // up queue
301             } else {
302                 PrintError("Kick on invalid queue (%d)\n", queue_idx);
303                 return -1;
304             }
305
306             break;
307         }
308         case VIRTIO_STATUS_PORT:
309             vnet_state->virtio_cfg.status = *(uint8_t *)src;
310
311             if (vnet_state->virtio_cfg.status == 0) {
312                 PrintDebug("VirtioVNET: Resetting device\n");
313                 virtio_reset(vnet_state);
314             }
315
316             break;
317
318         case VIRTIO_ISR_PORT:
319             vnet_state->virtio_cfg.pci_isr = *(uint8_t *)src;
320             break;
321         default:
322             return -1;
323             break;
324     }
325
326     return length;
327 }
328
329
330 static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * private_data) {
331
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 /*
336     PrintDebug("VirtioVNET: VIRTIO SYMBIOTIC Read  for port %d (index =%d), length=%d\n", 
337                port, port_idx, length);
338 */
339     switch (port_idx) {
340         case HOST_FEATURES_PORT:
341             if (length != 4) {
342                 PrintError("Illegal read length for host features\n");
343                 return -1;
344             }
345
346             *(uint32_t *)dst = vnet_state->virtio_cfg.host_features;
347         
348             break;
349         case VRING_PG_NUM_PORT:
350             if (length != 4) {
351                 PrintError("Illegal read length for page frame number\n");
352                 return -1;
353             }
354
355             *(uint32_t *)dst = vnet_state->cur_queue->pfn;
356
357             break;
358         case VRING_SIZE_PORT:
359             if (length != 2) {
360                 PrintError("Illegal read length for vring size\n");
361                 return -1;
362             }
363                 
364             *(uint16_t *)dst = vnet_state->cur_queue->queue_size;
365
366             break;
367
368         case VIRTIO_STATUS_PORT:
369             if (length != 1) {
370                 PrintError("Illegal read length for status\n");
371                 return -1;
372             }
373
374             *(uint8_t *)dst = vnet_state->virtio_cfg.status;
375             break;
376
377         case VIRTIO_ISR_PORT:
378             *(uint8_t *)dst = vnet_state->virtio_cfg.pci_isr;
379             vnet_state->virtio_cfg.pci_isr = 0;
380             v3_pci_lower_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
381             break;
382
383         default:
384             if ( (port_idx >= sizeof(struct virtio_config)) && 
385                  (port_idx < (sizeof(struct virtio_config) + sizeof(struct vnet_config))) ) {
386                 int cfg_offset = port_idx - sizeof(struct virtio_config);
387                 uint8_t * cfg_ptr = (uint8_t *)&(vnet_state->vnet_cfg);
388
389                 memcpy(dst, cfg_ptr + cfg_offset, length);
390                 
391             } else {
392                 PrintError("Read of Unhandled Virtio Read\n");
393                 return -1;
394             }
395           
396             break;
397     }
398
399     return length;
400 }
401
402
403
404 static struct v3_device_ops dev_ops = {
405     .free = NULL,
406     .reset = NULL,
407     .start = NULL,
408     .stop = NULL,
409 };
410
411
412 static int vnet_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
413     struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
414     struct virtio_vnet_state * virtio_state = NULL;
415     struct pci_device * pci_dev = NULL;
416     char * name = v3_cfg_val(cfg, "name");
417
418     PrintDebug("VirtioVNET: Initializing VIRTIO VNET Control device\n");
419
420     if (pci_bus == NULL) {
421         PrintError("VirtIO devices require a PCI Bus");
422         return -1;
423     }
424     
425     virtio_state  = (struct virtio_vnet_state *)V3_Malloc(sizeof(struct virtio_vnet_state));
426     memset(virtio_state, 0, sizeof(struct virtio_vnet_state));
427
428     struct vm_device * dev = v3_allocate_device(name, &dev_ops, virtio_state);
429
430     if (v3_attach_device(vm, dev) == -1) {
431         PrintError("Could not attach device %s\n", name);
432         return -1;
433     }
434
435
436     // PCI initialization
437     {
438         struct v3_pci_bar bars[6];
439         int num_ports = sizeof(struct virtio_config) + sizeof(struct vnet_config);
440         int tmp_ports = num_ports;
441         int i;
442
443
444         // This gets the number of ports, rounded up to a power of 2
445         virtio_state->io_range_size = 1; // must be a power of 2
446
447         while (tmp_ports > 0) {
448             tmp_ports >>= 1;
449             virtio_state->io_range_size <<= 1;
450         }
451         
452         // this is to account for any low order bits being set in num_ports
453         // if there are none, then num_ports was already a power of 2 so we shift right to reset it
454         if ((num_ports & ((virtio_state->io_range_size >> 1) - 1)) == 0) {
455             virtio_state->io_range_size >>= 1;
456         }
457
458
459         for (i = 0; i < 6; i++) {
460             bars[i].type = PCI_BAR_NONE;
461         }
462
463         bars[0].type = PCI_BAR_IO;
464         bars[0].default_base_port = -1;
465         bars[0].num_ports = virtio_state->io_range_size;
466
467         bars[0].io_read = virtio_io_read;
468         bars[0].io_write = virtio_io_write;
469         bars[0].private_data = virtio_state;
470
471         pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
472                                          0, PCI_AUTO_DEV_NUM, 0,
473                                          "LNX_VIRTIO_VNET", bars,
474                                          NULL, NULL, NULL, virtio_state);
475
476         if (!pci_dev) {
477             PrintError("Could not register PCI Device\n");
478             return -1;
479         }
480         
481         pci_dev->config_header.vendor_id = VIRTIO_VENDOR_ID;
482         pci_dev->config_header.subsystem_vendor_id = VIRTIO_SUBVENDOR_ID;
483         
484
485         pci_dev->config_header.device_id = VIRTIO_VNET_DEV_ID;
486         pci_dev->config_header.class = PCI_CLASS_MEMORY;
487         pci_dev->config_header.subclass = PCI_MEM_SUBCLASS_RAM;
488     
489         pci_dev->config_header.subsystem_id = VIRTIO_VNET_SUBDEVICE_ID;
490
491
492         pci_dev->config_header.intr_pin = 1;
493
494         pci_dev->config_header.max_latency = 1; // ?? (qemu does it...)
495
496
497         virtio_state->pci_dev = pci_dev;
498         virtio_state->pci_bus = pci_bus;
499     }
500
501     virtio_reset(virtio_state);
502
503    
504     return 0;
505 }
506
507
508 device_register("LNX_VIRTIO_VNET", vnet_init)