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 host_pci passthrough PCI support
[palacios.git] / palacios / src / devices / host_pci.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) 2012, Jack Lange <jacklange@cs.pitt.edu>
11  * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jacklange@cs.pitt.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
21 /* This is the generic passthrough PCI virtual device */
22
23 /* 
24  * The basic idea is that we do not change the hardware PCI configuration
25  * Instead we modify the guest environment to map onto the physical configuration
26  * 
27  * The pci subsystem handles most of the configuration space, except for the bar registers.
28  * We handle them here, by either letting them go directly to hardware or remapping through virtual hooks
29  * 
30  * Memory Bars are always remapped via the shadow map, 
31  * IO Bars are selectively remapped through hooks if the guest changes them 
32  */
33
34 #include <palacios/vmm.h>
35 #include <palacios/vmm_dev_mgr.h>
36 #include <palacios/vmm_sprintf.h>
37 #include <palacios/vmm_lowlevel.h>
38 #include <palacios/vm_guest.h> // must include this to avoid dependency issue
39 #include <palacios/vmm_symspy.h>
40
41 #include <devices/pci.h>
42 #include <devices/pci_types.h>
43 #include <interfaces/host_pci.h>
44
45 #define PCI_BUS_MAX  7
46 #define PCI_DEV_MAX 32
47 #define PCI_FN_MAX   7
48
49 #define PCI_DEVICE 0x0
50 #define PCI_PCI_BRIDGE 0x1
51 #define PCI_CARDBUS_BRIDGE 0x2
52
53 #define PCI_HDR_SIZE 256
54
55
56
57
58 struct host_pci_state {
59     // This holds the description of the host PCI device configuration
60     struct v3_host_pci_dev * host_dev;
61
62
63     struct v3_host_pci_bar virt_bars[6];
64     struct v3_host_pci_bar virt_exp_rom;
65      
66     struct vm_device * pci_bus;
67     struct pci_device * pci_dev;
68
69     char name[32];
70 };
71
72
73
74 /*
75 static int pci_exp_rom_init(struct vm_device * dev, struct host_pci_state * state) {
76     struct pci_device * pci_dev = state->pci_dev;
77     struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
78
79
80     
81     PrintDebug("Adding 32 bit PCI mem region: start=%p, end=%p\n",
82                (void *)(addr_t)hrom->addr, 
83                (void *)(addr_t)(hrom->addr + hrom->size));
84
85     if (hrom->exp_rom_enabled) {
86         // only map shadow memory if the ROM is enabled 
87
88         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
89                           hrom->addr, 
90                           hrom->addr + hrom->size - 1,
91                           hrom->addr);
92
93         // Initially the virtual location matches the physical ones
94         memcpy(&(state->virt_exp_rom), hrom, sizeof(struct v3_host_pci_bar));
95
96
97         PrintDebug("phys exp_rom: addr=%p, size=%u\n", 
98                    (void *)(addr_t)hrom->addr, 
99                    hrom->size);
100
101
102         // Update the pci subsystem versions
103         pci_dev->config_header.expansion_rom_address = PCI_EXP_ROM_VAL(hrom->addr, hrom->exp_rom_enabled);
104     }
105
106
107
108     return 0;
109 }
110 */
111
112
113 static int pt_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
114     struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
115     int port_offset = port % pbar->size;
116
117     if (length == 1) {
118         *(uint8_t *)dst = v3_inb(pbar->addr + port_offset);
119     } else if (length == 2) {
120         *(uint16_t *)dst = v3_inw(pbar->addr + port_offset);
121     } else if (length == 4) {
122         *(uint32_t *)dst = v3_indw(pbar->addr + port_offset);
123     } else {
124         PrintError("Invalid PCI passthrough IO Redirection size read\n");
125         return -1;
126     }
127
128     return length;
129 }
130
131
132 static int pt_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
133     struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
134     int port_offset = port % pbar->size;
135     
136     if (length == 1) {
137         v3_outb(pbar->addr + port_offset, *(uint8_t *)src);
138     } else if (length == 2) {
139         v3_outw(pbar->addr + port_offset, *(uint16_t *)src);
140     } else if (length == 4) {
141         v3_outdw(pbar->addr + port_offset, *(uint32_t *)src);
142     } else {
143         PrintError("Invalid PCI passthrough IO Redirection size write\n");
144         return -1;
145     }
146     
147     return length;
148
149 }
150
151
152
153 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
154     struct vm_device * dev = (struct vm_device *)private_data;
155     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
156     struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
157     uint32_t bar_val = 0;
158
159     if (hbar->type == PT_BAR_IO) {
160         int i = 0;
161
162         bar_val = PCI_IO_BAR_VAL(hbar->addr);
163
164         for (i = 0; i < hbar->size; i++) {
165             v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL);
166         }
167     } else if (hbar->type == PT_BAR_MEM32) {
168         bar_val = PCI_MEM32_BAR_VAL(hbar->addr, hbar->prefetchable);
169
170         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
171                           hbar->addr, hbar->addr + hbar->size - 1,
172                           hbar->addr);
173         
174     } else if (hbar->type == PT_BAR_MEM24) {
175         bar_val = PCI_MEM24_BAR_VAL(hbar->addr, hbar->prefetchable);
176
177         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
178                           hbar->addr, hbar->addr + hbar->size - 1,
179                           hbar->addr);
180     } else if (hbar->type == PT_BAR_MEM64_LO) {
181         PrintError("Don't currently handle 64 bit bars...\n");
182     } else if (hbar->type == PT_BAR_MEM64_HI) {
183         PrintError("Don't currently handle 64 bit bars...\n");
184     }
185
186
187     memcpy(&(state->virt_bars[bar_num]), hbar, sizeof(struct v3_host_pci_bar));
188
189     *dst = bar_val;
190
191     return 0;
192 }
193
194
195
196 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
197     struct vm_device * dev = (struct vm_device *)private_data;
198     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
199     
200     struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
201     struct v3_host_pci_bar * vbar = &(state->virt_bars[bar_num]);
202
203
204
205     if (vbar->type == PT_BAR_NONE) {
206         return 0;
207     } else if (vbar->type == PT_BAR_IO) {
208         int i = 0;
209
210         // unhook old ports
211         for (i = 0; i < vbar->size; i++) {
212             if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
213                 PrintError("Could not unhook previously hooked port.... %d (0x%x)\n", 
214                            (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i);
215                 return -1;
216             }
217         }
218
219         // clear the low bits to match the size
220         vbar->addr = *src & ~(hbar->size - 1);
221
222         // udpate source version
223         *src = PCI_IO_BAR_VAL(vbar->addr);
224
225         PrintDebug("Rehooking passthrough IO ports starting at %d (0x%x)\n", 
226                    (uint32_t)vbar->addr, (uint32_t)vbar->addr);
227
228         if (vbar->addr == hbar->addr) {
229             // Map the io ports as passthrough
230             for (i = 0; i < hbar->size; i++) {
231                 v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL); 
232             }
233         } else {
234             // We have to manually handle the io redirection
235             for (i = 0; i < vbar->size; i++) {
236                 v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, hbar); 
237             }
238         }
239     } else if (vbar->type == PT_BAR_MEM32) {
240         // remove old mapping
241         struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
242
243         if (old_reg == NULL) {
244             // uh oh...
245             PrintError("Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr);
246             return -1;
247         }
248
249         v3_delete_mem_region(dev->vm, old_reg);
250
251         // clear the low bits to match the size
252         vbar->addr = *src & ~(hbar->size - 1);
253
254         // Set reserved bits
255         *src = PCI_MEM32_BAR_VAL(vbar->addr, hbar->prefetchable);
256
257         PrintDebug("Adding pci Passthrough remapping: start=0x%x, size=%d, end=0x%x (hpa=%p)\n", 
258                    (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
259
260         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
261                           vbar->addr, 
262                           vbar->addr + vbar->size - 1,
263                           hbar->addr);
264
265     } else if (vbar->type == PT_BAR_MEM64_LO) {
266         // We only store the written values here, the actual reconfig comes when the high BAR is updated
267
268         vbar->addr = *src & ~(hbar->size - 1);
269
270         *src = PCI_MEM64_LO_BAR_VAL(vbar->addr, hbar->prefetchable);
271
272
273     } else if (vbar->type == PT_BAR_MEM64_HI) {
274         struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
275         struct v3_mem_region * old_reg =  v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
276
277         if (old_reg == NULL) {
278             // uh oh...
279             PrintError("Could not find PCI Passthrough memory redirection region (addr=%p)\n", 
280                        (void *)(addr_t)vbar->addr);
281             return -1;
282         }
283
284         // remove old mapping
285         v3_delete_mem_region(dev->vm, old_reg);
286
287         vbar->addr = (((uint64_t)*src) << 32) + lo_vbar->addr;
288
289         // We don't set size, because we assume region is less than 4GB
290         // src does not change, because there are no reserved bits
291         
292
293         PrintDebug("Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n", 
294                    (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size, 
295                    (void *)(addr_t)(vbar->addr + vbar->size));
296
297         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, 
298                               vbar->addr + vbar->size - 1, hbar->addr) == -1) {
299
300             PrintDebug("Fail to insert shadow region (%p, %p)  -> %p\n",
301                        (void *)(addr_t)vbar->addr,
302                        (void *)(addr_t)(vbar->addr + vbar->size - 1),
303                        (void *)(addr_t)hbar->addr);
304             return -1;
305         }
306         
307     } else {
308         PrintError("Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
309         return -1;
310     }
311
312
313     return 0;
314 }
315
316
317 static int pt_config_write(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
318     struct vm_device * dev = (struct vm_device *)private_data;
319     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
320     
321 //    V3_Print("Writing host PCI config space update\n");
322
323     // We will mask all operations to the config header itself, 
324     // and only allow direct access to the device specific config space
325     if (reg_num < 64) {
326         return 0;
327     }
328
329     return v3_host_pci_config_write(state->host_dev, reg_num, src, length);
330 }
331
332
333
334 static int pt_config_read(struct pci_device * pci_dev, uint32_t reg_num, void * dst, uint_t length, void * private_data) {
335     struct vm_device * dev = (struct vm_device *)private_data;
336     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
337     
338   //  V3_Print("Reading host PCI config space update\n");
339
340     return v3_host_pci_config_read(state->host_dev, reg_num, dst, length);
341 }
342
343
344
345
346 /* This is really iffy....
347  * It was totally broken before, but it's _not_ totally fixed now
348  * The Expansion rom can be enabled/disabled via software using the low order bit
349  * We should probably handle that somehow here... 
350  */
351 static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) {
352     struct vm_device * dev = (struct vm_device *)(priv_data);
353     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
354     
355     struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
356     struct v3_host_pci_bar * vrom = &(state->virt_exp_rom);
357
358     PrintDebug("exp_rom update: src=0x%x\n", *src);
359     PrintDebug("vrom is size=%u, addr=0x%x\n", vrom->size, (uint32_t)vrom->addr);
360     PrintDebug("hrom is size=%u, addr=0x%x\n", hrom->size, (uint32_t)hrom->addr);
361
362     if (hrom->exp_rom_enabled) {
363         // only remove old mapping if present, I.E. if the rom was enabled previously 
364         if (vrom->exp_rom_enabled) {
365             struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr);
366           
367             if (old_reg == NULL) {
368                 // uh oh...
369                 PrintError("Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr);
370                 return -1;
371             }
372           
373             v3_delete_mem_region(dev->vm, old_reg);
374         }
375       
376       
377         vrom->addr = *src & ~(hrom->size - 1);
378       
379         // Set flags in actual register value
380         *src = PCI_EXP_ROM_VAL(vrom->addr, (*src & 0x00000001));
381       
382         PrintDebug("Cooked src=0x%x\n", *src);
383       
384   
385         PrintDebug("Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n", 
386                    (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
387       
388         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr, 
389                               vrom->addr + vrom->size - 1, hrom->addr) == -1) {
390             PrintError("Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n", 
391                        (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
392             return -1;
393         }
394     }
395     
396     return 0;
397 }
398
399
400 static int pt_cmd_update(struct pci_device * pci, pci_cmd_t cmd, uint64_t arg, void * priv_data) {
401     struct vm_device * dev = (struct vm_device *)(priv_data);
402     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
403
404     V3_Print("Host PCI Device: CMD update (%d)(arg=%llu)\n", cmd, arg);
405     
406     v3_host_pci_cmd_update(state->host_dev, cmd, arg);
407
408     return 0;
409 }
410
411
412 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) {
413     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
414     struct pci_device * pci_dev = NULL;
415     struct v3_pci_bar bars[6];
416     int bus_num = 0;
417     int i;
418
419     for (i = 0; i < 6; i++) {
420         bars[i].type = PCI_BAR_PASSTHROUGH;
421         bars[i].private_data = dev;
422         bars[i].bar_init = pci_bar_init;
423         bars[i].bar_write = pci_bar_write;
424     }
425
426     pci_dev = v3_pci_register_device(state->pci_bus,
427                                      PCI_STD_DEVICE,
428                                      bus_num, -1, 0, 
429                                      state->name, bars,
430                                      pt_config_write,
431                                      pt_config_read,
432                                      pt_cmd_update,
433                                      pt_exp_rom_write,               
434                                      dev);
435
436
437     state->pci_dev = pci_dev;
438
439     //    pci_exp_rom_init(dev, state);
440     pci_dev->config_header.expansion_rom_address = 0;
441     
442     v3_pci_enable_capability(pci_dev, PCI_CAP_MSI);
443 //    v3_pci_enable_capability(pci_dev, PCI_CAP_MSIX);
444     v3_pci_enable_capability(pci_dev, PCI_CAP_PCIE);
445     v3_pci_enable_capability(pci_dev, PCI_CAP_PM);
446
447
448
449     if (state->host_dev->iface == SYMBIOTIC) {
450 #ifdef V3_CONFIG_SYMBIOTIC
451         v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num);
452 #else
453         PrintError("ERROR Symbiotic Passthrough is not enabled\n");
454         return -1;
455 #endif
456     }
457
458     return 0;
459 }
460
461
462 static struct v3_device_ops dev_ops = {
463     .free = NULL,
464 };
465
466
467 static int irq_ack(struct guest_info * core, uint32_t irq, void * private_data) {
468     struct host_pci_state * state = (struct host_pci_state *)private_data;
469
470     
471     //    V3_Print("Acking IRQ %d\n", irq);
472     v3_host_pci_ack_irq(state->host_dev, irq);
473
474     return 0;
475 }
476
477
478 static int irq_handler(void * private_data, uint32_t vec_index) {
479     struct host_pci_state * state = (struct host_pci_state *)private_data;
480     struct v3_irq vec;
481
482     vec.irq = vec_index;
483     vec.ack = irq_ack;
484     vec.private_data = state;
485
486
487     //    V3_Print("Raising host PCI IRQ %d\n", vec_index);
488
489     if (state->pci_dev->irq_type == IRQ_NONE) {
490         return 0;
491     } else if (state->pci_dev->irq_type == IRQ_INTX) {
492         v3_pci_raise_acked_irq(state->pci_bus, state->pci_dev, vec);
493     } else {
494         v3_pci_raise_irq(state->pci_bus, state->pci_dev, vec_index);
495     }
496
497     return 0;
498 }
499
500
501 static int host_pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
502     struct host_pci_state * state = V3_Malloc(sizeof(struct host_pci_state));
503     struct vm_device * dev = NULL;
504     struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
505     char * dev_id = v3_cfg_val(cfg, "ID");    
506     char * url = v3_cfg_val(cfg, "url");
507
508     memset(state, 0, sizeof(struct host_pci_state));
509
510     if (!pci) {
511         PrintError("PCI bus not specified in config file\n");
512         return -1;
513     }
514     
515     state->pci_bus = pci;
516     strncpy(state->name, dev_id, 32);
517
518
519     dev = v3_add_device(vm, dev_id, &dev_ops, state);
520
521     if (dev == NULL) {
522         PrintError("Could not attach device %s\n", dev_id);
523         V3_Free(state);
524         return -1;
525     }
526
527     state->host_dev = v3_host_pci_get_dev(vm, url, state);
528
529     if (state->host_dev == NULL) {
530         PrintError("Could not connect to host pci device (%s)\n", url);
531         return -1;
532     }
533
534
535     state->host_dev->irq_handler = irq_handler;
536
537     if (setup_virt_pci_dev(vm, dev) == -1) {
538         PrintError("Could not setup virtual host PCI device\n");
539         return -1;
540     }
541
542     return 0;
543 }
544
545
546
547
548 device_register("HOST_PCI", host_pci_init)