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 symbiotic virtio device
[palacios.git] / palacios / src / devices / lnx_virtio_sym.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 SYM_MAGIC_PORT 20
29 #define SYM_SWAP_BASE_PORT 24
30 #define SYM_SWAP_SIZE_PORT 32
31
32
33 #define QUEUE_SIZE 128
34
35 struct sym_config {
36     uint32_t magic;
37     uint64_t swap_base;
38     uint32_t swap_size;
39 } __attribute__((packed));
40
41
42
43 struct virtio_sym_state {
44     struct sym_config sym_cfg;
45     struct virtio_config virtio_cfg;
46
47
48     struct vm_device * pci_bus;
49     struct pci_device * pci_dev;
50
51     struct virtio_queue queue[1];
52
53     struct virtio_queue * cur_queue;
54
55     int io_range_size;
56 };
57
58
59
60
61
62 static int virtio_reset(struct vm_device * dev) {
63     struct virtio_sym_state * virtio = (struct virtio_sym_state *)dev->private_data;
64
65     memset(virtio->queue, 0, sizeof(struct virtio_queue));
66
67     virtio->cur_queue = &(virtio->queue[0]);
68
69
70     virtio->virtio_cfg.status = 0;
71     virtio->virtio_cfg.pci_isr = 0;
72
73     virtio->queue[1].queue_size = QUEUE_SIZE;
74
75
76     memset(&(virtio->sym_cfg), 0, sizeof(struct sym_config));
77
78     return 0;
79 }
80
81
82 static int get_desc_count(struct virtio_queue * q, int index) {
83     struct vring_desc * tmp_desc = &(q->desc[index]);
84     int cnt = 1;
85     
86     while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
87         tmp_desc = &(q->desc[tmp_desc->next]);
88         cnt++;
89     }
90
91     return cnt;
92 }
93
94
95 static int handle_kick(struct vm_device * dev) {
96     struct virtio_sym_state * virtio = (struct virtio_sym_state *)dev->private_data;    
97     struct virtio_queue * q = virtio->cur_queue;
98
99     return -1;
100
101     PrintDebug("VIRTIO Symbiotic KICK: cur_index=%d (mod=%d), avail_index=%d\n", 
102                q->cur_avail_idx, q->cur_avail_idx % QUEUE_SIZE, q->avail->index);
103
104     while (q->cur_avail_idx < q->avail->index) {
105         struct vring_desc * tmp_desc = NULL;
106         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
107         int desc_cnt = get_desc_count(q, desc_idx);
108         int i = 0;
109         uint32_t req_len = 0;
110
111
112         PrintDebug("Descriptor Count=%d, index=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE);
113
114         for (i = 0; i < desc_cnt; i++) {
115             addr_t page_addr;
116             tmp_desc = &(q->desc[desc_idx]);
117             
118             PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", tmp_desc, 
119                        (void *)(tmp_desc->addr_gpa), tmp_desc->length, 
120                        tmp_desc->flags, tmp_desc->next);
121         
122
123             if (guest_pa_to_host_va(dev->vm, tmp_desc->addr_gpa, (addr_t *)&(page_addr)) == -1) {
124                 PrintError("Could not translate block header address\n");
125                 return -1;
126             }
127
128             /*      
129                if (handle_sym_op(dev, tmp_desc, buf_desc, status_desc) == -1) {
130                PrintError("Error handling symbiotic operation\n");
131                return -1;
132                }
133             */
134
135             PrintDebug("Symbiotic Device Currently Ignored\n");
136
137
138             req_len += tmp_desc->length;
139             desc_idx = tmp_desc->next;
140         }
141
142         q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
143         q->used->ring[q->used->index % QUEUE_SIZE].length = req_len; // What do we set this to????
144
145         q->used->index++;
146         q->cur_avail_idx++;
147     }
148
149     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
150         PrintDebug("Raising IRQ %d\n",  virtio->pci_dev->config_header.intr_line);
151         v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
152         virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
153     }
154
155     return 0;
156 }
157
158
159 static int virtio_io_write(uint16_t port, void * src, uint_t length, struct vm_device * dev) {
160     struct virtio_sym_state * virtio = (struct virtio_sym_state *)dev->private_data;
161     int port_idx = port % virtio->io_range_size;
162
163
164     PrintDebug("VIRTIO SYMBIOTIC Write for port %d (index=%d) len=%d, value=%x\n", 
165                port, port_idx,  length, *(uint32_t *)src);
166
167
168
169     switch (port_idx) {
170         case GUEST_FEATURES_PORT:
171             if (length != 4) {
172                 PrintError("Illegal write length for guest features\n");
173                 return -1;
174             }
175             
176             virtio->virtio_cfg.guest_features = *(uint32_t *)src;
177
178             break;
179         case VRING_PG_NUM_PORT:
180             if (length == 4) {
181                 addr_t pfn = *(uint32_t *)src;
182                 addr_t page_addr = (pfn << VIRTIO_PAGE_SHIFT);
183
184
185                 virtio->cur_queue->pfn = pfn;
186                 
187                 virtio->cur_queue->ring_desc_addr = page_addr ;
188                 virtio->cur_queue->ring_avail_addr = page_addr + (QUEUE_SIZE * sizeof(struct vring_desc));
189                 virtio->cur_queue->ring_used_addr = ( virtio->cur_queue->ring_avail_addr + \
190                                                  sizeof(struct vring_avail)    + \
191                                                  (QUEUE_SIZE * sizeof(uint16_t)));
192                 
193                 // round up to next page boundary.
194                 virtio->cur_queue->ring_used_addr = (virtio->cur_queue->ring_used_addr + 0xfff) & ~0xfff;
195
196                 if (guest_pa_to_host_va(dev->vm, virtio->cur_queue->ring_desc_addr, (addr_t *)&(virtio->cur_queue->desc)) == -1) {
197                     PrintError("Could not translate ring descriptor address\n");
198                     return -1;
199                 }
200
201
202                 if (guest_pa_to_host_va(dev->vm, virtio->cur_queue->ring_avail_addr, (addr_t *)&(virtio->cur_queue->avail)) == -1) {
203                     PrintError("Could not translate ring available address\n");
204                     return -1;
205                 }
206
207
208                 if (guest_pa_to_host_va(dev->vm, virtio->cur_queue->ring_used_addr, (addr_t *)&(virtio->cur_queue->used)) == -1) {
209                     PrintError("Could not translate ring used address\n");
210                     return -1;
211                 }
212
213                 PrintDebug("RingDesc_addr=%p, Avail_addr=%p, Used_addr=%p\n",
214                            (void *)(virtio->cur_queue->ring_desc_addr),
215                            (void *)(virtio->cur_queue->ring_avail_addr),
216                            (void *)(virtio->cur_queue->ring_used_addr));
217
218                 PrintDebug("RingDesc=%p, Avail=%p, Used=%p\n", 
219                            virtio->cur_queue->desc, virtio->cur_queue->avail, virtio->cur_queue->used);
220
221             } else {
222                 PrintError("Illegal write length for page frame number\n");
223                 return -1;
224             }
225             break;
226         case VRING_Q_SEL_PORT:
227             virtio->virtio_cfg.vring_queue_selector = *(uint16_t *)src;
228
229             if (virtio->virtio_cfg.vring_queue_selector > 0) {
230                 PrintError("Virtio Symbiotic device has not qeueues. Selected %d\n", 
231                            virtio->virtio_cfg.vring_queue_selector);
232                 return -1;
233             }
234             
235             virtio->cur_queue = &(virtio->queue[virtio->virtio_cfg.vring_queue_selector]);
236
237             break;
238         case VRING_Q_NOTIFY_PORT:
239             PrintDebug("Handling Kick\n");
240             if (handle_kick(dev) == -1) {
241                 PrintError("Could not handle Symbiotic Notification\n");
242                 return -1;
243             }
244             break;
245         case VIRTIO_STATUS_PORT:
246             virtio->virtio_cfg.status = *(uint8_t *)src;
247
248             if (virtio->virtio_cfg.status == 0) {
249                 PrintDebug("Resetting device\n");
250                 virtio_reset(dev);
251             }
252
253             break;
254
255         case VIRTIO_ISR_PORT:
256             virtio->virtio_cfg.pci_isr = *(uint8_t *)src;
257             break;
258         default:
259             return -1;
260             break;
261     }
262
263     return length;
264 }
265
266
267 static int virtio_io_read(uint16_t port, void * dst, uint_t length, struct vm_device * dev) {
268     struct virtio_sym_state * virtio = (struct virtio_sym_state *)dev->private_data;
269     int port_idx = port % virtio->io_range_size;
270
271
272     PrintDebug("VIRTIO SYMBIOTIC Read  for port %d (index =%d), length=%d\n", 
273                port, port_idx, length);
274
275     switch (port_idx) {
276         case HOST_FEATURES_PORT:
277             if (length != 4) {
278                 PrintError("Illegal read length for host features\n");
279                 return -1;
280             }
281
282             *(uint32_t *)dst = virtio->virtio_cfg.host_features;
283         
284             break;
285         case VRING_PG_NUM_PORT:
286             if (length != 4) {
287                 PrintError("Illegal read length for page frame number\n");
288                 return -1;
289             }
290
291             *(uint32_t *)dst = virtio->cur_queue->pfn;
292
293             break;
294         case VRING_SIZE_PORT:
295             if (length != 2) {
296                 PrintError("Illegal read length for vring size\n");
297                 return -1;
298             }
299                 
300             *(uint16_t *)dst = virtio->cur_queue->queue_size;
301
302             break;
303
304         case VIRTIO_STATUS_PORT:
305             if (length != 1) {
306                 PrintError("Illegal read length for status\n");
307                 return -1;
308             }
309
310             *(uint8_t *)dst = virtio->virtio_cfg.status;
311             break;
312
313         case VIRTIO_ISR_PORT:
314             *(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
315             virtio->virtio_cfg.pci_isr = 0;
316             v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
317             break;
318
319         default:
320             if ( (port_idx >= sizeof(struct virtio_config)) && 
321                  (port_idx < (sizeof(struct virtio_config) + sizeof(struct sym_config))) ) {
322                 int cfg_offset = port_idx - sizeof(struct virtio_config);
323                 uint8_t * cfg_ptr = (uint8_t *)&(virtio->sym_cfg);
324
325                 memcpy(dst, cfg_ptr + cfg_offset, length);
326                 
327             } else {
328                 PrintError("Read of Unhandled Virtio Read\n");
329                 return -1;
330             }
331           
332             break;
333     }
334
335     return length;
336 }
337
338
339
340    
341
342 static struct v3_device_ops dev_ops = {
343     .free = NULL,
344     .reset = NULL,
345     .start = NULL,
346     .stop = NULL,
347 };
348
349
350
351
352 static int virtio_init(struct guest_info * vm, void * cfg_data) {
353     struct vm_device * pci_bus = v3_find_dev(vm, (char *)cfg_data);
354     struct virtio_sym_state * virtio_state = NULL;
355     struct pci_device * pci_dev = NULL;
356
357     PrintDebug("Initializing VIRTIO Symbiotic device\n");
358
359     if (pci_bus == NULL) {
360         PrintError("VirtIO devices require a PCI Bus");
361         return -1;
362     }
363
364     
365     virtio_state  = (struct virtio_sym_state *)V3_Malloc(sizeof(struct virtio_sym_state));
366     memset(virtio_state, 0, sizeof(struct virtio_sym_state));
367
368
369     struct vm_device * dev = v3_allocate_device("LNX_VIRTIO_SYM", &dev_ops, virtio_state);
370     if (v3_attach_device(vm, dev) == -1) {
371         PrintError("Could not attach device %s\n", "LNX_VIRTIO_SYM");
372         return -1;
373     }
374
375
376     // PCI initialization
377     {
378         struct v3_pci_bar bars[6];
379         int num_ports = sizeof(struct virtio_config) + sizeof(struct sym_config);
380         int tmp_ports = num_ports;
381         int i;
382
383
384
385         // This gets the number of ports, rounded up to a power of 2
386         virtio_state->io_range_size = 1; // must be a power of 2
387
388         while (tmp_ports > 0) {
389             tmp_ports >>= 1;
390             virtio_state->io_range_size <<= 1;
391         }
392         
393         // this is to account for any low order bits being set in num_ports
394         // if there are none, then num_ports was already a power of 2 so we shift right to reset it
395         if ((num_ports & ((virtio_state->io_range_size >> 1) - 1)) == 0) {
396             virtio_state->io_range_size >>= 1;
397         }
398
399
400         for (i = 0; i < 6; i++) {
401             bars[i].type = PCI_BAR_NONE;
402         }
403
404         bars[0].type = PCI_BAR_IO;
405         bars[0].default_base_port = -1;
406         bars[0].num_ports = virtio_state->io_range_size;
407
408         bars[0].io_read = virtio_io_read;
409         bars[0].io_write = virtio_io_write;
410
411         pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
412                                          0, PCI_AUTO_DEV_NUM, 0,
413                                          "LNX_VIRTIO_SYM", bars,
414                                          NULL, NULL, NULL, dev);
415
416         if (!pci_dev) {
417             PrintError("Could not register PCI Device\n");
418             return -1;
419         }
420         
421         pci_dev->config_header.vendor_id = VIRTIO_VENDOR_ID;
422         pci_dev->config_header.subsystem_vendor_id = VIRTIO_SUBVENDOR_ID;
423         
424
425         pci_dev->config_header.device_id = VIRTIO_SYMBIOTIC_DEV_ID;
426         pci_dev->config_header.class = PCI_CLASS_MEMORY;
427         pci_dev->config_header.subclass = PCI_MEM_SUBCLASS_RAM;
428     
429         pci_dev->config_header.subsystem_id = VIRTIO_SYMBIOTIC_SUBDEVICE_ID;
430
431
432         pci_dev->config_header.intr_pin = 1;
433
434         pci_dev->config_header.max_latency = 1; // ?? (qemu does it...)
435
436
437         virtio_state->pci_dev = pci_dev;
438         virtio_state->pci_bus = pci_bus;
439     }
440
441     virtio_reset(dev);
442
443
444     return 0;
445 }
446
447
448 device_register("LNX_VIRTIO_SYM", virtio_init)