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.


1af5e86e9ea9b1fa36c10aaf9ebd4e9e141a71b8
[palacios.git] / palacios / src / devices / host_pci_selpriv.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) 2013, Peter Dinda <pdinda@northwestern.edu> (refactoring and selpriv feature)
11  * Copyright (c) 2012, Jack Lange <jacklange@cs.pitt.edu>
12  * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Authors: Jack Lange <jacklange@cs.pitt.edu>
16  *          Peter Dinda <pdinda@northwestern.edu> (selective privilege and refactor)
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21
22
23 /* 
24    This is the generic passthrough PCI virtual device which allows
25    access to the underlying device to be dynamically provided and
26    revoked.  It is a variant of the host_pci.c device, which is
27    "always on" (access permitted).
28 */
29
30 /* 
31  * The basic idea is that we do not change the hardware PCI
32  * configuration Instead we modify the guest environment to map onto
33  * the physical configuration
34  * 
35  * The pci subsystem handles most of the configuration space, except
36  * for the bar registers.  We handle them here, by either letting them
37  * go directly to hardware or remapping through virtual hooks
38  * 
39  * Memory Bars are remapped via the shadow map on privileged operation
40  * IO Bars are selectively remapped through hooks if the guest changes
41  * them
42  *   
43  * By default, this device is fully privileged at all times, meaning
44  * that passthrough to the underlying host side implementation is
45  * always active.  In this mode, it should behave the same has the
46  * host_pci.c device.
47  *
48  * The device can also be instantiated with the options
49  * selective_privilege=yes and privilege_extension=name In this
50  * "selective privilege" mode of operation, the device starts without
51  * privilege, and the extension can dynamically switch between
52  * privileged and unprivileged operation.  In unprivileged operation,
53  * the bars are remapped to stubs, and interrupt delivery is disabled.
54  */
55
56
57 #include <palacios/vmm.h>
58 #include <palacios/vmm_dev_mgr.h>
59 #include <palacios/vmm_sprintf.h>
60 #include <palacios/vmm_lowlevel.h>
61 #include <palacios/vm_guest.h> // must include this to avoid dependency issue
62 #include <palacios/vmm_symspy.h>
63
64 #include <devices/pci.h>
65 #include <devices/pci_types.h>
66 #include <interfaces/host_pci.h>
67
68 #include <palacios/vmm_types.h>
69
70 #include <gears/privilege.h>
71
72
73 #ifndef V3_CONFIG_DEBUG_HOST_PCI_SELPRIV
74 #undef PrintDebug
75 #define PrintDebug(fmt, args...)
76 #endif
77
78
79 // set this to one if you want to test the device
80 // in "always on" mode for compatability with host_pci.c
81 #define TEST_NOSELPRIV 0
82
83
84 #define PCI_BUS_MAX  7
85 #define PCI_DEV_MAX 32
86 #define PCI_FN_MAX   7
87
88 #define PCI_DEVICE 0x0
89 #define PCI_PCI_BRIDGE 0x1
90 #define PCI_CARDBUS_BRIDGE 0x2
91
92 #define PCI_HDR_SIZE 256
93
94
95 typedef enum { UNPRIVILEGED=0, PRIVILEGED} priv_state_t;
96 typedef enum { UNMAPPED=0, STUB, PHYSICAL}  bar_state_t;
97
98 struct host_pci_state {
99     // This holds the description of the host PCI device configuration
100     struct v3_host_pci_dev * host_dev;
101
102
103     struct v3_host_pci_bar virt_bars[6];
104     struct v3_host_pci_bar virt_exp_rom;
105      
106     struct vm_device * pci_bus;
107     struct pci_device * pci_dev;
108
109     char name[32];
110
111   // The current privilege level of the device
112   priv_state_t             priv_state;
113   // how each bar is currently mapped
114   bar_state_t              bar_state[6];
115 };
116
117
118
119 /*
120 static int pci_exp_rom_init(struct vm_device * dev, struct host_pci_state * state) {
121     struct pci_device * pci_dev = state->pci_dev;
122     struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
123
124
125     
126     PrintDebug(info->vm_info, info, "Adding 32 bit PCI mem region: start=%p, end=%p\n",
127                (void *)(addr_t)hrom->addr, 
128                (void *)(addr_t)(hrom->addr + hrom->size));
129
130     if (hrom->exp_rom_enabled) {
131         // only map shadow memory if the ROM is enabled 
132
133         v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
134                           hrom->addr, 
135                           hrom->addr + hrom->size - 1,
136                           hrom->addr);
137
138         // Initially the virtual location matches the physical ones
139         memcpy(&(state->virt_exp_rom), hrom, sizeof(struct v3_host_pci_bar));
140
141
142         PrintDebug(info->vm_info, info, "phys exp_rom: addr=%p, size=%u\n", 
143                    (void *)(addr_t)hrom->addr, 
144                    hrom->size);
145
146
147         // Update the pci subsystem versions
148         pci_dev->config_header.expansion_rom_address = PCI_EXP_ROM_VAL(hrom->addr, hrom->exp_rom_enabled);
149     }
150
151
152
153     return 0;
154 }
155 */
156
157
158 static int pt_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
159     struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
160     int port_offset = port % pbar->size;
161
162     if (length == 1) {
163         *(uint8_t *)dst = v3_inb(pbar->addr + port_offset);
164     } else if (length == 2) {
165         *(uint16_t *)dst = v3_inw(pbar->addr + port_offset);
166     } else if (length == 4) {
167         *(uint32_t *)dst = v3_indw(pbar->addr + port_offset);
168     } else {
169         PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size read\n");
170         return -1;
171     }
172
173     return length;
174 }
175
176
177 static int pt_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
178     struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
179     int port_offset = port % pbar->size;
180     
181     if (length == 1) {
182         v3_outb(pbar->addr + port_offset, *(uint8_t *)src);
183     } else if (length == 2) {
184         v3_outw(pbar->addr + port_offset, *(uint16_t *)src);
185     } else if (length == 4) {
186         v3_outdw(pbar->addr + port_offset, *(uint32_t *)src);
187     } else {
188         PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size write\n");
189         return -1;
190     }
191     
192     return length;
193
194 }
195
196
197 static int pt_io_read_stub(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
198   PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at port %d\n",length,port);
199   memset(dst,0,length);
200   return length;
201 }
202
203 static int pt_io_write_stub(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
204   PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at port %d\n",length,port);
205   return length;
206 }
207
208
209 static int pt_mem_read_stub(struct guest_info * core, 
210                             addr_t guest_addr, 
211                             void * dst, 
212                             uint_t length, 
213                             void * priv_data)
214 {
215   PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at %p\n",length,(void*)guest_addr);
216
217   memset(dst,0,length);
218   return length;
219 }
220
221 static int pt_mem_write_stub(struct guest_info * core, 
222                              addr_t guest_addr, 
223                              void * src, 
224                              uint_t length, 
225                              void * priv_data)
226 {
227   PrintDebug(core->vm_info, core, "Ignoring write of %u bytes at %p\n",length,(void*)guest_addr);
228   return length;
229 }
230
231
232 static int pci_unmap_bar(struct vm_device        *dev,
233                          struct host_pci_state   *state,
234                          int                      bar_num)
235 {
236   //  struct v3_host_pci_bar *hbar = &(state->host_dev->bars[bar_num]);
237   struct v3_host_pci_bar *vbar = &(state->virt_bars[bar_num]);
238   
239   if (state->bar_state[bar_num] == UNMAPPED) {
240     // this is idempotent, so not an error
241     return 0;
242   }
243   
244   if (state->bar_state[bar_num] != PHYSICAL &&
245       state->bar_state[bar_num] != STUB) { 
246     PrintError(dev->vm, VCORE_NONE, "Unknown bar state %d\n", state->bar_state[bar_num]);
247     return -1;
248   }
249
250   switch (vbar->type) { 
251     
252   case PT_BAR_NONE:
253     break;
254     
255   case PT_BAR_IO: {
256     int i;
257     // unhook old ports - same for both physical and stub
258     for (i = 0; i < vbar->size; i++) {
259       if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
260         PrintError(dev->vm, VCORE_NONE, "Could not unhook previously hooked port.... %d (0x%x)\n", 
261                    (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i);
262         return -1;
263       }
264     }
265   }
266     break;
267     
268   case PT_BAR_MEM32: {
269     
270     if (state->bar_state[bar_num] == PHYSICAL ) { 
271       // remove old mapping
272       struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
273       
274       if (old_reg == NULL) {
275         // uh oh...
276         PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr);
277         return -1;
278       }
279       
280       v3_delete_mem_region(dev->vm, old_reg); // void return?!
281       
282     } else {  // STUB
283       if (v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr)==-1) { 
284         PrintError(dev->vm, VCORE_NONE, "Failed to unhook memory\n");
285         return -1;
286       }
287     }
288   }
289
290     break;
291
292   case PT_BAR_MEM64_LO: 
293     
294     // the unmapping happens on the HI BAR
295
296     break;
297
298   case PT_BAR_MEM64_HI: {
299     //struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
300     struct v3_mem_region * old_reg =  v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
301
302     if (state->bar_state[bar_num] == PHYSICAL) { 
303       if (old_reg == NULL) {
304         // uh oh...
305         PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=%p)\n", 
306                    (void *)(addr_t)vbar->addr);
307         return -1;
308       }
309       
310       // remove old mapping
311       v3_delete_mem_region(dev->vm, old_reg); // void return?!
312           
313     } else { // STUB
314       if (v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr)==-1) { 
315         PrintError(dev->vm, VCORE_NONE, "Failed to unhook memory\n");
316         return -1;
317       }
318     }
319   }
320     break;
321     
322   default:
323       PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
324       return -1;
325       break;
326   }
327   
328   state->bar_state[bar_num] = UNMAPPED;
329   
330   return 0;
331   
332 }
333
334   
335   
336 static int pci_map_bar(struct vm_device        *dev,
337                        struct host_pci_state   *state,
338                        int                      bar_num,
339                        bar_state_t              target)
340 {
341   struct v3_host_pci_bar *hbar = &(state->host_dev->bars[bar_num]);
342   struct v3_host_pci_bar *vbar = &(state->virt_bars[bar_num]);
343   
344   if (state->bar_state[bar_num] != UNMAPPED) { 
345     PrintError(dev->vm, VCORE_NONE, "Attempt to map bar that is already mapped\n");
346     return -1;
347   }
348   
349   if (target != STUB && target != PHYSICAL) { 
350     PrintError(dev->vm, VCORE_NONE, "Attempt to map to unknown target %d\n",target);
351     return -1;
352   }
353   
354   
355   switch (vbar->type) { 
356     
357   case PT_BAR_NONE:
358     break;
359     
360   case PT_BAR_IO: {
361     int i;
362     
363     if (target == PHYSICAL) { 
364       if (vbar->addr == hbar->addr) {
365         // Map the io ports as passthrough
366         PrintDebug(dev->vm,VCORE_NONE,"Adding io port direct mappings\n");
367         for (i = 0; i < hbar->size; i++) {
368           if (v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL) == -1) { 
369             PrintError(dev->vm, VCORE_NONE, "Failed to hook ioport %llu direct\n",hbar->addr+i);
370             return -1;
371           }
372         }
373       } else {
374         // We have to manually handle the io redirection
375         PrintDebug(dev->vm,VCORE_NONE,"Adding io port indirect mappings\n");
376         for (i = 0; i < vbar->size; i++) {
377           if (v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, hbar) == -1) { 
378             PrintError(dev->vm,VCORE_NONE,"Failed to hook ioport %llu indirect\n", vbar->addr+i);
379             return -1;
380           }
381         }
382       }
383     } else { // STUB
384       // hook to stubs
385       PrintDebug(dev->vm,VCORE_NONE,"Adding io port indirect mappings to stubs\n");
386       for (i = 0; i < vbar->size; i++) {
387         if (v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read_stub, pt_io_write_stub, dev)==-1) { 
388           PrintError(dev->vm, VCORE_NONE, "Failed to hook ioport %llu indirect to stub\n",vbar->addr+i);
389           return -1;
390         }
391       } 
392     }
393   }
394     break;
395     
396   case PT_BAR_MEM32: {
397     
398     if (target == PHYSICAL) { 
399       PrintDebug(dev->vm, VCORE_NONE, 
400                  "Adding pci Passthrough mapping: start=0x%x, size=%d, end=0x%x (hpa=%p)\n", 
401                  (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
402       
403       if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, 
404                             vbar->addr, 
405                             vbar->addr + vbar->size - 1,
406                             hbar->addr) == -1 ) {
407         PrintError(dev->vm, VCORE_NONE, "Failed to add region\n");
408         return -1;
409       }
410     } else { // STUB
411       // we will hook the memory instead with the stubs
412       PrintDebug(dev->vm, VCORE_NONE, 
413                  "Adding pci Passthrough mapping to stubs: start=0x%x, size=%d, end=0x%x (hpa=%p)\n",
414                  (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
415       
416       if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY,
417                            vbar->addr, vbar->addr+vbar->size-1,
418                            pt_mem_read_stub, pt_mem_write_stub, dev) == -1) { 
419         PrintError(dev->vm, VCORE_NONE, "Cannot hook memory for unprivileged access\n");
420         return -1;
421       }
422     }
423   }
424     break;
425     
426   case PT_BAR_MEM64_LO: 
427     
428     // the mapping happens on the HI BAR
429     
430     break;
431     
432   case PT_BAR_MEM64_HI: {
433     
434     if (target == PHYSICAL) { 
435       PrintDebug(dev->vm, VCORE_NONE, 
436                  "Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n", 
437                  (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size, 
438                  (void *)(addr_t)(vbar->addr + vbar->size));
439       
440       if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, 
441                             vbar->addr + vbar->size - 1, hbar->addr) == -1) {
442         
443         PrintDebug(dev->vm, VCORE_NONE, "Fail to insert shadow region (%p, %p)  -> %p\n",
444                    (void *)(addr_t)vbar->addr,
445                    (void *)(addr_t)(vbar->addr + vbar->size - 1),
446                    (void *)(addr_t)hbar->addr);
447         return -1;
448       }
449     } else { // STUB
450       // we will hook the memory instead, using the stubs
451       PrintDebug(dev->vm, VCORE_NONE, 
452                  "Adding pci Passthrough mapping to stubs: start=0x%x, size=%d, end=0x%x (hpa=%p)\n",
453                  (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
454       
455       if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY,
456                            vbar->addr, vbar->addr+vbar->size-1,
457                            pt_mem_read_stub, pt_mem_write_stub, dev) == -1) { 
458         PrintError(dev->vm, VCORE_NONE, "Cannot hook memory for unprivileged access\n");
459         return -1;
460       }
461     }
462   }
463     break;
464     
465   default:
466     PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
467     return -1;
468     break;
469   }
470   
471   state->bar_state[bar_num]=target;
472   
473   return 0;
474   
475  }
476         
477
478 //
479 // This unmaps the bar no matter what state it's in
480 //
481 static int deactivate_bar(struct vm_device *dev,
482                            int bar_num)
483 {
484   struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
485   
486   if (pci_unmap_bar(dev, state, bar_num)) { 
487     PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num);
488     return -1;
489   } 
490   return 0;
491 }
492   
493 //
494 // This does nothing if the bar is already physical
495 // if it's not, it unmaps it and then maps it physical
496 //
497 static int activate_bar_physical(struct vm_device *dev,
498                                  int bar_num)
499 {
500   struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
501   
502   if (state->bar_state[bar_num] != PHYSICAL) { 
503     if (pci_unmap_bar(dev, state, bar_num)) { 
504       PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num);
505       return -1;
506     } 
507     if (pci_map_bar(dev,state,bar_num,PHYSICAL)) { 
508       PrintError(dev->vm, VCORE_NONE, "Cannot map bar %d\n", bar_num);
509       return -1;
510     }
511   }
512   return 0;
513 }
514
515 //
516 // This does nothing if the bar is already stub
517 // if it's not, it unmaps it and then maps it stub
518 //
519 static int activate_bar_stub(struct vm_device *dev,
520                              int bar_num)
521 {
522   struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
523   
524   if (state->bar_state[bar_num] != STUB) { 
525     if (pci_unmap_bar(dev, state, bar_num)) { 
526       PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num);
527       return -1;
528     } 
529     if (pci_map_bar(dev,state,bar_num,STUB)) { 
530       PrintError(dev->vm, VCORE_NONE, "Cannot map bar %d\n", bar_num);
531       return -1;
532     }
533   }
534   return 0;
535 }
536
537 static int activate_bar(struct vm_device *dev,
538                         int bar_num)
539 {
540   struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
541   
542   if (state->priv_state == PRIVILEGED) { 
543     return activate_bar_physical(dev,bar_num);
544   } else {
545     return activate_bar_stub(dev,bar_num);
546   }
547 }
548
549
550
551 static int reactivate_device_privileged(struct vm_device *dev)
552 {
553   int i;
554   struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
555   
556   PrintDebug(dev->vm, VCORE_NONE, "Switch device to privileged operation\n");
557   
558   for (i=0;i<6;i++) { 
559     if (activate_bar_physical(dev,i)) { 
560       PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d as physical\n",i);
561       return -1;
562     }
563   }
564   state->priv_state=PRIVILEGED;
565   return 0;
566 }
567
568 static int reactivate_device_unprivileged(struct vm_device *dev)
569 {
570   int i;
571   struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
572   
573   PrintDebug(dev->vm, VCORE_NONE, "Switch device to unprivileged operation\n");
574   
575   for (i=0;i<6;i++) { 
576     if (activate_bar_stub(dev,i)) { 
577       PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d as stub\n",i);
578       return -1;
579     }
580   }
581   state->priv_state=UNPRIVILEGED;
582   return 0;
583 }
584
585
586 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
587     struct vm_device * dev = (struct vm_device *)private_data;
588     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
589     struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
590     uint32_t bar_val = 0;
591
592     if (hbar->type == PT_BAR_IO) {
593         bar_val = PCI_IO_BAR_VAL(hbar->addr);
594     } else if (hbar->type == PT_BAR_MEM32) {
595         bar_val = PCI_MEM32_BAR_VAL(hbar->addr, hbar->prefetchable);
596     } else if (hbar->type == PT_BAR_MEM24) {
597         bar_val = PCI_MEM24_BAR_VAL(hbar->addr, hbar->prefetchable);
598     } else if (hbar->type == PT_BAR_MEM64_LO) {
599         struct v3_host_pci_bar * hi_hbar = &(state->host_dev->bars[bar_num + 1]);
600         bar_val = PCI_MEM64_LO_BAR_VAL(hi_hbar->addr, hbar->prefetchable);
601     } else if (hbar->type == PT_BAR_MEM64_HI) {
602         bar_val = PCI_MEM64_HI_BAR_VAL(hbar->addr, hbar->prefetchable);
603     } else {
604         PrintDebug(dev->vm, VCORE_NONE, "Unknown bar type %d\n",hbar->type);
605     }
606
607     memcpy(&(state->virt_bars[bar_num]), hbar, sizeof(struct v3_host_pci_bar));
608
609     *dst = bar_val;
610
611     if (activate_bar(dev,bar_num)) { 
612       PrintError(dev->vm, VCORE_NONE,"Cannot activate bar %d\n",bar_num);
613       return -1;
614     }
615
616
617     return 0;
618 }
619
620
621
622 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
623     struct vm_device * dev = (struct vm_device *)private_data;
624     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
625     
626     struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
627     struct v3_host_pci_bar * vbar = &(state->virt_bars[bar_num]);
628
629
630     if (deactivate_bar(dev,bar_num)) { 
631       PrintError(dev->vm, VCORE_NONE, "Cannot deactivate bar %d\n", bar_num);
632       return -1;
633     }
634
635     switch (vbar->type) { 
636     case PT_BAR_NONE:
637       // nothing to do
638       break;
639
640     case PT_BAR_IO: 
641
642       // clear the low bits to match the size
643       vbar->addr = *src & ~(hbar->size - 1);
644       
645       // udpate source version
646       *src = PCI_IO_BAR_VAL(vbar->addr);
647
648       break;
649
650     case PT_BAR_MEM32:
651
652       // clear the low bits to match the size
653       vbar->addr = *src & ~(hbar->size - 1);
654       
655       // Set reserved bits
656       *src = PCI_MEM32_BAR_VAL(vbar->addr, hbar->prefetchable);
657       
658       break;
659
660     case  PT_BAR_MEM64_LO:
661
662       // We only store the written values here, the actual reconfig comes when the high BAR is updated
663       
664       vbar->addr = *src & ~(hbar->size - 1);
665       
666       *src = PCI_MEM64_LO_BAR_VAL(vbar->addr, hbar->prefetchable);
667
668       break;
669
670     case PT_BAR_MEM64_HI: {
671
672       struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
673
674       vbar->addr = (((uint64_t)*src) << 32) + lo_vbar->addr;
675       
676       // We don't set size, because we assume region is less than 4GB
677       // src does not change, because there are no reserved bits
678         
679     }
680       break;
681       
682     default:
683       PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
684       return -1;
685       
686       break;
687     }
688
689     if (activate_bar(dev,bar_num)) { 
690       PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d\n", bar_num);
691       return -1;
692     }
693
694     return 0;
695
696 }
697
698
699 static int pt_config_write(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
700     struct vm_device * dev = (struct vm_device *)private_data;
701     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
702     
703 //    V3_PrintStubStub(dev->vm, VCORE_NONE, "Writing host PCI config space update\n");
704
705     // We will mask all operations to the config header itself, 
706     // and only allow direct access to the device specific config space
707     if (reg_num < 64) {
708         return 0;
709     }
710
711     if (TEST_NOSELPRIV || state->priv_state==PRIVILEGED) { 
712       return v3_host_pci_config_write(state->host_dev, reg_num, src, length);
713     } else {
714       PrintError(dev->vm, VCORE_NONE, "configuration write while unprivileged - CURRENTLY IGNORED\n");
715       return length;
716     }
717 }
718
719
720
721 static int pt_config_read(struct pci_device * pci_dev, uint32_t reg_num, void * dst, uint_t length, void * private_data) {
722     struct vm_device * dev = (struct vm_device *)private_data;
723     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
724     
725   //  V3_PrintStubStub(dev->vm, VCORE_NONE, "Reading host PCI config space update\n");
726
727     if (TEST_NOSELPRIV || state->priv_state==PRIVILEGED) { 
728       return v3_host_pci_config_read(state->host_dev, reg_num, dst, length);
729     } else {
730       PrintError(dev->vm, VCORE_NONE, "configuration read while unprivileged - CURRENTLY IGNORED\n");
731       memset(dst,0,length);
732       return length;
733     }
734 }
735
736
737
738 /* This is really iffy....
739  * It was totally broken before, but it's _not_ totally fixed now
740  * The Expansion rom can be enabled/disabled via software using the low order bit
741  * We should probably handle that somehow here... 
742  */
743 static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) {
744     struct vm_device * dev = (struct vm_device *)(priv_data);
745     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
746     
747     struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
748     struct v3_host_pci_bar * vrom = &(state->virt_exp_rom);
749
750     PrintDebug(dev->vm, VCORE_NONE, "exp_rom update: src=0x%x\n", *src);
751     PrintDebug(dev->vm, VCORE_NONE, "vrom is size=%u, addr=0x%x\n", vrom->size, (uint32_t)vrom->addr);
752     PrintDebug(dev->vm, VCORE_NONE, "hrom is size=%u, addr=0x%x\n", hrom->size, (uint32_t)hrom->addr);
753
754
755     if (!TEST_NOSELPRIV && state->priv_state != PRIVILEGED) { 
756       PrintError(dev->vm, VCORE_NONE, "Attempt to do expansion rom write when not privileged is IGNORED\n");
757       //PrintError(dev->vm, VCORE_NONE,"\tKCH: letting it through though\n");
758       /* KCH: let it through for now, commenting below */
759       return 0;
760     }
761
762     if (hrom->exp_rom_enabled) {
763         // only remove old mapping if present, I.E. if the rom was enabled previously 
764         if (vrom->exp_rom_enabled) {
765             struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr);
766           
767             if (old_reg == NULL) {
768                 // uh oh...
769                 PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr);
770                 return -1;
771             }
772           
773             v3_delete_mem_region(dev->vm, old_reg);
774         }
775       
776       
777         vrom->addr = *src & ~(hrom->size - 1);
778       
779         // Set flags in actual register value
780         *src = PCI_EXP_ROM_VAL(vrom->addr, (*src & 0x00000001));
781       
782         PrintDebug(dev->vm, VCORE_NONE, "Cooked src=0x%x\n", *src);
783       
784   
785         PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n", 
786                    (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
787       
788         if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr, 
789                               vrom->addr + vrom->size - 1, hrom->addr) == -1) {
790             PrintError(dev->vm, VCORE_NONE, "Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n", 
791                        (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
792             return -1;
793         }
794     }
795     
796     return 0;
797 }
798
799
800 static int pt_cmd_update(struct pci_device * pci, pci_cmd_t cmd, uint64_t arg, void * priv_data) {
801     struct vm_device * dev = (struct vm_device *)(priv_data);
802     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
803
804     V3_Print(dev->vm, VCORE_NONE, "Host PCI Device: CMD update (%d)(arg=%llu)\n", cmd, arg);
805     
806     
807     if (TEST_NOSELPRIV || state->priv_state == PRIVILEGED) { 
808       v3_host_pci_cmd_update(state->host_dev, cmd, arg);
809     } else {
810       PrintError(dev->vm, VCORE_NONE, "Attempt to do cmd update when not in privileged mode is IGNORED\n");
811       return 0;
812     }
813
814     return 0;
815 }
816
817
818 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) {
819     struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
820     struct pci_device * pci_dev = NULL;
821     struct v3_pci_bar bars[6];
822     int bus_num = 0;
823     int i;
824
825     for (i = 0; i < 6; i++) {
826         bars[i].type = PCI_BAR_PASSTHROUGH;
827         bars[i].private_data = dev;
828         bars[i].bar_init = pci_bar_init;
829         bars[i].bar_write = pci_bar_write;
830     }
831
832     pci_dev = v3_pci_register_device(state->pci_bus,
833                                      PCI_STD_DEVICE,
834                                      bus_num, -1, 0, 
835                                      state->name, bars,
836                                      pt_config_write,
837                                      pt_config_read,
838                                      pt_cmd_update,
839                                      pt_exp_rom_write,               
840                                      dev);
841
842
843     state->pci_dev = pci_dev;
844
845     //    pci_exp_rom_init(dev, state);
846     pci_dev->config_header.expansion_rom_address = 0;
847     
848     v3_pci_enable_capability(pci_dev, PCI_CAP_MSI);
849 //    v3_pci_enable_capability(pci_dev, PCI_CAP_MSIX);
850     v3_pci_enable_capability(pci_dev, PCI_CAP_PCIE);
851     v3_pci_enable_capability(pci_dev, PCI_CAP_PM);
852
853
854
855     if (state->host_dev->iface == SYMBIOTIC) {
856 #ifdef V3_CONFIG_SYMBIOTIC
857         v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num);
858 #else
859         PrintError(vm_info, VCORE_NONE, "ERROR Symbiotic Passthrough is not enabled\n");
860         return -1;
861 #endif
862     }
863
864     return 0;
865 }
866
867
868
869 static int init_priv(struct guest_info * core, void ** private_data) 
870 {
871   PrintDebug(core->vm_info, core, "Initializing host_pci device selective privilege operation\n");
872   return 0;
873 }
874
875 static int lower_priv(struct guest_info * core, void * private_data)
876 {
877   struct vm_device * dev = (struct vm_device *)private_data;
878   
879   PrintDebug(core->vm_info, core, "Lowering privilege for device %s\n", dev->name);
880   
881   return reactivate_device_unprivileged(dev);
882 }
883
884 static int raise_priv(struct guest_info * core, void * private_data)
885 {
886   struct vm_device * dev = (struct vm_device *)private_data;
887   
888   PrintDebug(core->vm_info, core, "Raising privilege for device %s\n", dev->name);
889     
890   return reactivate_device_privileged(dev);
891 }
892
893 static int deinit_priv(struct guest_info * core, void * private_data)
894 {
895   PrintDebug(core->vm_info, core, "Deinitializing host_pci device selective privilege operation\n");
896   return 0;
897 }
898
899
900 static struct v3_device_ops dev_ops = {
901     .free = NULL,
902 };
903
904
905 static int irq_ack(struct guest_info * core, uint32_t irq, void * private_data) {
906     struct host_pci_state * state = (struct host_pci_state *)private_data;
907
908     
909     PrintDebug(core->vm_info,core,"GM HPCI: IRQ ACK of irq %d\n", irq);
910     //    V3_PrintStubStub(core->vm_info, core, "Acking IRQ %d\n", irq);
911     v3_host_pci_ack_irq(state->host_dev, irq);
912
913     return 0;
914 }
915
916
917 static int irq_handler(void * private_data, uint32_t vec_index) {
918     struct host_pci_state * state = (struct host_pci_state *)private_data;
919     struct v3_irq vec;
920
921     vec.irq = vec_index;
922     vec.ack = irq_ack;
923     vec.private_data = state;
924
925     // For selective privilege, the interrupt is always delivered
926     // identically to the regular case, regardless of the current
927     // privilege level.  The idea here is that delivery of the
928     // interrupt will result in a dispatch to the privileged guest code
929     // and the entry to that code will enable privilege before any
930     // mapped bar is read or written.
931
932     PrintDebug(0, 0,"Host PCI / Selective Privilege: Delivering vec irq %d, state %d\n", vec.irq, (int)(state->pci_dev->irq_type));
933     if (state->pci_dev->irq_type == IRQ_NONE) {
934         return 0;
935     } else if (state->pci_dev->irq_type == IRQ_INTX) {
936         v3_pci_raise_acked_irq(state->pci_bus, state->pci_dev, vec);
937     } else {
938         v3_pci_raise_irq(state->pci_bus, state->pci_dev, vec_index);
939     }
940
941     return 0;
942 }
943
944
945 static int host_pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
946     struct host_pci_state * state = V3_Malloc(sizeof(struct host_pci_state));
947     struct vm_device * dev = NULL;
948     struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
949     char * dev_id = v3_cfg_val(cfg, "ID");    
950     char * url = v3_cfg_val(cfg, "url");
951
952     memset(state, 0, sizeof(struct host_pci_state));
953
954     if (!pci) {
955         PrintError(vm, VCORE_NONE, "PCI bus not specified in config file\n");
956         return -1;
957     }
958     
959     state->pci_bus = pci;
960     strncpy(state->name, dev_id, 32);
961
962
963     dev = v3_add_device(vm, dev_id, &dev_ops, state);
964
965     if (dev == NULL) {
966         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
967         V3_Free(state);
968         return -1;
969     }
970
971     state->host_dev = v3_host_pci_get_dev(vm, url, state);
972
973     if (state->host_dev == NULL) {
974         PrintError(vm, VCORE_NONE, "Could not connect to host pci device (%s)\n", url);
975         return -1;
976     }
977
978
979     state->host_dev->irq_handler = irq_handler;
980
981     char * sel_priv = v3_cfg_val(cfg, "selective_privilege");
982
983     if (!sel_priv || !strncasecmp(sel_priv,"off",3) || !strncasecmp(sel_priv,"0",1)) { 
984 #if TEST_NOSELPRIV
985       state->priv_state=PRIVILEGED;
986 #else
987       state->priv_state=UNPRIVILEGED;
988 #endif
989       PrintDebug(vm, VCORE_NONE, "Selective privilege functionality disabled\n");
990     } else {
991
992       char * ext_priv = v3_cfg_val(cfg, "privilege_extension");
993
994       if (!ext_priv) { 
995         PrintError(vm, VCORE_NONE, "Selective privilege functionality requested, but not privilege extension given\n");
996         return -1;
997       }
998       if (v3_bind_privilege(&(vm->cores[0]), 
999                             ext_priv, 
1000                             init_priv,
1001                             lower_priv,
1002                             raise_priv,
1003                             deinit_priv,
1004                             dev)) { 
1005         PrintError(vm, VCORE_NONE, "Cannot bind to privilege %s\n",ext_priv);
1006         return -1;
1007       }
1008
1009       state->priv_state=UNPRIVILEGED;
1010
1011       PrintDebug(vm, VCORE_NONE, "Selective privilege functionality enabled (initially unprivileged)\n");
1012
1013     }
1014
1015     if (setup_virt_pci_dev(vm, dev) == -1) {
1016         PrintError(vm, VCORE_NONE, "Could not setup virtual host PCI device\n");
1017         return -1;
1018     }
1019
1020     return 0;
1021 }
1022
1023
1024
1025
1026 device_register("HOST_PCI_SELPRIV", host_pci_init)
1027