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.


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