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.


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