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.


b12d3a01290c7e2a3da25053a39200d3acd60049
[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(info->vm_info, info, "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(info->vm_info, info, "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(core->vm_info, core, "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(core->vm_info, core, "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         struct v3_host_pci_bar * hi_hbar = &(state->host_dev->bars[bar_num + 1]);
182         bar_val = PCI_MEM64_LO_BAR_VAL(hi_hbar->addr, hbar->prefetchable);
183     } else if (hbar->type == PT_BAR_MEM64_HI) {
184         bar_val = PCI_MEM64_HI_BAR_VAL(hbar->addr, hbar->prefetchable);
185
186         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
187                           hbar->addr, hbar->addr + hbar->size - 1,
188                           hbar->addr);  
189     }
190
191
192     memcpy(&(state->virt_bars[bar_num]), hbar, sizeof(struct v3_host_pci_bar));
193
194     *dst = bar_val;
195
196     return 0;
197 }
198
199
200
201 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
202     struct vm_device * dev = (struct vm_device *)private_data;
203     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
204     
205     struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
206     struct v3_host_pci_bar * vbar = &(state->virt_bars[bar_num]);
207
208
209
210     if (vbar->type == PT_BAR_NONE) {
211         return 0;
212     } else if (vbar->type == PT_BAR_IO) {
213         int i = 0;
214
215         // unhook old ports
216         for (i = 0; i < vbar->size; i++) {
217             if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
218                 PrintError(VM_NONE, VCORE_NONE, "Could not unhook previously hooked port.... %d (0x%x)\n", 
219                            (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i);
220                 return -1;
221             }
222         }
223
224         // clear the low bits to match the size
225         vbar->addr = *src & ~(hbar->size - 1);
226
227         // udpate source version
228         *src = PCI_IO_BAR_VAL(vbar->addr);
229
230         PrintDebug(VM_NONE, VCORE_NONE, "Rehooking passthrough IO ports starting at %d (0x%x)\n", 
231                    (uint32_t)vbar->addr, (uint32_t)vbar->addr);
232
233         if (vbar->addr == hbar->addr) {
234             // Map the io ports as passthrough
235             for (i = 0; i < hbar->size; i++) {
236                 v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL); 
237             }
238         } else {
239             // We have to manually handle the io redirection
240             for (i = 0; i < vbar->size; i++) {
241                 v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, hbar); 
242             }
243         }
244     } else if (vbar->type == PT_BAR_MEM32) {
245         // remove old mapping
246         struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
247
248         if (old_reg == NULL) {
249             // uh oh...
250             PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr);
251             return -1;
252         }
253
254         v3_delete_mem_region(dev->vm, old_reg);
255
256         // clear the low bits to match the size
257         vbar->addr = *src & ~(hbar->size - 1);
258
259         // Set reserved bits
260         *src = PCI_MEM32_BAR_VAL(vbar->addr, hbar->prefetchable);
261
262         PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough remapping: start=0x%x, size=%d, end=0x%x (hpa=%p)\n", 
263                    (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
264
265         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
266                           vbar->addr, 
267                           vbar->addr + vbar->size - 1,
268                           hbar->addr);
269
270     } else if (vbar->type == PT_BAR_MEM64_LO) {
271         // We only store the written values here, the actual reconfig comes when the high BAR is updated
272
273         vbar->addr = *src & ~(hbar->size - 1);
274
275         *src = PCI_MEM64_LO_BAR_VAL(vbar->addr, hbar->prefetchable);
276
277
278     } else if (vbar->type == PT_BAR_MEM64_HI) {
279         struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
280         struct v3_mem_region * old_reg =  v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
281
282         if (old_reg == NULL) {
283             // uh oh...
284             PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=%p)\n", 
285                        (void *)(addr_t)vbar->addr);
286             return -1;
287         }
288
289         // remove old mapping
290         v3_delete_mem_region(dev->vm, old_reg);
291
292         vbar->addr = (((uint64_t)*src) << 32) + lo_vbar->addr;
293
294         // We don't set size, because we assume region is less than 4GB
295         // src does not change, because there are no reserved bits
296         
297
298         PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n", 
299                    (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size, 
300                    (void *)(addr_t)(vbar->addr + vbar->size));
301
302         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, 
303                               vbar->addr + vbar->size - 1, hbar->addr) == -1) {
304
305             PrintDebug(VM_NONE, VCORE_NONE, "Fail to insert shadow region (%p, %p)  -> %p\n",
306                        (void *)(addr_t)vbar->addr,
307                        (void *)(addr_t)(vbar->addr + vbar->size - 1),
308                        (void *)(addr_t)hbar->addr);
309             return -1;
310         }
311         
312     } else {
313         PrintError(VM_NONE, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
314         return -1;
315     }
316
317
318     return 0;
319 }
320
321
322 static int pt_config_write(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
323     struct vm_device * dev = (struct vm_device *)private_data;
324     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
325     
326 //    V3_Print(VM_NONE, VCORE_NONE, "Writing host PCI config space update\n");
327
328     // We will mask all operations to the config header itself, 
329     // and only allow direct access to the device specific config space
330     if (reg_num < 64) {
331         return 0;
332     }
333
334     return v3_host_pci_config_write(state->host_dev, reg_num, src, length);
335 }
336
337
338
339 static int pt_config_read(struct pci_device * pci_dev, uint32_t reg_num, void * dst, uint_t length, void * private_data) {
340     struct vm_device * dev = (struct vm_device *)private_data;
341     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
342     
343   //  V3_Print(VM_NONE, VCORE_NONE, "Reading host PCI config space update\n");
344
345     return v3_host_pci_config_read(state->host_dev, reg_num, dst, length);
346 }
347
348
349
350
351 /* This is really iffy....
352  * It was totally broken before, but it's _not_ totally fixed now
353  * The Expansion rom can be enabled/disabled via software using the low order bit
354  * We should probably handle that somehow here... 
355  */
356 static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) {
357     struct vm_device * dev = (struct vm_device *)(priv_data);
358     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
359     
360     struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
361     struct v3_host_pci_bar * vrom = &(state->virt_exp_rom);
362
363     PrintDebug(VM_NONE, VCORE_NONE, "exp_rom update: src=0x%x\n", *src);
364     PrintDebug(VM_NONE, VCORE_NONE, "vrom is size=%u, addr=0x%x\n", vrom->size, (uint32_t)vrom->addr);
365     PrintDebug(VM_NONE, VCORE_NONE, "hrom is size=%u, addr=0x%x\n", hrom->size, (uint32_t)hrom->addr);
366
367     if (hrom->exp_rom_enabled) {
368         // only remove old mapping if present, I.E. if the rom was enabled previously 
369         if (vrom->exp_rom_enabled) {
370             struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr);
371           
372             if (old_reg == NULL) {
373                 // uh oh...
374                 PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr);
375                 return -1;
376             }
377           
378             v3_delete_mem_region(dev->vm, old_reg);
379         }
380       
381       
382         vrom->addr = *src & ~(hrom->size - 1);
383       
384         // Set flags in actual register value
385         *src = PCI_EXP_ROM_VAL(vrom->addr, (*src & 0x00000001));
386       
387         PrintDebug(VM_NONE, VCORE_NONE, "Cooked src=0x%x\n", *src);
388       
389   
390         PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n", 
391                    (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
392       
393         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr, 
394                               vrom->addr + vrom->size - 1, hrom->addr) == -1) {
395             PrintError(VM_NONE, VCORE_NONE, "Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n", 
396                        (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
397             return -1;
398         }
399     }
400     
401     return 0;
402 }
403
404
405 static int pt_cmd_update(struct pci_device * pci, pci_cmd_t cmd, uint64_t arg, void * priv_data) {
406     struct vm_device * dev = (struct vm_device *)(priv_data);
407     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
408
409     V3_Print(VM_NONE, VCORE_NONE, "Host PCI Device: CMD update (%d)(arg=%llu)\n", cmd, arg);
410     
411     v3_host_pci_cmd_update(state->host_dev, cmd, arg);
412
413     return 0;
414 }
415
416
417 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) {
418     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
419     struct pci_device * pci_dev = NULL;
420     struct v3_pci_bar bars[6];
421     int bus_num = 0;
422     int i;
423
424     for (i = 0; i < 6; i++) {
425         bars[i].type = PCI_BAR_PASSTHROUGH;
426         bars[i].private_data = dev;
427         bars[i].bar_init = pci_bar_init;
428         bars[i].bar_write = pci_bar_write;
429     }
430
431     pci_dev = v3_pci_register_device(state->pci_bus,
432                                      PCI_STD_DEVICE,
433                                      bus_num, -1, 0, 
434                                      state->name, bars,
435                                      pt_config_write,
436                                      pt_config_read,
437                                      pt_cmd_update,
438                                      pt_exp_rom_write,               
439                                      dev);
440
441
442     state->pci_dev = pci_dev;
443
444     //    pci_exp_rom_init(dev, state);
445     pci_dev->config_header.expansion_rom_address = 0;
446     
447     v3_pci_enable_capability(pci_dev, PCI_CAP_MSI);
448 //    v3_pci_enable_capability(pci_dev, PCI_CAP_MSIX);
449     v3_pci_enable_capability(pci_dev, PCI_CAP_PCIE);
450     v3_pci_enable_capability(pci_dev, PCI_CAP_PM);
451
452
453
454     if (state->host_dev->iface == SYMBIOTIC) {
455 #ifdef V3_CONFIG_SYMBIOTIC
456         v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num);
457 #else
458         PrintError(VM_NONE, VCORE_NONE, "ERROR Symbiotic Passthrough is not enabled\n");
459         return -1;
460 #endif
461     }
462
463     return 0;
464 }
465
466
467 static struct v3_device_ops dev_ops = {
468     .free = NULL,
469 };
470
471
472 static int irq_ack(struct guest_info * core, uint32_t irq, void * private_data) {
473     struct host_pci_state * state = (struct host_pci_state *)private_data;
474
475     
476     //    V3_Print(core->vm_info, core, "Acking IRQ %d\n", irq);
477     v3_host_pci_ack_irq(state->host_dev, irq);
478
479     return 0;
480 }
481
482
483 static int irq_handler(void * private_data, uint32_t vec_index) {
484     struct host_pci_state * state = (struct host_pci_state *)private_data;
485     struct v3_irq vec;
486
487     vec.irq = vec_index;
488     vec.ack = irq_ack;
489     vec.private_data = state;
490
491
492     //    V3_Print(VM_NONE, VCORE_NONE, "Raising host PCI IRQ %d\n", vec_index);
493
494     if (state->pci_dev->irq_type == IRQ_NONE) {
495         return 0;
496     } else if (state->pci_dev->irq_type == IRQ_INTX) {
497         v3_pci_raise_acked_irq(state->pci_bus, state->pci_dev, vec);
498     } else {
499         v3_pci_raise_irq(state->pci_bus, state->pci_dev, vec_index);
500     }
501
502     return 0;
503 }
504
505
506 static int host_pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
507     struct host_pci_state * state = V3_Malloc(sizeof(struct host_pci_state));
508     struct vm_device * dev = NULL;
509     struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
510     char * dev_id = v3_cfg_val(cfg, "ID");    
511     char * url = v3_cfg_val(cfg, "url");
512
513     memset(state, 0, sizeof(struct host_pci_state));
514
515     if (!pci) {
516         PrintError(vm, VCORE_NONE, "PCI bus not specified in config file\n");
517         return -1;
518     }
519     
520     state->pci_bus = pci;
521     strncpy(state->name, dev_id, 32);
522
523
524     dev = v3_add_device(vm, dev_id, &dev_ops, state);
525
526     if (dev == NULL) {
527         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
528         V3_Free(state);
529         return -1;
530     }
531
532     state->host_dev = v3_host_pci_get_dev(vm, url, state);
533
534     if (state->host_dev == NULL) {
535         PrintError(vm, VCORE_NONE, "Could not connect to host pci device (%s)\n", url);
536         return -1;
537     }
538
539
540     state->host_dev->irq_handler = irq_handler;
541
542     if (setup_virt_pci_dev(vm, dev) == -1) {
543         PrintError(vm, VCORE_NONE, "Could not setup virtual host PCI device\n");
544         return -1;
545     }
546
547     return 0;
548 }
549
550
551
552
553 device_register("HOST_PCI", host_pci_init)