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.


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