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.


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