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.


Linux compatibility fixes
[palacios.git] / linux_module / iface-host-pci-hw.h
1 /* Linux host side PCI passthrough support
2  * Jack Lange <jacklange@cs.pitt.edu>, 2012
3  */
4
5 #include <linux/pci.h>
6 #include <linux/iommu.h>
7 #include <linux/interrupt.h>
8 #include <linux/version.h>
9
10
11 #define PCI_HDR_SIZE 256
12
13 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 43)
14 #define IOMMU_FOUND()        iommu_present(&pci_bus_type)
15 #define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc(&pci_bus_type)
16 #else
17 #define IOMMU_FOUND()        iommu_found()
18 #define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc()
19 #endif
20
21
22
23 static int setup_hw_pci_dev(struct host_pci_device * host_dev) {
24     int ret = 0;
25     struct pci_dev * dev = NULL;
26     struct v3_host_pci_dev * v3_dev = &(host_dev->v3_dev);
27
28     dev = pci_get_bus_and_slot(host_dev->hw_dev.bus,
29                                host_dev->hw_dev.devfn);
30
31
32     if (dev == NULL) {
33         printk("Could not find HW pci device (bus=%d, devfn=%d)\n", 
34                host_dev->hw_dev.bus, host_dev->hw_dev.devfn); 
35         return -1;
36     }
37
38     // record pointer in dev state
39     host_dev->hw_dev.dev = dev;
40
41     host_dev->hw_dev.intx_disabled = 1;
42     palacios_spinlock_init(&(host_dev->hw_dev.intx_lock));
43
44     if (pci_enable_device(dev)) {
45         printk("Could not enable Device\n");
46         return -1;
47     }
48     
49     ret = pci_request_regions(dev, "v3vee");
50     if (ret != 0) {
51         printk("Could not reservce PCI regions\n");
52         return -1;
53     }
54
55
56     pci_reset_function(host_dev->hw_dev.dev);
57     pci_save_state(host_dev->hw_dev.dev);
58
59
60     {
61         int i = 0;
62         for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
63             printk("Resource %d\n", i);
64             printk("\tflags = 0x%lx\n", pci_resource_flags(dev, i));
65             printk("\t name=%s, start=%lx, size=%d\n", 
66                    host_dev->hw_dev.dev->resource[i].name, (uintptr_t)pci_resource_start(dev, i),
67                    (u32)pci_resource_len(dev, i));
68
69         }
70
71         printk("Rom BAR=%d\n", dev->rom_base_reg);
72     }
73
74     /* Cache first 6 BAR regs */
75     {
76         int i = 0;
77
78         for (i = 0; i < 6; i++) {
79             struct v3_host_pci_bar * bar = &(v3_dev->bars[i]);
80             unsigned long flags;
81             
82             bar->size = pci_resource_len(dev, i);
83             bar->addr = pci_resource_start(dev, i);
84             flags = pci_resource_flags(dev, i);
85
86             if (flags & IORESOURCE_IO) {
87                 bar->type = PT_BAR_IO;
88             } else if (flags & IORESOURCE_MEM) {
89                 if ((flags & IORESOURCE_MEM_64)) {
90                     // this should never happen with i==5, but it
91                     // is technically an OOB access without the modulo
92                     struct v3_host_pci_bar * hi_bar = &(v3_dev->bars[(i + 1) % 6]); 
93             
94                     bar->type = PT_BAR_MEM64_LO;
95
96                     hi_bar->type = PT_BAR_MEM64_HI;
97                     hi_bar->size = bar->size;
98                     hi_bar->addr = bar->addr;
99                     hi_bar->cacheable = ((flags & IORESOURCE_CACHEABLE) != 0);
100                     hi_bar->prefetchable = ((flags & IORESOURCE_PREFETCH) != 0);
101                     
102                     i++;
103                 } else if (flags & IORESOURCE_DMA) {
104                     bar->type = PT_BAR_MEM24;
105                 } else {
106                     bar->type = PT_BAR_MEM32;
107                 }
108                 
109                 bar->cacheable = ((flags & IORESOURCE_CACHEABLE) != 0);
110                 bar->prefetchable = ((flags & IORESOURCE_PREFETCH) != 0);
111
112             } else {
113                 bar->type = PT_BAR_NONE;
114             }
115         }
116     }
117
118     /* Cache expansion rom bar */
119     {
120         struct resource * rom_res = &(dev->resource[PCI_ROM_RESOURCE]);
121         int rom_size = pci_resource_len(dev, PCI_ROM_RESOURCE);
122
123         if (rom_size > 0) {
124           //unsigned long flags;
125
126             v3_dev->exp_rom.size = rom_size;
127             v3_dev->exp_rom.addr = pci_resource_start(dev, PCI_ROM_RESOURCE);
128             // flags = pci_resource_flags(dev, PCI_ROM_RESOURCE); 
129
130             v3_dev->exp_rom.type = PT_EXP_ROM;
131
132             v3_dev->exp_rom.exp_rom_enabled = rom_res->flags & IORESOURCE_ROM_ENABLE;
133         }
134     }
135
136     /* Cache entire configuration space */
137     {
138         int m = 0;
139
140         // Copy the configuration space to the local cached version
141         for (m = 0; m < PCI_HDR_SIZE; m += 4) {
142             pci_read_config_dword(dev, m, (u32 *)&(v3_dev->cfg_space[m]));
143         }
144     }
145
146
147     /* HARDCODED for now but this will need to depend on IOMMU support detection */
148     if (IOMMU_FOUND()) {
149         printk("Setting host PCI device (%s) as IOMMU\n", host_dev->name);
150         v3_dev->iface = IOMMU;
151     } else {
152         printk("Setting host PCI device (%s) as SYMBIOTIC\n", host_dev->name);
153         v3_dev->iface = SYMBIOTIC;
154     }
155
156     return 0;
157
158 }
159
160
161
162 static irqreturn_t host_pci_intx_irq_handler(int irq, void * priv_data) {
163     struct host_pci_device * host_dev = priv_data;
164
165     //   printk("Host PCI IRQ handler (%d)\n", irq);
166
167     palacios_spinlock_lock(&(host_dev->hw_dev.intx_lock));
168     disable_irq_nosync(irq);
169     host_dev->hw_dev.intx_disabled = 1;
170     palacios_spinlock_unlock(&(host_dev->hw_dev.intx_lock));
171
172     V3_host_pci_raise_irq(&(host_dev->v3_dev), 0);
173
174     return IRQ_HANDLED;
175 }
176
177
178
179 static irqreturn_t host_pci_msi_irq_handler(int irq, void * priv_data) {
180     struct host_pci_device * host_dev = priv_data;
181     //    printk("Host PCI MSI IRQ Handler (%d)\n", irq);
182
183     V3_host_pci_raise_irq(&(host_dev->v3_dev), 0);
184
185     return IRQ_HANDLED;
186 }
187
188 static irqreturn_t host_pci_msix_irq_handler(int irq, void * priv_data) {
189     struct host_pci_device * host_dev = priv_data;
190     int i = 0;
191     
192     //    printk("Host PCI MSIX IRQ Handler (%d)\n", irq);
193     
194     // find vector index
195     for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
196         if (irq == host_dev->hw_dev.msix_entries[i].vector) {
197             V3_host_pci_raise_irq(&(host_dev->v3_dev), i);
198         } else {
199             printk("Error Could not find matching MSIX vector for IRQ %d\n", irq);
200         }
201     }    
202     return IRQ_HANDLED;
203 }
204
205
206 static int hw_pci_cmd(struct host_pci_device * host_dev, host_pci_cmd_t cmd, u64 arg) {
207     //struct v3_host_pci_dev * v3_dev = &(host_dev->v3_dev);
208     struct pci_dev * dev = host_dev->hw_dev.dev;
209
210     switch (cmd) {
211         case HOST_PCI_CMD_DMA_DISABLE:
212             printk("Passthrough PCI device disabling BMDMA\n");
213             pci_clear_master(host_dev->hw_dev.dev);
214             break;
215         case HOST_PCI_CMD_DMA_ENABLE:
216             printk("Passthrough PCI device Enabling BMDMA\n");
217             pci_set_master(host_dev->hw_dev.dev);
218             break;
219
220         case HOST_PCI_CMD_INTX_DISABLE:
221             printk("Passthrough PCI device disabling INTx IRQ\n");
222
223             disable_irq(dev->irq);
224             free_irq(dev->irq, (void *)host_dev);
225
226             break;
227         case HOST_PCI_CMD_INTX_ENABLE:
228             printk("Passthrough PCI device Enabling INTx IRQ\n");
229         
230             if (request_threaded_irq(dev->irq, NULL, host_pci_intx_irq_handler, 
231                                      IRQF_ONESHOT, "V3Vee_Host_PCI_INTx", (void *)host_dev)) {
232                 printk("ERROR Could not assign IRQ to host PCI device (%s)\n", host_dev->name);
233             }
234
235             break;
236
237         case HOST_PCI_CMD_MSI_DISABLE:
238             printk("Passthrough PCI device Disabling MSIs\n");
239
240             disable_irq(dev->irq);
241             free_irq(dev->irq, (void *)host_dev);
242
243             pci_disable_msi(dev);
244
245             break;
246         case HOST_PCI_CMD_MSI_ENABLE:
247             printk("Passthrough PCI device Enabling MSI\n");
248             
249             if (!dev->msi_enabled) {
250                 pci_enable_msi(dev);
251
252                 if (request_irq(dev->irq, host_pci_msi_irq_handler, 
253                                 0, "V3Vee_host_PCI_MSI", (void *)host_dev)) {
254                     printk("Error Requesting IRQ %d for Passthrough MSI IRQ\n", dev->irq);
255                 }
256             }
257
258             break;
259
260
261
262         case HOST_PCI_CMD_MSIX_ENABLE: {
263             int i = 0;
264             
265             printk("Passthrough PCI device Enabling MSIX\n");
266             host_dev->hw_dev.num_msix_vecs = arg;;
267             host_dev->hw_dev.msix_entries = kcalloc(host_dev->hw_dev.num_msix_vecs, 
268                                                     sizeof(struct msix_entry), GFP_KERNEL);
269             
270             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
271                 host_dev->hw_dev.msix_entries[i].entry = i;
272             }
273             
274             pci_enable_msix(dev, host_dev->hw_dev.msix_entries, 
275                             host_dev->hw_dev.num_msix_vecs);
276             
277             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
278                 if (request_irq(host_dev->hw_dev.msix_entries[i].vector, 
279                                 host_pci_msix_irq_handler, 
280                                 0, "V3VEE_host_PCI_MSIX", (void *)host_dev)) {
281                     printk("Error requesting IRQ %d for Passthrough MSIX IRQ\n", 
282                            host_dev->hw_dev.msix_entries[i].vector);
283                 }
284             }
285
286             break;
287         }
288
289         case HOST_PCI_CMD_MSIX_DISABLE: {
290             int i = 0;
291
292             printk("Passthrough PCI device Disabling MSIX\n");
293             
294             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
295                 disable_irq(host_dev->hw_dev.msix_entries[i].vector);
296             }
297
298             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
299                 free_irq(host_dev->hw_dev.msix_entries[i].vector, (void *)host_dev);
300             }
301
302             host_dev->hw_dev.num_msix_vecs = 0;
303             palacios_free(host_dev->hw_dev.msix_entries);
304
305             pci_disable_msix(dev);
306
307             break;
308         }
309         default:
310             printk("Error: unhandled passthrough PCI command: %d\n", cmd);
311             return -1;
312            
313     }
314
315     return 0;
316 }
317
318
319 static int hw_ack_irq(struct host_pci_device * host_dev, u32 vector) {
320     struct pci_dev * dev = host_dev->hw_dev.dev;
321     unsigned long flags;
322
323     //    printk("Acking IRQ vector %d\n", vector);
324
325     palacios_spinlock_lock_irqsave(&(host_dev->hw_dev.intx_lock), flags);
326     //    printk("Enabling IRQ %d\n", dev->irq);
327     enable_irq(dev->irq);
328     host_dev->hw_dev.intx_disabled = 0;
329     palacios_spinlock_unlock_irqrestore(&(host_dev->hw_dev.intx_lock), flags);
330     
331     return 0;
332 }
333
334
335
336
337 static int reserve_hw_pci_dev(struct host_pci_device * host_dev, void * v3_ctx) {
338     int ret = 0;
339     unsigned long flags;
340     struct v3_host_pci_dev * v3_dev = &(host_dev->v3_dev);
341     struct pci_dev * dev = host_dev->hw_dev.dev;
342
343     palacios_spinlock_lock_irqsave(&lock, flags);
344     if (host_dev->hw_dev.in_use == 0) {
345         host_dev->hw_dev.in_use = 1;
346     } else {
347         ret = -1;
348     }
349     palacios_spinlock_unlock_irqrestore(&lock, flags);
350
351
352     if (v3_dev->iface == IOMMU) {
353         struct v3_guest_mem_region region;
354         int flags = 0;
355         uintptr_t gpa = 0;
356
357         host_dev->hw_dev.iommu_domain = IOMMU_DOMAIN_ALLOC();
358
359         while (V3_get_guest_mem_region(v3_ctx, &region, gpa)) {
360         
361             printk("Memory region: start=%p, end=%p\n", (void *)region.start, (void *)region.end);
362
363
364             flags = IOMMU_READ | IOMMU_WRITE; // Need to see what IOMMU_CACHE means
365         
366             /* This version could be wrong */
367 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) 
368             // Guest VAs start at zero and go to end of memory
369             iommu_map_range(host_dev->hw_dev.iommu_domain, 0, region.start, (region.end - region.start), flags);
370 #else 
371             /* Linux actually made the interface worse... Now you can only map memory in powers of 2 (meant to only be pages...) */
372             {   
373                 u64 size = region.end - region.start;
374                 u32 page_size = 512 * 4096; // assume large 64bit pages (2MB)
375                 u64 hpa = region.start;
376
377                 do {
378                     if (size < page_size) {
379                         page_size = 4096; // less than a 2MB granularity, so we switch to small pages (4KB)
380                     }
381                     
382                     printk("Mapping IOMMU region gpa=%p hpa=%p (size=%d)\n", (void *)gpa, (void *)hpa, page_size);
383                     
384                     if (iommu_map(host_dev->hw_dev.iommu_domain, gpa, hpa, 
385                                   get_order(page_size), flags)) {
386                         printk("ERROR: Could not map sub region (GPA=%p) (HPA=%p) (order=%d)\n", 
387                                (void *)gpa, (void *)hpa, get_order(page_size));
388                         break;
389                     }
390                     
391                     hpa += page_size;
392                     gpa += page_size;
393                     
394                     size -= page_size;
395                 } while (size > 0);
396             }
397 #endif
398         }
399
400         if (iommu_attach_device(host_dev->hw_dev.iommu_domain, &(dev->dev))) {
401             printk("ERROR attaching host PCI device to IOMMU domain\n");
402         }
403
404     }
405
406
407     printk("Requesting Threaded IRQ handler for IRQ %d\n", dev->irq);
408     // setup regular IRQs until advanced IRQ mechanisms are enabled
409     if (request_threaded_irq(dev->irq, NULL, host_pci_intx_irq_handler, 
410                              IRQF_ONESHOT, "V3Vee_Host_PCI_INTx", (void *)host_dev)) {
411         printk("ERROR Could not assign IRQ to host PCI device (%s)\n", host_dev->name);
412     }
413
414
415
416     
417     return ret;
418 }
419
420
421
422 static int write_hw_pci_config(struct host_pci_device * host_dev, u32 reg, void * data, u32 length) {
423     struct pci_dev * dev = host_dev->hw_dev.dev;
424
425     if (reg < 64) {
426         return 0;
427     }
428         
429     if (length == 1) {
430         pci_write_config_byte(dev, reg, *(u8 *)data);
431     } else if (length == 2) {
432         pci_write_config_word(dev, reg, *(u16 *)data);
433     } else if (length == 4) {
434         pci_write_config_dword(dev, reg, *(u32 *)data);
435     } else {
436         printk("Invalid length of host PCI config update\n");
437         return -1;
438     }
439     
440     return 0;
441 }
442
443
444
445 static int read_hw_pci_config(struct host_pci_device * host_dev, u32 reg, void * data, u32 length) {
446     struct pci_dev * dev = host_dev->hw_dev.dev;
447
448         
449     if (length == 1) {
450         pci_read_config_byte(dev, reg, data);
451     } else if (length == 2) {
452         pci_read_config_word(dev, reg, data);
453     } else if (length == 4) {
454         pci_read_config_dword(dev, reg, data);
455     } else {
456         printk("Invalid length of host PCI config read\n");
457         return -1;
458     }
459
460
461     return 0; 
462 }
463
464
465 //
466 // Should be a matching teardown function here, otherwise we
467 // are at least leaking the lock from the lockchecker's perspective
468 // we would like to be able to do a palacios_spinlock_deinit() here...