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.


Update host device framework to support PCI and other interrupt types
[palacios.git] / palacios / src / devices / pci_front.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) 2011, Peter Dinda <pdinda@northwestern.edu>
11  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
12  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Authors: 
16  *    Peter Dinda <pdinda@northwestern.edu>    (PCI front device forwarding to host dev interface)
17  *    Jack Lange <jarusl@cs.northwestern.edu>  (original PCI passthrough to physical hardware)
18  *
19  * This is free software.  You are permitted to use,
20  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
21  */
22
23
24 /* 
25   This is front-end PCI device intended to be used together with the
26   host device interface and a *virtual* PCI device implementation in
27   the host OS.  It makes it possible to project such a virtual device
28   into the guest as a PCI device.  It's based on the PCI passthrough
29   device, which projects *physical* PCI devices into the guest.
30
31   If you need to project a non-PCI host-based virtual or physical
32   device into the guest, you should use the generic device.
33
34 */
35
36 /* 
37  * The basic idea is that we do not change the hardware PCI configuration
38  * Instead we modify the guest environment to map onto the physical configuration
39  * 
40  * The pci subsystem handles most of the configuration space, except for the bar registers.
41  * We handle them here, by either letting them go directly to hardware or remapping through virtual hooks
42  * 
43  * Memory Bars are always remapped via the shadow map, 
44  * IO Bars are selectively remapped through hooks if the guest changes them 
45  */
46
47 #include <palacios/vmm.h>
48 #include <palacios/vmm_dev_mgr.h>
49 #include <palacios/vmm_sprintf.h>
50 #include <palacios/vmm_lowlevel.h>
51 #include <palacios/vm_guest.h> 
52 #include <palacios/vmm_symspy.h>
53
54 #include <devices/pci.h>
55 #include <devices/pci_types.h>
56
57 #include <interfaces/vmm_host_dev.h>
58
59
60 #ifndef V3_CONFIG_DEBUG_PCI_FRONT
61 #undef PrintDebug
62 #define PrintDebug(fmt, args...)
63 #endif
64
65
66 // Our own address in PCI-land
67 union pci_addr_reg {
68     uint32_t value;
69     struct {
70         uint_t rsvd1   : 2;
71         uint_t reg     : 6;
72         uint_t func    : 3;
73         uint_t dev     : 5;
74         uint_t bus     : 8;
75         uint_t rsvd2   : 7;
76         uint_t enable  : 1;
77     } __attribute__((packed));
78 } __attribute__((packed));
79
80
81 // identical to PCI passthrough device
82 typedef enum { PT_BAR_NONE,
83                PT_BAR_IO, 
84                PT_BAR_MEM32, 
85                PT_BAR_MEM24, 
86                PT_BAR_MEM64_LO, 
87                PT_BAR_MEM64_HI,
88                PT_EXP_ROM } pt_bar_type_t;
89
90 // identical to PCI passthrough device
91 struct pt_bar {
92     uint32_t size;
93     pt_bar_type_t type;
94
95     /*  We store 64 bit memory bar addresses in the high BAR
96      *  because they are the last to be updated
97      *  This means that the addr field must be 64 bits
98      */
99     uint64_t addr; 
100
101     uint32_t val;
102 };
103
104
105
106
107 struct pci_front_internal {
108     // this is our local cache of what the host device has
109     union {
110         uint8_t config_space[256];
111         struct pci_config_header real_hdr;
112     } __attribute__((packed));
113     
114     // We do need a representation of the bars
115     // since we need to be made aware when they are written
116     // so that we can change the hooks.
117     //
118     // We assume here that the PCI subsystem, on a bar write
119     // will first send us a config_update, which we forward to
120     // the host dev.   Then it will send us a bar update
121     // which we will use to rehook the device
122     //
123     struct pt_bar bars[6];      // our bars (for update purposes)
124     //
125     // Currently unsupported
126     //
127     //struct pt_bar exp_rom;      // and exp ram areas of the config space, above
128      
129     struct vm_device  *pci_bus;  // what bus we are attached to
130     struct pci_device *pci_dev;  // our representation as a registered PCI device
131
132     union pci_addr_reg pci_addr; // our pci address
133
134     char name[32];
135
136     v3_host_dev_t     host_dev;  // the actual implementation
137 };
138
139
140
141 /*
142 static int push_config(struct pci_front_internal *state, uint8_t *config)
143 {
144     if (v3_host_dev_config_write(state->host_dev, 0, config, 256) != 256) { 
145         return -1;
146     } else {
147         return 0;
148     }
149 }
150 */
151
152 static int pull_config(struct pci_front_internal *state, uint8_t *config)
153 {
154     if (v3_host_dev_read_config(state->host_dev, 0, config, 256) != 256) { 
155         return -1;
156     } else {
157         return 0;
158     }
159 }
160
161
162 static int pci_front_read_mem(struct guest_info * core, 
163                               addr_t              gpa,
164                               void              * dst,
165                               uint_t              len,
166                               void              * priv)
167 {
168     int i;
169     int rc;
170     struct vm_device *dev = (struct vm_device *) priv;
171     struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
172
173     PrintDebug(info->vm_info, info, "pci_front (%s): reading 0x%x bytes from gpa 0x%p from host dev 0x%p ...",
174                state->name, len, (void*)gpa, state->host_dev);
175
176     rc = v3_host_dev_read_mem(state->host_dev, gpa, dst, len);
177
178     PrintDebug(info->vm_info, info, " done ... read %d bytes: 0x", rc);
179
180     for (i = 0; i < rc; i++) { 
181         PrintDebug(info->vm_info, info, "%x", ((uint8_t *)dst)[i]);
182     }
183
184     PrintDebug(info->vm_info, info, "\n");
185
186     return rc;
187 }
188
189 static int pci_front_write_mem(struct guest_info * core, 
190                                addr_t              gpa,
191                                void              * src,
192                                uint_t              len,
193                                void              * priv)
194 {
195     int i;
196     int rc;
197     struct vm_device *dev = (struct vm_device *) priv;
198     struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
199
200     PrintDebug(info->vm_info, info, "pci_front (%s): writing 0x%x bytes to gpa 0x%p to host dev 0x%p bytes=0x",
201                state->name, len, (void*)gpa, state->host_dev);
202
203     for (i = 0; i < len; i++) { 
204         PrintDebug(info->vm_info, info, "%x", ((uint8_t *)src)[i]);
205     }
206
207     rc = v3_host_dev_write_mem(state->host_dev, gpa, src, len);
208
209     PrintDebug(info->vm_info, info, " %d bytes written\n",rc);
210     
211     return rc;
212 }
213
214
215 static int pci_front_read_port(struct guest_info * core, 
216                                uint16_t            port, 
217                                void              * dst, 
218                                uint_t              len, 
219                                void              * priv_data) 
220 {
221     int i;
222     struct pci_front_internal *state = (struct pci_front_internal *) priv_data;
223     
224     PrintDebug(info->vm_info, info, "pci_front (%s): reading 0x%x bytes from port 0x%x from host dev 0x%p ...",
225                state->name, len, port, state->host_dev);
226
227     int rc = v3_host_dev_read_io(state->host_dev, port, dst, len);
228     
229     PrintDebug(info->vm_info, info, " done ... read %d bytes: 0x", rc);
230
231     for (i = 0; i < rc; i++) { 
232         PrintDebug(info->vm_info, info, "%x", ((uint8_t *)dst)[i]);
233     }
234
235     PrintDebug(info->vm_info, info, "\n");
236
237     return rc;
238     
239 }
240
241 static int pci_front_write_port(struct guest_info * core, 
242                                 uint16_t            port, 
243                                 void              * src, 
244                                 uint_t              len, 
245                                 void              * priv_data) 
246 {
247     int i;
248     struct pci_front_internal *state = (struct pci_front_internal *) priv_data;
249     
250     PrintDebug(info->vm_info, info, "pci_front (%s): writing 0x%x bytes to port 0x%x to host dev 0x%p bytes=0x",
251                state->name, len, port, state->host_dev);
252
253     for (i = 0; i < len; i++) { 
254         PrintDebug(info->vm_info, info, "%x", ((uint8_t *)src)[i]);
255     }
256
257     int rc = v3_host_dev_write_io(state->host_dev, port, src, len);
258
259     PrintDebug(info->vm_info, info, " %d bytes written\n",rc);
260     
261     return rc;
262 }
263
264
265
266 //
267 // This is called at registration time for the device
268 // 
269 // We assume that someone has called pull_config to get a local
270 // copy of the config data from the host device by this point
271 //
272 // It might be smarter to do the pull config here since 
273 // in init we may not yet have the host device running... 
274 //
275 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
276     struct vm_device * dev = (struct vm_device *)private_data;
277     struct pci_front_internal * state = (struct pci_front_internal *)(dev->private_data);
278
279
280     const uint32_t bar_base_reg = 4;   // offset in 32bit words to skip to the first bar
281
282     union pci_addr_reg pci_addr = {state->pci_addr.value};  // my address
283
284     uint32_t bar_val = 0;
285     uint32_t max_val = 0;
286
287     struct pt_bar * pbar = &(state->bars[bar_num]);
288
289     pci_addr.reg = bar_base_reg + bar_num;
290
291     PrintDebug(info->vm_info, info, "pci_front (%s): pci_bar_init: PCI Address = 0x%x\n", state->name, pci_addr.value);
292
293     // This assumees that pull_config() has been previously called and 
294     // we have a local copy of the host device's configuration space
295     bar_val = *((uint32_t*)(&(state->config_space[(bar_base_reg+bar_num)*4])));
296
297     // Now let's set our copy of the relevant bar accordingly
298     pbar->val = bar_val; 
299     
300     // Now we will configure the hooks relevant to this bar
301
302     // We preset this type when we encounter a MEM64 Low BAR
303     // This is a 64 bit memory region that we turn into a memory hook
304     if (pbar->type == PT_BAR_MEM64_HI) {
305         struct pt_bar * lo_pbar = &(state->bars[bar_num - 1]);
306
307         max_val = PCI_MEM64_MASK_HI;
308
309         pbar->size += lo_pbar->size;
310
311         PrintDebug(info->vm_info, info, "pci_front (%s): pci_bar_init: Adding 64 bit PCI mem region: start=0x%p, end=0x%p as a full hook\n",
312                    state->name, 
313                    (void *)(addr_t)pbar->addr, 
314                    (void *)(addr_t)(pbar->addr + pbar->size));
315
316         if (v3_hook_full_mem(dev->vm,
317                              V3_MEM_CORE_ANY,
318                              pbar->addr,
319                              pbar->addr+pbar->size-1,
320                              pci_front_read_mem,
321                              pci_front_write_mem,
322                              dev)<0) { 
323             
324             PrintError(info->vm_info, info, "pci_front (%s): pci_bar_init: failed to hook 64 bit region (0x%p, 0x%p)\n",
325                        state->name, 
326                        (void *)(addr_t)pbar->addr,
327                        (void *)(addr_t)(pbar->addr + pbar->size - 1));
328             return -1;
329         }
330
331     } else if ((bar_val & 0x3) == 0x1) {
332         // This an I/O port region which we will turn into a range of hooks
333
334         int i = 0;
335
336         pbar->type = PT_BAR_IO;
337         pbar->addr = PCI_IO_BASE(bar_val);
338
339         max_val = bar_val | PCI_IO_MASK;
340
341         pbar->size = (uint16_t)~PCI_IO_BASE(max_val) + 1;
342
343         
344         PrintDebug(info->vm_info, info, "pci_front (%s): pci_bar_init: hooking ports 0x%x through 0x%x\n",
345                    state->name, (uint32_t)pbar->addr, (uint32_t)pbar->addr + pbar->size - 1);
346
347         for (i = 0; i < pbar->size; i++) {
348             if (v3_dev_hook_io(dev,
349                                pbar->addr + i, 
350                                pci_front_read_port,
351                                pci_front_write_port)<0) {
352                 PrintError(info->vm_info, info, "pci_front (%s): pci_bar_init: unabled to hook I/O port 0x%x\n",state->name, (unsigned)(pbar->addr+i));
353                 return -1;
354             }
355         }
356
357     } else {
358
359         // might be a 32 bit memory region or an empty bar
360
361         max_val = bar_val | PCI_MEM_MASK;
362
363         if (max_val == 0) {
364             // nothing, so just ignore it
365             pbar->type = PT_BAR_NONE;
366         } else {
367
368             // memory region - hook it
369
370             if ((bar_val & 0x6) == 0x0) {
371                 // 32 bit memory region
372
373                 pbar->type = PT_BAR_MEM32;
374                 pbar->addr = PCI_MEM32_BASE(bar_val);
375                 pbar->size = ~PCI_MEM32_BASE(max_val) + 1;
376
377                 PrintDebug(info->vm_info, info, "pci_front (%s): pci_init_bar: adding 32 bit PCI mem region: start=0x%p, end=0x%p\n",
378                            state->name, 
379                            (void *)(addr_t)pbar->addr, 
380                            (void *)(addr_t)(pbar->addr + pbar->size));
381
382                 if (v3_hook_full_mem(dev->vm, 
383                                      V3_MEM_CORE_ANY,
384                                      pbar->addr,
385                                      pbar->addr+pbar->size-1,
386                                      pci_front_read_mem,
387                                      pci_front_write_mem,
388                                      dev) < 0 ) { 
389                     PrintError(info->vm_info, info, "pci_front (%s): pci_init_bar: unable to hook 32 bit memory region 0x%p to 0x%p\n",
390                                state->name, (void*)(pbar->addr), (void*)(pbar->addr+pbar->size-1));
391                     return -1;
392                 }
393
394             } else if ((bar_val & 0x6) == 0x2) {
395
396                 // 24 bit memory region
397
398                 pbar->type = PT_BAR_MEM24;
399                 pbar->addr = PCI_MEM24_BASE(bar_val);
400                 pbar->size = ~PCI_MEM24_BASE(max_val) + 1;
401
402
403                 if (v3_hook_full_mem(dev->vm, 
404                                      V3_MEM_CORE_ANY,
405                                      pbar->addr,
406                                      pbar->addr+pbar->size-1,
407                                      pci_front_read_mem,
408                                      pci_front_write_mem,
409                                      dev) < 0 ) { 
410                     PrintError(info->vm_info, info, "pci_front (%s): pci_init_bar: unable to hook 24 bit memory region 0x%p to 0x%p\n",
411                                state->name, (void*)(pbar->addr), (void*)(pbar->addr+pbar->size-1));
412                     return -1;
413                 }
414
415             } else if ((bar_val & 0x6) == 0x4) {
416                 
417                 // partial update of a 64 bit region, no hook done yet
418
419                 struct pt_bar * hi_pbar = &(state->bars[bar_num + 1]);
420
421                 pbar->type = PT_BAR_MEM64_LO;
422                 hi_pbar->type = PT_BAR_MEM64_HI;
423
424                 // Set the low bits, only for temporary storage until we calculate the high BAR
425                 pbar->addr = PCI_MEM64_BASE_LO(bar_val);
426                 pbar->size = ~PCI_MEM64_BASE_LO(max_val) + 1;
427
428                 PrintDebug(info->vm_info, info, "pci_front (%s): pci_bar_init: partial 64 bit update\n",state->name);
429
430             } else {
431                 PrintError(info->vm_info, info, "pci_front (%s): pci_bar_init: invalid memory bar type\n",state->name);
432                 return -1;
433             }
434
435         }
436     }
437
438
439
440     // Update the pci subsystem versions
441     *dst = bar_val;
442
443     return 0;
444 }
445
446
447 //
448 // If the guest modifies a BAR, we expect that pci.c will do the following,
449 // in this order
450 //
451 //    1. notify us via the config_update callback, which we will feed back
452 //       to the host device
453 //    2. notify us of the bar change via the following callback 
454 //
455 // This callback will unhook as needed for the old bar value and rehook
456 // as needed for the new bar value
457 //
458 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
459     struct vm_device * dev = (struct vm_device *)private_data;
460     struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
461     
462     struct pt_bar * pbar = &(state->bars[bar_num]);
463
464     PrintDebug(info->vm_info, info, "pci_front (%s): bar update: bar_num=%d, src=0x%x\n", state->name, bar_num, *src);
465     PrintDebug(info->vm_info, info, "pci_front (%s): the current bar has size=%u, type=%d, addr=%p, val=0x%x\n",
466                state->name, pbar->size, pbar->type, (void *)(addr_t)pbar->addr, pbar->val);
467
468
469
470     if (pbar->type == PT_BAR_NONE) {
471         PrintDebug(info->vm_info, info, "pci_front (%s): bar update is to empty bar - ignored\n",state->name);
472         return 0;
473     } else if (pbar->type == PT_BAR_IO) {
474         int i = 0;
475
476         // unhook old ports
477         PrintDebug(info->vm_info, info, "pci_front (%s): unhooking I/O ports 0x%x through 0x%x\n", 
478                    state->name, 
479                    (unsigned)(pbar->addr), (unsigned)(pbar->addr+pbar->size-1));
480         for (i = 0; i < pbar->size; i++) {
481             if (v3_dev_unhook_io(dev, pbar->addr + i) == -1) {
482                 PrintError(info->vm_info, info, "pci_front (%s): could not unhook previously hooked port.... 0x%x\n", 
483                            state->name, 
484                            (uint32_t)pbar->addr + i);
485                 return -1;
486             }
487         }
488
489         PrintDebug(info->vm_info, info, "pci_front (%s): setting I/O Port range size=%d\n", state->name, pbar->size);
490
491         // 
492         // Not clear if this cooking is needed... why not trust
493         // the write?  Who cares if it wants to suddenly hook more ports?
494         // 
495
496         // clear the low bits to match the size
497         *src &= ~(pbar->size - 1);
498
499         // Set reserved bits
500         *src |= (pbar->val & ~PCI_IO_MASK);
501
502         pbar->addr = PCI_IO_BASE(*src); 
503
504         PrintDebug(info->vm_info, info, "pci_front (%s): cooked src=0x%x\n", state->name, *src);
505
506         PrintDebug(info->vm_info, info, "pci_front (%s): rehooking I/O ports 0x%x through 0x%x\n",
507                    state->name, (unsigned)(pbar->addr), (unsigned)(pbar->addr+pbar->size-1));
508
509         for (i = 0; i < pbar->size; i++) {
510             if (v3_dev_hook_io(dev,
511                                pbar->addr + i, 
512                                pci_front_read_port, 
513                                pci_front_write_port)<0) { 
514                 PrintError(info->vm_info, info, "pci_front (%s): unable to rehook port 0x%x\n",state->name, (unsigned)(pbar->addr+i));
515                 return -1;
516             }
517         }
518
519     } else if (pbar->type == PT_BAR_MEM32) {
520
521         if (v3_unhook_mem(dev->vm,V3_MEM_CORE_ANY,pbar->addr)<0) { 
522             PrintError(info->vm_info, info, "pci_front (%s): unable to unhook 32 bit memory region starting at 0x%p\n", 
523                        state->name, (void*)(pbar->addr));
524             return -1;
525         }
526
527         // Again, not sure I need to do this cooking...
528
529         // clear the low bits to match the size
530         *src &= ~(pbar->size - 1);
531
532         // Set reserved bits
533         *src |= (pbar->val & ~PCI_MEM_MASK);
534
535         PrintDebug(info->vm_info, info, "pci_front (%s): cooked src=0x%x\n", state->name, *src);
536
537         pbar->addr = PCI_MEM32_BASE(*src);
538
539         PrintDebug(info->vm_info, info, "pci_front (%s): rehooking 32 bit memory region 0x%p through 0x%p\n",
540                    state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
541                    
542         if (v3_hook_full_mem(dev->vm,
543                              V3_MEM_CORE_ANY,
544                              pbar->addr,
545                              pbar->addr+pbar->size-1,
546                              pci_front_read_mem,
547                              pci_front_write_mem,
548                              dev)<0) { 
549             PrintError(info->vm_info, info, "pci_front (%s): unable to rehook 32 bit memory region 0x%p through 0x%p\n",
550                        state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
551             return -1;
552         }
553
554     } else if (pbar->type == PT_BAR_MEM64_LO) {
555         // We only store the written values here, the actual reconfig comes when the high BAR is updated
556
557         // clear the low bits to match the size
558         *src &= ~(pbar->size - 1);
559
560         // Set reserved bits
561         *src |= (pbar->val & ~PCI_MEM_MASK);
562
563         // Temp storage, used when hi bar is written
564         pbar->addr = PCI_MEM64_BASE_LO(*src);
565
566         PrintDebug(info->vm_info, info, "pci_front (%s): handled partial update for 64 bit memory region\n",state->name);
567
568     } else if (pbar->type == PT_BAR_MEM64_HI) {
569         struct pt_bar * lo_vbar = &(state->bars[bar_num - 1]);
570
571         if (v3_unhook_mem(dev->vm,V3_MEM_CORE_ANY,pbar->addr)<0) { 
572             PrintError(info->vm_info, info, "pci_front (%s): unable to unhook 64 bit memory region starting at 0x%p\n", 
573                        state->name, (void*)(pbar->addr));
574             return -1;
575         }
576
577         
578         // We don't set size, because we assume region is less than 4GB
579
580         // Set reserved bits
581         *src |= (pbar->val & ~PCI_MEM64_MASK_HI);
582
583         pbar->addr = PCI_MEM64_BASE_HI(*src);
584         pbar->addr <<= 32;
585         pbar->addr += lo_vbar->addr;
586
587         PrintDebug(info->vm_info, info, "pci_front (%s): rehooking 64 bit memory region 0x%p through 0x%p\n",
588                    state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
589                    
590         if (v3_hook_full_mem(dev->vm,
591                              V3_MEM_CORE_ANY,
592                              pbar->addr,
593                              pbar->addr+pbar->size-1,
594                              pci_front_read_mem,
595                              pci_front_write_mem,
596                              dev)<0) { 
597             PrintError(info->vm_info, info, "pci_front (%s): unable to rehook 64 bit memory region 0x%p through 0x%p\n",
598                        state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
599             return -1;
600         }
601         
602     } else {
603         PrintError(info->vm_info, info, "pci_front (%s): unhandled PCI bar type %d\n", state->name, pbar->type);
604         return -1;
605     }
606
607     pbar->val = *src;
608     
609     return 0;
610 }
611
612
613 static int pci_front_config_update(struct pci_device *pci_dev, uint_t reg_num, void * src, uint_t length, void * private_data) 
614 {
615     int i;
616     struct vm_device * dev = (struct vm_device *)private_data;
617     struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
618     union pci_addr_reg pci_addr = {state->pci_addr.value};
619     
620     pci_addr.reg = reg_num >> 2;
621
622     PrintDebug(info->vm_info, info, "pci_front (%s): configuration update: writing 0x%x bytes at offset 0x%x to host device 0x%p, bytes=0x",
623                state->name, length, pci_addr.value, state->host_dev);
624     
625     for (i = 0; i < length; i++) { 
626         PrintDebug(info->vm_info, info, "%x", ((uint8_t *)src)[i]);
627     }
628
629     PrintDebug(info->vm_info, info, "\n");
630
631     if (v3_host_dev_write_config(state->host_dev,
632                                  pci_addr.value,
633                                  src,
634                                  length) != length) { 
635         PrintError(info->vm_info, info, "pci_front (%s): configuration update: unable to write all bytes\n",state->name);
636         return -1;
637     }
638
639
640     return 0;
641 }
642
643
644 static int unhook_all_mem(struct pci_front_internal *state)
645 {
646     int bar_num;
647     struct vm_device *bus = state->pci_bus;
648
649
650     for (bar_num=0;bar_num<6;bar_num++) { 
651         struct pt_bar * pbar = &(state->bars[bar_num]);
652
653         PrintDebug(info->vm_info, info, "pci_front (%s): unhooking for bar %d\n", state->name, bar_num);
654
655         if (pbar->type == PT_BAR_MEM32) {
656             if (v3_unhook_mem(bus->vm,V3_MEM_CORE_ANY,pbar->addr)<0) { 
657                 PrintError(info->vm_info, info, "pci_front (%s): unable to unhook 32 bit memory region starting at 0x%p\n", 
658                            state->name, (void*)(pbar->addr));
659                 return -1;
660             }
661         } else  if (pbar->type == PT_BAR_MEM64_HI) {
662
663             if (v3_unhook_mem(bus->vm,V3_MEM_CORE_ANY,pbar->addr)<0) { 
664                 PrintError(info->vm_info, info, "pci_front (%s): unable to unhook 64 bit memory region starting at 0x%p\n", 
665                            state->name, (void*)(pbar->addr));
666                 return -1;
667             }
668         }
669     }
670     
671     return 0;
672 }
673
674
675
676 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) 
677 {
678     struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
679     struct pci_device * pci_dev = NULL;
680     struct v3_pci_bar bars[6];
681     int bus_num = 0;
682     int i;
683
684     for (i = 0; i < 6; i++) {
685         bars[i].type = PCI_BAR_PASSTHROUGH;
686         bars[i].private_data = dev;
687         bars[i].bar_init = pci_bar_init;
688         bars[i].bar_write = pci_bar_write;
689     }
690
691     pci_dev = v3_pci_register_device(state->pci_bus,
692                                      PCI_STD_DEVICE,
693                                      bus_num, -1, 0, 
694                                      state->name, bars,
695                                      pci_front_config_update,
696                                      NULL,      // no suport for config reads
697                                      NULL,      // no support for command updates
698                                      NULL,      // no support for expansion roms              
699                                      dev);
700
701
702     state->pci_dev = pci_dev;
703
704
705     // EXPANSION ROMS CURRENTLY UNSUPPORTED
706
707     // COMMANDS CURRENTLY UNSUPPORTED
708
709     return 0;
710 }
711
712
713
714 //
715 // Note: potential bug:  not clear what pointer I get here
716 //
717 static int pci_front_free(struct pci_front_internal *state)
718 {
719
720     if (unhook_all_mem(state)<0) { 
721         return -1;
722     }
723
724     // the device manager will unhook the i/o ports for us
725
726     if (state->host_dev) { 
727         v3_host_dev_close(state->host_dev);
728         state->host_dev=0;
729     }
730
731
732     V3_Free(state);
733
734     PrintDebug(info->vm_info, info, "pci_front (%s): freed\n",state->name);
735
736     return 0;
737 }
738
739 #ifdef V3_CONFIG_HOST_DEVICE
740 static void pci_front_intr_update_callback(v3_host_dev_t hdev, v3_guest_dev_t gdev, uint8_t irq, int raise)
741 {
742     if (gdev) { 
743
744         struct vm_device *dev = (struct vm_device *) gdev;
745         struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
746
747         // We expect the host device will raise and lower irqs as needed, so we
748         // don't need an "acked" irq.  Also, we expect the host is using INTX, not
749         // MSI.  It's doubtful that MSI will work.  
750         // expect: state->pci_dev->irq_type==IRQ_INTX
751         if (raise) { 
752             v3_pci_raise_irq(state->pci_bus, state->pci_dev, irq);
753         } else {
754             v3_pci_lower_irq(state->pci_bus, state->pci_dev, irq);
755         }
756     }
757 }
758 #endif
759
760
761 static struct v3_device_ops dev_ops = {
762 //
763 // Note: potential bug:  not clear what pointer I get here
764 //
765     .free = (int (*)(void*))pci_front_free,
766 };
767
768
769
770
771
772
773
774 static int pci_front_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) 
775 {
776     struct vm_device * dev;
777     struct vm_device * bus;
778     struct pci_front_internal *state;
779     char *dev_id;
780     char *bus_id;
781     char *url;
782
783     
784     if (!(dev_id = v3_cfg_val(cfg, "ID"))) { 
785         PrintError(info->vm_info, info, "pci_front: no id  given!\n");
786         return -1;
787     }
788     
789     if (!(bus_id = v3_cfg_val(cfg, "bus"))) { 
790         PrintError(info->vm_info, info, "pci_front (%s): no bus given!\n",dev_id);
791         return -1;
792     }
793     
794     if (!(url = v3_cfg_val(cfg, "hostdev"))) { 
795         PrintError(info->vm_info, info, "pci_front (%s): no host device url given!\n",dev_id);
796         return -1;
797     }
798     
799     if (!(bus = v3_find_dev(vm,bus_id))) { 
800         PrintError(info->vm_info, info, "pci_front (%s): cannot attach to bus %s\n",dev_id,bus_id);
801         return -1;
802     }
803     
804     if (!(state = V3_Malloc(sizeof(struct pci_front_internal)))) { 
805         PrintError(info->vm_info, info, "pci_front (%s): cannot allocate state for device\n",dev_id);
806         return -1;
807     }
808     
809     memset(state, 0, sizeof(struct pci_front_internal));
810     
811     state->pci_bus = bus;
812     strncpy(state->name, dev_id, 32);
813     
814     if (!(dev = v3_add_device(vm, dev_id, &dev_ops, state))) { 
815         PrintError(info->vm_info, info, "pci_front (%s): unable to add device\n",state->name);
816         return -1;
817     }
818     
819     if (!(state->host_dev=v3_host_dev_open(url,V3_BUS_CLASS_PCI,dev,pci_front_intr_update_callback,vm))) { 
820         PrintError(info->vm_info, info, "pci_front (%s): unable to attach to host device %s\n",state->name, url);
821         v3_remove_device(dev);
822         return -1;
823     }
824     
825     // fetch config space from the host
826     if (pull_config(state,state->config_space)) { 
827         PrintError(info->vm_info, info, "pci_front (%s): cannot initially configure device\n",state->name);
828         v3_remove_device(dev);
829         return -1;
830     }
831
832     // setup virtual device for now
833     if (setup_virt_pci_dev(vm,dev)<0) { 
834         PrintError(info->vm_info, info, "pci_front (%s): cannot set up virtual pci device\n", state->name);
835         v3_remove_device(dev);
836         return -1;
837     }
838
839     // We do not need to hook anything here since pci will call
840     // us back via the bar_init functions
841
842     PrintDebug(info->vm_info, info, "pci_front (%s): inited and ready to be Potemkinized\n",state->name);
843
844     return 0;
845
846 }
847
848
849 device_register("PCI_FRONT", pci_front_init)