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.


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