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.


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