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.


Cleanup and sanity-checking of use of strncpy/strcpy (Coverity static analysis)
[palacios.git] / palacios / src / devices / pci_passthrough.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
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
44 // Hardcoded... Are these standard??
45 #define PCI_CFG_ADDR    0xcf8
46 #define PCI_CFG_DATA    0xcfc
47
48 #define PCI_BUS_MAX  7
49 #define PCI_DEV_MAX 32
50 #define PCI_FN_MAX   7
51
52 #define PCI_DEVICE 0x0
53 #define PCI_PCI_BRIDGE 0x1
54 #define PCI_CARDBUS_BRIDGE 0x2
55
56 #define PCI_HDR_SIZE 256
57
58
59 union pci_addr_reg {
60     uint32_t value;
61     struct {
62         uint_t rsvd1   : 2;
63         uint_t reg     : 6;
64         uint_t func    : 3;
65         uint_t dev     : 5;
66         uint_t bus     : 8;
67         uint_t rsvd2   : 7;
68         uint_t enable  : 1;
69     } __attribute__((packed));
70 } __attribute__((packed));
71
72
73 typedef enum { PT_BAR_NONE,
74                PT_BAR_IO, 
75                PT_BAR_MEM32, 
76                PT_BAR_MEM24, 
77                PT_BAR_MEM64_LO, 
78                PT_BAR_MEM64_HI,
79                PT_EXP_ROM } pt_bar_type_t;
80
81 struct pt_bar {
82     uint32_t size;
83     pt_bar_type_t type;
84
85     /*  We store 64 bit memory bar addresses in the high BAR
86      *  because they are the last to be updated
87      *  This means that the addr field must be 64 bits
88      */
89     uint64_t addr; 
90
91     uint32_t val;
92 };
93
94
95
96
97 struct pt_dev_state {
98     union {
99         uint8_t config_space[256];
100         struct pci_config_header real_hdr;
101     } __attribute__((packed));
102
103     struct pt_bar phys_bars[6];
104     struct pt_bar virt_bars[6];
105
106     struct pt_bar phys_exp_rom;
107     struct pt_bar virt_exp_rom;
108      
109     struct vm_device * pci_bus;
110     struct pci_device * pci_dev;
111
112     union pci_addr_reg phys_pci_addr;
113
114     char name[32];
115 };
116
117
118 static inline uint32_t pci_cfg_read32(uint32_t addr) {
119     v3_outdw(PCI_CFG_ADDR, addr);
120     return v3_indw(PCI_CFG_DATA);
121 }
122
123
124
125 static inline void pci_cfg_write32(uint32_t addr, uint32_t val) {
126     v3_outdw(PCI_CFG_ADDR, addr);
127     v3_outdw(PCI_CFG_DATA, val);
128 }
129
130
131
132 static inline uint16_t pci_cfg_read16(uint32_t addr) {
133     v3_outw(PCI_CFG_ADDR, addr);
134     return v3_inw(PCI_CFG_DATA);
135 }
136
137
138
139 static inline void pci_cfg_write16(uint32_t addr, uint16_t val) {
140     v3_outw(PCI_CFG_ADDR, addr);
141     v3_outw(PCI_CFG_DATA, val);
142 }
143
144
145
146 static inline uint8_t pci_cfg_read8(uint32_t addr) {
147     v3_outb(PCI_CFG_ADDR, addr);
148     return v3_inb(PCI_CFG_DATA);
149 }
150
151
152
153 static inline void pci_cfg_write8(uint32_t addr, uint8_t val) {
154     v3_outb(PCI_CFG_ADDR, addr);
155     v3_outb(PCI_CFG_DATA, val);
156 }
157
158
159
160 static int pci_exp_rom_init(struct vm_device * dev, struct pt_dev_state * state) {
161     struct pci_device * pci_dev = state->pci_dev;
162     const uint32_t exp_rom_base_reg = 12;
163     union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
164     uint32_t max_val = 0;
165     uint32_t rom_val = 0;
166     struct pt_bar * prom = &(state->phys_exp_rom);
167     struct pt_bar * vrom = &(state->virt_exp_rom);
168
169     // should read from cached header
170     pci_addr.reg = exp_rom_base_reg;
171
172     rom_val = pci_cfg_read32(pci_addr.value);
173
174     prom->val = rom_val;
175     prom->type = PT_EXP_ROM;
176
177     max_val = rom_val | PCI_EXP_ROM_MASK;
178     
179     // Cycle the physical bar, to determine the actual size
180     // Disable irqs, to try to prevent accesses to the space via a interrupt handler
181     // This is not SMP safe!!
182     // What we probably want to do is write a 0 to the command register
183     //irq_state = v3_irq_save();
184     
185     pci_cfg_write32(pci_addr.value, max_val);
186     max_val = pci_cfg_read32(pci_addr.value);
187     pci_cfg_write32(pci_addr.value, rom_val);
188     
189     //v3_irq_restore(irq_state);
190     
191     prom->type = PT_EXP_ROM;
192     prom->addr = PCI_EXP_ROM_BASE(rom_val);
193     prom->size = ~PCI_EXP_ROM_BASE(max_val) + 1;
194     
195     PrintDebug(VM_NONE, VCORE_NONE, "Adding 32 bit PCI mem region: start=%p, end=%p\n",
196                (void *)(addr_t)prom->addr, 
197                (void *)(addr_t)(prom->addr + prom->size));
198
199     if ((prom->val & 0x1) == 0x1) {
200         // only map shadow memory if the ROM is enabled
201
202         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
203                           prom->addr, 
204                           prom->addr + prom->size - 1,
205                           prom->addr);
206     }
207
208     // Initially the virtual location matches the physical ones
209     memcpy(&(state->virt_exp_rom), &(state->phys_exp_rom), sizeof(struct pt_bar));
210
211     PrintDebug(VM_NONE, VCORE_NONE, "exp_rom_val=0x%x\n", rom_val);
212
213     PrintDebug(VM_NONE, VCORE_NONE, "phys exp_rom: addr=%p, size=%u\n", 
214                (void *)(addr_t)prom->addr, 
215                prom->size);
216
217     PrintDebug(VM_NONE, VCORE_NONE, "virt exp_rom: addr=%p, size=%u\n",
218                (void *)(addr_t)vrom->addr, 
219                vrom->size);
220
221     // Update the pci subsystem versions
222     pci_dev->config_header.expansion_rom_address = rom_val;
223
224     return 0;
225 }
226
227
228 // We initialize this 
229 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
230     struct vm_device * dev = (struct vm_device *)private_data;
231     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
232     const uint32_t bar_base_reg = 4;
233     union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
234     uint32_t bar_val = 0;
235     uint32_t max_val = 0;
236     //addr_t irq_state = 0;
237     struct pt_bar * pbar = &(state->phys_bars[bar_num]);
238
239     // should read from cached header
240     pci_addr.reg = bar_base_reg + bar_num;
241
242     PrintDebug(VM_NONE, VCORE_NONE, "PCI Address = 0x%x\n", pci_addr.value);
243
244     bar_val = pci_cfg_read32(pci_addr.value);
245     pbar->val = bar_val; 
246     
247     // We preset this type when we encounter a MEM64 Low BAR
248     if (pbar->type == PT_BAR_MEM64_HI) {
249         struct pt_bar * lo_pbar = &(state->phys_bars[bar_num - 1]);
250
251         max_val = PCI_MEM64_MASK_HI;
252
253         pci_cfg_write32(pci_addr.value, max_val);
254         max_val = pci_cfg_read32(pci_addr.value);
255         pci_cfg_write32(pci_addr.value, bar_val);
256
257         pbar->addr = PCI_MEM64_BASE_HI(bar_val);
258         pbar->addr <<= 32;
259         pbar->addr |= lo_pbar->addr;
260
261         // Executive Decision: We will not support devices with memory mapped regions over 4GB 
262         // The right way to do this would be to change 'size' to the order (power of 2) of the region
263         pbar->size += lo_pbar->size;
264
265         PrintDebug(VM_NONE, VCORE_NONE, "Adding 64 bit PCI mem region: start=0x%p, end=0x%p\n",
266                    (void *)(addr_t)pbar->addr, 
267                    (void *)(addr_t)(pbar->addr + pbar->size));
268         
269
270         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, pbar->addr, 
271                               pbar->addr + pbar->size - 1, pbar->addr) == -1) {
272
273             PrintError(VM_NONE, VCORE_NONE, "Fail to insert shadow region (0x%p, 0x%p)  -> 0x%p\n",
274                        (void *)(addr_t)pbar->addr,
275                        (void *)(addr_t)(pbar->addr + pbar->size - 1),
276                        (void *)(addr_t)pbar->addr);
277             return -1;
278         }
279
280     } else if ((bar_val & 0x3) == 0x1) {
281         int i = 0;
282
283         // IO bar
284         pbar->type = PT_BAR_IO;
285         pbar->addr = PCI_IO_BASE(bar_val);
286
287         max_val = bar_val | PCI_IO_MASK;
288
289         // Cycle the physical bar, to determine the actual size
290         // Disable irqs, to try to prevent accesses to the space via a interrupt handler
291         // This is not SMP safe!!
292         // What we probably want to do is write a 0 to the command register
293         //irq_state = v3_irq_save();
294         
295         pci_cfg_write32(pci_addr.value, max_val);
296         max_val = pci_cfg_read32(pci_addr.value);
297         pci_cfg_write32(pci_addr.value, bar_val);
298
299         //v3_irq_restore(irq_state);
300
301         V3_Print(VM_NONE, VCORE_NONE, "max_val = %x\n", max_val);
302
303         pbar->size = (uint16_t)~PCI_IO_BASE(max_val) + 1;
304
305         
306         V3_Print(VM_NONE, VCORE_NONE, "IO Bar with %d (%x) ports %x->%x\n", pbar->size, pbar->size, 
307                  (uint32_t)pbar->addr, (uint32_t)pbar->addr + pbar->size);
308         // setup a set of null io hooks
309         // This allows the guest to do passthrough IO to these ports
310         // While still reserving them in the IO map
311         for (i = 0; i < pbar->size; i++) {
312             v3_hook_io_port(dev->vm, pbar->addr + i, NULL, NULL, NULL); 
313         }
314
315     } else {
316
317         // might be memory, might be nothing    
318
319         max_val = bar_val | PCI_MEM_MASK;
320
321         // Cycle the physical bar, to determine the actual size
322         // Disable irqs, to try to prevent accesses to the space via a interrupt handler
323         // This is not SMP safe!!
324         // What we probably want to do is write a 0 to the command register
325         //irq_state = v3_irq_save();
326         
327         pci_cfg_write32(pci_addr.value, max_val);
328         max_val = pci_cfg_read32(pci_addr.value);
329         pci_cfg_write32(pci_addr.value, bar_val);
330
331         //v3_irq_restore(irq_state);
332
333         
334         if (max_val == 0) {
335             pbar->type = PT_BAR_NONE;
336         } else {
337
338             // if its a memory region, setup passthrough mem mapping
339
340             if ((bar_val & 0x6) == 0x0) {
341                 // MEM 32
342                 pbar->type = PT_BAR_MEM32;
343                 pbar->addr = PCI_MEM32_BASE(bar_val);
344                 pbar->size = ~PCI_MEM32_BASE(max_val) + 1;
345
346                 PrintDebug(VM_NONE, VCORE_NONE, "Adding 32 bit PCI mem region: start=%p, end=%p\n",
347                            (void *)(addr_t)pbar->addr, 
348                            (void *)(addr_t)(pbar->addr + pbar->size));
349
350                 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
351                                   pbar->addr, 
352                                   pbar->addr + pbar->size - 1,
353                                   pbar->addr);
354
355             } else if ((bar_val & 0x6) == 0x2) {
356                 // Mem 24
357                 pbar->type = PT_BAR_MEM24;
358                 pbar->addr = PCI_MEM24_BASE(bar_val);
359                 pbar->size = ~PCI_MEM24_BASE(max_val) + 1;
360
361                 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
362                                   pbar->addr, 
363                                   pbar->addr + pbar->size - 1,
364                                   pbar->addr);
365
366             } else if ((bar_val & 0x6) == 0x4) {
367                 struct pt_bar * hi_pbar = &(state->phys_bars[bar_num + 1]);
368
369                 pbar->type = PT_BAR_MEM64_LO;
370                 hi_pbar->type = PT_BAR_MEM64_HI;
371
372                 // Set the low bits, only for temporary storage until we calculate the high BAR
373                 pbar->addr = PCI_MEM64_BASE_LO(bar_val);
374                 pbar->size = ~PCI_MEM64_BASE_LO(max_val) + 1;
375
376             } else {
377                 PrintError(VM_NONE, VCORE_NONE, "Invalid Memory bar type\n");
378                 return -1;
379             }
380
381         }
382     }
383
384
385     // Initially the virtual bars match the physical ones
386     memcpy(&(state->virt_bars[bar_num]), &(state->phys_bars[bar_num]), sizeof(struct pt_bar));
387
388     PrintDebug(VM_NONE, VCORE_NONE, "bar_num=%d, bar_val=0x%x\n", bar_num, bar_val);
389
390     PrintDebug(VM_NONE, VCORE_NONE, "phys bar  type=%d, addr=%p, size=%d\n", 
391                pbar->type, (void *)(addr_t)pbar->addr, 
392                pbar->size);
393
394     PrintDebug(VM_NONE, VCORE_NONE, "virt bar  type=%d, addr=%p, size=%d\n",
395                state->virt_bars[bar_num].type, (void *)(addr_t)state->virt_bars[bar_num].addr, 
396                state->virt_bars[bar_num].size);
397
398     // Update the pci subsystem versions
399     *dst = bar_val;
400
401     return 0;
402 }
403
404 static int pt_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
405     struct pt_bar * pbar = (struct pt_bar *)priv_data;
406     int port_offset = port % pbar->size;
407
408     if (length == 1) {
409         *(uint8_t *)dst = v3_inb(pbar->addr + port_offset);
410     } else if (length == 2) {
411         *(uint16_t *)dst = v3_inw(pbar->addr + port_offset);
412     } else if (length == 4) {
413         *(uint32_t *)dst = v3_indw(pbar->addr + port_offset);
414     } else {
415         PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size read\n");
416         return -1;
417     }
418
419     return length;
420 }
421
422
423 static int pt_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
424     struct pt_bar * pbar = (struct pt_bar *)priv_data;
425     int port_offset = port % pbar->size;
426     
427     if (length == 1) {
428         v3_outb(pbar->addr + port_offset, *(uint8_t *)src);
429     } else if (length == 2) {
430         v3_outw(pbar->addr + port_offset, *(uint16_t *)src);
431     } else if (length == 4) {
432         v3_outdw(pbar->addr + port_offset, *(uint32_t *)src);
433     } else {
434         PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size write\n");
435         return -1;
436     }
437     
438     return length;
439
440 }
441
442
443
444
445
446 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
447     struct vm_device * dev = (struct vm_device *)private_data;
448     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
449     
450     struct pt_bar * pbar = &(state->phys_bars[bar_num]);
451     struct pt_bar * vbar = &(state->virt_bars[bar_num]);
452
453     PrintDebug(VM_NONE, VCORE_NONE, "Bar update: bar_num=%d, src=0x%x\n", bar_num, *src);
454     PrintDebug(VM_NONE, VCORE_NONE, "vbar is size=%u, type=%d, addr=%p, val=0x%x\n",
455                vbar->size, vbar->type, (void *)(addr_t)vbar->addr, vbar->val);
456     PrintDebug(VM_NONE, VCORE_NONE, "pbar is size=%u, type=%d, addr=%p, val=0x%x\n",
457                pbar->size, pbar->type, (void *)(addr_t)pbar->addr, pbar->val);
458
459
460
461     if (vbar->type == PT_BAR_NONE) {
462         return 0;
463     } else if (vbar->type == PT_BAR_IO) {
464         int i = 0;
465
466         // unhook old ports
467         for (i = 0; i < vbar->size; i++) {
468             if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
469                 PrintError(VM_NONE, VCORE_NONE, "Could not unhook previously hooked port.... %d (0x%x)\n", 
470                            (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i);
471                 return -1;
472             }
473         }
474
475         PrintDebug(VM_NONE, VCORE_NONE, "Setting IO Port range size=%d\n", pbar->size);
476
477         // clear the low bits to match the size
478         *src &= ~(pbar->size - 1);
479
480         // Set reserved bits
481         *src |= (pbar->val & ~PCI_IO_MASK);
482
483         vbar->addr = PCI_IO_BASE(*src); 
484
485         PrintDebug(VM_NONE, VCORE_NONE, "Cooked src=0x%x\n", *src);
486
487         PrintDebug(VM_NONE, VCORE_NONE, "Rehooking passthrough IO ports starting at %d (0x%x)\n", 
488                    (uint32_t)vbar->addr, (uint32_t)vbar->addr);
489
490         if (vbar->addr == pbar->addr) {
491             // Map the io ports as passthrough
492             for (i = 0; i < pbar->size; i++) {
493                 v3_hook_io_port(dev->vm, pbar->addr + i, NULL, NULL, NULL); 
494             }
495         } else {
496             // We have to manually handle the io redirection
497             for (i = 0; i < vbar->size; i++) {
498                 v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, pbar); 
499             }
500         }
501     } else if (vbar->type == PT_BAR_MEM32) {
502         // remove old mapping
503         struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
504
505         if (old_reg == NULL) {
506             // uh oh...
507             PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr);
508             return -1;
509         }
510
511         v3_delete_mem_region(dev->vm, old_reg);
512
513         // clear the low bits to match the size
514         *src &= ~(pbar->size - 1);
515
516         // Set reserved bits
517         *src |= (pbar->val & ~PCI_MEM_MASK);
518
519         PrintDebug(VM_NONE, VCORE_NONE, "Cooked src=0x%x\n", *src);
520
521         vbar->addr = PCI_MEM32_BASE(*src);
522
523         PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough remapping: start=0x%x, size=%d, end=0x%x\n", 
524                    (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size);
525
526         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
527                           vbar->addr, 
528                           vbar->addr + vbar->size - 1,
529                           pbar->addr);
530
531     } else if (vbar->type == PT_BAR_MEM64_LO) {
532         // We only store the written values here, the actual reconfig comes when the high BAR is updated
533
534         // clear the low bits to match the size
535         *src &= ~(pbar->size - 1);
536
537         // Set reserved bits
538         *src |= (pbar->val & ~PCI_MEM_MASK);
539
540         // Temp storage, used when hi bar is written
541         vbar->addr = PCI_MEM64_BASE_LO(*src);
542
543     } else if (vbar->type == PT_BAR_MEM64_HI) {
544         struct pt_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
545         struct v3_mem_region * old_reg =  v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
546
547         if (old_reg == NULL) {
548             // uh oh...
549             PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=%p)\n", 
550                        (void *)(addr_t)vbar->addr);
551             return -1;
552         }
553
554         // remove old mapping
555         v3_delete_mem_region(dev->vm, old_reg);
556
557         // We don't set size, because we assume region is less than 4GB
558
559         // Set reserved bits
560         *src |= (pbar->val & ~PCI_MEM64_MASK_HI);
561
562         vbar->addr = PCI_MEM64_BASE_HI(*src);
563         vbar->addr <<= 32;
564         vbar->addr += lo_vbar->addr;
565
566         PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n", 
567                    (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size, 
568                    (void *)(addr_t)(vbar->addr + vbar->size));
569
570         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, 
571                               vbar->addr + vbar->size - 1, pbar->addr) == -1) {
572
573             PrintDebug(VM_NONE, VCORE_NONE, "Fail to insert shadow region (%p, %p)  -> %p\n",
574                        (void *)(addr_t)vbar->addr,
575                        (void *)(addr_t)(vbar->addr + vbar->size - 1),
576                        (void *)(addr_t)pbar->addr);
577             return -1;
578         }
579         
580     } else {
581         PrintError(VM_NONE, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
582         return -1;
583     }
584
585     vbar->val = *src;
586     
587     return 0;
588 }
589
590
591 static int pt_config_update(struct pci_device * pci_dev, uint_t reg_num, void * src, uint_t length, void * private_data) {
592     struct vm_device * dev = (struct vm_device *)private_data;
593     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
594     union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
595
596     pci_addr.reg = reg_num >> 2;
597
598     if (length == 1) {
599         pci_cfg_write8(pci_addr.value, *(uint8_t *)src);
600     } else if (length == 2) {
601         pci_cfg_write16(pci_addr.value, *(uint16_t *)src);
602     } else if (length == 4) {
603         pci_cfg_write32(pci_addr.value, *(uint32_t *)src);      
604     }
605
606     return 0;
607 }
608
609
610 /* This is really iffy....
611  * It was totally broken before, but it's _not_ totally fixed now
612  * The Expansion rom can be enabled/disabled via software using the low order bit
613  * We should probably handle that somehow here... 
614  */
615 static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) {
616     struct vm_device * dev = (struct vm_device *)(priv_data);
617     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
618     
619     struct pt_bar * prom = &(state->phys_exp_rom);
620     struct pt_bar * vrom = &(state->virt_exp_rom);
621
622     PrintDebug(VM_NONE, VCORE_NONE, "exp_rom update: src=0x%x\n", *src);
623     PrintDebug(VM_NONE, VCORE_NONE, "vrom is size=%u, addr=0x%x, val=0x%x\n", vrom->size, (uint32_t)vrom->addr, vrom->val);
624     PrintDebug(VM_NONE, VCORE_NONE, "prom is size=%u, addr=0x%x, val=0x%x\n", prom->size, (uint32_t)prom->addr, prom->val);
625
626     // only remove old mapping if present, I.E. if the rom was enabled previously 
627     if ((vrom->val & 0x1) == 0x1) {
628         struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr);
629         
630         if (old_reg == NULL) {
631             // uh oh...
632             PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr);
633             return -1;
634         }
635     
636         v3_delete_mem_region(dev->vm, old_reg);
637     }
638     
639     // clear the low bits to match the size
640     *src &= ~(prom->size - 1);
641     
642     // Set reserved bits
643     *src |= (prom->val & ~PCI_EXP_ROM_MASK);
644     
645     PrintDebug(VM_NONE, VCORE_NONE, "Cooked src=0x%x\n", *src);
646
647     vrom->addr = PCI_EXP_ROM_BASE(*src);
648     
649
650     if ((prom->val & 0x1) == 0x1) {
651         PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n", 
652                    (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
653         
654         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr, 
655                               vrom->addr + vrom->size - 1, prom->addr) == -1) {
656             PrintError(VM_NONE, VCORE_NONE, "Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n", 
657                        (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
658             return -1;
659         }
660     }
661
662     vrom->val = *src;
663     
664     return 0;
665 }
666
667
668 static int find_real_pci_dev(uint16_t vendor_id, uint16_t device_id, struct pt_dev_state * state) {
669     union pci_addr_reg pci_addr = {0x80000000};
670     uint_t i, j, k, m;    
671
672     union {
673         uint32_t value;
674         struct {
675             uint16_t vendor;
676             uint16_t device;
677         } __attribute__((packed));
678     } __attribute__((packed)) pci_hdr = {0};
679
680     //PrintDebug(VM_NONE, VCORE_NONE, "Scanning PCI busses for vendor=%x, device=%x\n", vendor_id, device_id);
681     for (i = 0, pci_addr.bus = 0; i < PCI_BUS_MAX; i++, pci_addr.bus++) {
682         for (j = 0, pci_addr.dev = 0; j < PCI_DEV_MAX; j++, pci_addr.dev++) {
683             for (k = 0, pci_addr.func = 0; k < PCI_FN_MAX; k++, pci_addr.func++) {
684
685                 v3_outdw(PCI_CFG_ADDR, pci_addr.value);
686                 pci_hdr.value = v3_indw(PCI_CFG_DATA);
687
688                 //PrintDebug(VM_NONE, VCORE_NONE, "\bus=%d, tvendor=%x, device=%x\n", pci_addr.bus, pci_hdr.vendor, pci_hdr.device);
689
690                 if ((pci_hdr.vendor == vendor_id) && (pci_hdr.device == device_id)) {
691                     uint32_t * cfg_space = (uint32_t *)&state->real_hdr;
692     
693                     state->phys_pci_addr = pci_addr;
694
695                     // Copy the configuration space to the local cached version
696                     for (m = 0, pci_addr.reg = 0; m < PCI_HDR_SIZE; m += 4, pci_addr.reg++) {
697                         cfg_space[pci_addr.reg] = pci_cfg_read32(pci_addr.value);
698                     }
699
700
701                     PrintDebug(VM_NONE, VCORE_NONE, "Found device %x:%x (bus=%d, dev=%d, func=%d)\n", 
702                                vendor_id, device_id, 
703                                pci_addr.bus, pci_addr.dev, pci_addr.func);
704
705                     return 0;
706                 }
707             }
708         }
709     }
710
711     return -1;
712 }
713
714
715
716 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) {
717     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
718     struct pci_device * pci_dev = NULL;
719     struct v3_pci_bar bars[6];
720     int bus_num = 0;
721     int i;
722
723     for (i = 0; i < 6; i++) {
724         bars[i].type = PCI_BAR_PASSTHROUGH;
725         bars[i].private_data = dev;
726         bars[i].bar_init = pci_bar_init;
727         bars[i].bar_write = pci_bar_write;
728     }
729
730     pci_dev = v3_pci_register_device(state->pci_bus,
731                                      PCI_STD_DEVICE,
732                                      bus_num, -1, 0, 
733                                      state->name, bars,
734                                      pt_config_update,
735                                      NULL, 
736                                      NULL,
737                                      pt_exp_rom_write,               
738                                      dev);
739
740
741     // This will overwrite the bar registers.. but that should be ok.
742     memcpy(pci_dev->config_space, (uint8_t *)&(state->real_hdr), sizeof(struct pci_config_header));
743
744     state->pci_dev = pci_dev;
745
746     pci_exp_rom_init(dev, state);
747
748     v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num);
749
750
751     return 0;
752 }
753
754
755 static struct v3_device_ops dev_ops = {
756     .free = NULL,
757 };
758
759
760
761 static int irq_handler(struct v3_vm_info * vm, struct v3_interrupt * intr, void * private_data) {
762     struct vm_device * dev = (struct vm_device *)private_data;
763     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
764
765
766     v3_pci_raise_irq(state->pci_bus, state->pci_dev, 0);
767
768     V3_ACK_IRQ(intr->irq);
769
770     return 0;
771 }
772
773
774
775
776 static int passthrough_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
777     struct pt_dev_state * state = V3_Malloc(sizeof(struct pt_dev_state));
778     struct vm_device * dev = NULL;
779     struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
780     char * dev_id = v3_cfg_val(cfg, "ID");    
781
782     if (!state) {
783         PrintError(vm, VCORE_NONE, "Cannot allocate in init\n");
784         return -1;
785     }
786
787     memset(state, 0, sizeof(struct pt_dev_state));
788
789     if (!pci) {
790         PrintError(vm, VCORE_NONE, "Could not find PCI device\n");
791         return -1;
792     }
793     
794     state->pci_bus = pci;
795     strncpy(state->name, dev_id, 32);
796     state->name[31] = 0 ;
797
798
799     dev = v3_add_device(vm, dev_id, &dev_ops, state);
800
801     if (dev == NULL) {
802         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
803         V3_Free(state);
804         return -1;
805     }
806
807
808     if (find_real_pci_dev(atox(v3_cfg_val(cfg, "vendor_id")), 
809                           atox(v3_cfg_val(cfg, "device_id")), 
810                           state) == -1) {
811         PrintError(vm, VCORE_NONE, "Could not find PCI Device %s:%s\n", 
812                    v3_cfg_val(cfg, "vendor_id"), 
813                    v3_cfg_val(cfg, "device_id"));
814         v3_remove_device(dev);
815         return 0;
816     }
817
818     setup_virt_pci_dev(vm, dev);
819
820     v3_hook_irq(vm, atoi(v3_cfg_val(cfg, "irq")), irq_handler, dev);
821     //    v3_hook_irq(info, 64, irq_handler, dev);
822
823     return 0;
824 }
825
826
827
828
829 device_register("PCI_PASSTHROUGH", passthrough_init)