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.


Cleanup and sanity-checking of OOB accesses and pointer-to-local issues (Coverity...
[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     palacios_spinlock_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                     // this should never happen with i==5, but it
82                     // is technically an OOB access without the modulo
83                     struct v3_host_pci_bar * hi_bar = &(v3_dev->bars[(i + 1) % 6]); 
84             
85                     bar->type = PT_BAR_MEM64_LO;
86
87                     hi_bar->type = PT_BAR_MEM64_HI;
88                     hi_bar->size = bar->size;
89                     hi_bar->addr = bar->addr;
90                     hi_bar->cacheable = ((flags & IORESOURCE_CACHEABLE) != 0);
91                     hi_bar->prefetchable = ((flags & IORESOURCE_PREFETCH) != 0);
92                     
93                     i++;
94                 } else if (flags & IORESOURCE_DMA) {
95                     bar->type = PT_BAR_MEM24;
96                 } else {
97                     bar->type = PT_BAR_MEM32;
98                 }
99                 
100                 bar->cacheable = ((flags & IORESOURCE_CACHEABLE) != 0);
101                 bar->prefetchable = ((flags & IORESOURCE_PREFETCH) != 0);
102
103             } else {
104                 bar->type = PT_BAR_NONE;
105             }
106         }
107     }
108
109     /* Cache expansion rom bar */
110     {
111         struct resource * rom_res = &(dev->resource[PCI_ROM_RESOURCE]);
112         int rom_size = pci_resource_len(dev, PCI_ROM_RESOURCE);
113
114         if (rom_size > 0) {
115           //unsigned long flags;
116
117             v3_dev->exp_rom.size = rom_size;
118             v3_dev->exp_rom.addr = pci_resource_start(dev, PCI_ROM_RESOURCE);
119             // flags = pci_resource_flags(dev, PCI_ROM_RESOURCE); 
120
121             v3_dev->exp_rom.type = PT_EXP_ROM;
122
123             v3_dev->exp_rom.exp_rom_enabled = rom_res->flags & IORESOURCE_ROM_ENABLE;
124         }
125     }
126
127     /* Cache entire configuration space */
128     {
129         int m = 0;
130
131         // Copy the configuration space to the local cached version
132         for (m = 0; m < PCI_HDR_SIZE; m += 4) {
133             pci_read_config_dword(dev, m, (u32 *)&(v3_dev->cfg_space[m]));
134         }
135     }
136
137
138     /* HARDCODED for now but this will need to depend on IOMMU support detection */
139     if (iommu_found()) {
140         printk("Setting host PCI device (%s) as IOMMU\n", host_dev->name);
141         v3_dev->iface = IOMMU;
142     } else {
143         printk("Setting host PCI device (%s) as SYMBIOTIC\n", host_dev->name);
144         v3_dev->iface = SYMBIOTIC;
145     }
146
147     return 0;
148
149 }
150
151
152
153 static irqreturn_t host_pci_intx_irq_handler(int irq, void * priv_data) {
154     struct host_pci_device * host_dev = priv_data;
155
156     //   printk("Host PCI IRQ handler (%d)\n", irq);
157
158     palacios_spinlock_lock(&(host_dev->hw_dev.intx_lock));
159     disable_irq_nosync(irq);
160     host_dev->hw_dev.intx_disabled = 1;
161     palacios_spinlock_unlock(&(host_dev->hw_dev.intx_lock));
162
163     V3_host_pci_raise_irq(&(host_dev->v3_dev), 0);
164
165     return IRQ_HANDLED;
166 }
167
168
169
170 static irqreturn_t host_pci_msi_irq_handler(int irq, void * priv_data) {
171     struct host_pci_device * host_dev = priv_data;
172     //    printk("Host PCI MSI IRQ Handler (%d)\n", irq);
173
174     V3_host_pci_raise_irq(&(host_dev->v3_dev), 0);
175
176     return IRQ_HANDLED;
177 }
178
179 static irqreturn_t host_pci_msix_irq_handler(int irq, void * priv_data) {
180     struct host_pci_device * host_dev = priv_data;
181     int i = 0;
182     
183     //    printk("Host PCI MSIX IRQ Handler (%d)\n", irq);
184     
185     // find vector index
186     for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
187         if (irq == host_dev->hw_dev.msix_entries[i].vector) {
188             V3_host_pci_raise_irq(&(host_dev->v3_dev), i);
189         } else {
190             printk("Error Could not find matching MSIX vector for IRQ %d\n", irq);
191         }
192     }    
193     return IRQ_HANDLED;
194 }
195
196
197 static int hw_pci_cmd(struct host_pci_device * host_dev, host_pci_cmd_t cmd, u64 arg) {
198     //struct v3_host_pci_dev * v3_dev = &(host_dev->v3_dev);
199     struct pci_dev * dev = host_dev->hw_dev.dev;
200
201     switch (cmd) {
202         case HOST_PCI_CMD_DMA_DISABLE:
203             printk("Passthrough PCI device disabling BMDMA\n");
204             pci_clear_master(host_dev->hw_dev.dev);
205             break;
206         case HOST_PCI_CMD_DMA_ENABLE:
207             printk("Passthrough PCI device Enabling BMDMA\n");
208             pci_set_master(host_dev->hw_dev.dev);
209             break;
210
211         case HOST_PCI_CMD_INTX_DISABLE:
212             printk("Passthrough PCI device disabling INTx IRQ\n");
213
214             disable_irq(dev->irq);
215             free_irq(dev->irq, (void *)host_dev);
216
217             break;
218         case HOST_PCI_CMD_INTX_ENABLE:
219             printk("Passthrough PCI device Enabling INTx IRQ\n");
220         
221             if (request_threaded_irq(dev->irq, NULL, host_pci_intx_irq_handler, 
222                                      IRQF_ONESHOT, "V3Vee_Host_PCI_INTx", (void *)host_dev)) {
223                 printk("ERROR Could not assign IRQ to host PCI device (%s)\n", host_dev->name);
224             }
225
226             break;
227
228         case HOST_PCI_CMD_MSI_DISABLE:
229             printk("Passthrough PCI device Disabling MSIs\n");
230
231             disable_irq(dev->irq);
232             free_irq(dev->irq, (void *)host_dev);
233
234             pci_disable_msi(dev);
235
236             break;
237         case HOST_PCI_CMD_MSI_ENABLE:
238             printk("Passthrough PCI device Enabling MSI\n");
239             
240             if (!dev->msi_enabled) {
241                 pci_enable_msi(dev);
242
243                 if (request_irq(dev->irq, host_pci_msi_irq_handler, 
244                                 0, "V3Vee_host_PCI_MSI", (void *)host_dev)) {
245                     printk("Error Requesting IRQ %d for Passthrough MSI IRQ\n", dev->irq);
246                 }
247             }
248
249             break;
250
251
252
253         case HOST_PCI_CMD_MSIX_ENABLE: {
254             int i = 0;
255             
256             printk("Passthrough PCI device Enabling MSIX\n");
257             host_dev->hw_dev.num_msix_vecs = arg;;
258             host_dev->hw_dev.msix_entries = kcalloc(host_dev->hw_dev.num_msix_vecs, 
259                                                     sizeof(struct msix_entry), GFP_KERNEL);
260             
261             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
262                 host_dev->hw_dev.msix_entries[i].entry = i;
263             }
264             
265             pci_enable_msix(dev, host_dev->hw_dev.msix_entries, 
266                             host_dev->hw_dev.num_msix_vecs);
267             
268             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
269                 if (request_irq(host_dev->hw_dev.msix_entries[i].vector, 
270                                 host_pci_msix_irq_handler, 
271                                 0, "V3VEE_host_PCI_MSIX", (void *)host_dev)) {
272                     printk("Error requesting IRQ %d for Passthrough MSIX IRQ\n", 
273                            host_dev->hw_dev.msix_entries[i].vector);
274                 }
275             }
276
277             break;
278         }
279
280         case HOST_PCI_CMD_MSIX_DISABLE: {
281             int i = 0;
282
283             printk("Passthrough PCI device Disabling MSIX\n");
284             
285             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
286                 disable_irq(host_dev->hw_dev.msix_entries[i].vector);
287             }
288
289             for (i = 0; i < host_dev->hw_dev.num_msix_vecs; i++) {
290                 free_irq(host_dev->hw_dev.msix_entries[i].vector, (void *)host_dev);
291             }
292
293             host_dev->hw_dev.num_msix_vecs = 0;
294             palacios_free(host_dev->hw_dev.msix_entries);
295
296             pci_disable_msix(dev);
297
298             break;
299         }
300         default:
301             printk("Error: unhandled passthrough PCI command: %d\n", cmd);
302             return -1;
303            
304     }
305
306     return 0;
307 }
308
309
310 static int hw_ack_irq(struct host_pci_device * host_dev, u32 vector) {
311     struct pci_dev * dev = host_dev->hw_dev.dev;
312     unsigned long flags;
313
314     //    printk("Acking IRQ vector %d\n", vector);
315
316     palacios_spinlock_lock_irqsave(&(host_dev->hw_dev.intx_lock), flags);
317     //    printk("Enabling IRQ %d\n", dev->irq);
318     enable_irq(dev->irq);
319     host_dev->hw_dev.intx_disabled = 0;
320     palacios_spinlock_unlock_irqrestore(&(host_dev->hw_dev.intx_lock), flags);
321     
322     return 0;
323 }
324
325
326
327
328 static int reserve_hw_pci_dev(struct host_pci_device * host_dev, void * v3_ctx) {
329     int ret = 0;
330     unsigned long flags;
331     struct v3_host_pci_dev * v3_dev = &(host_dev->v3_dev);
332     struct pci_dev * dev = host_dev->hw_dev.dev;
333
334     palacios_spinlock_lock_irqsave(&lock, flags);
335     if (host_dev->hw_dev.in_use == 0) {
336         host_dev->hw_dev.in_use = 1;
337     } else {
338         ret = -1;
339     }
340     palacios_spinlock_unlock_irqrestore(&lock, flags);
341
342
343     if (v3_dev->iface == IOMMU) {
344         struct v3_guest_mem_region region;
345         int flags = 0;
346         uintptr_t gpa = 0;
347
348         host_dev->hw_dev.iommu_domain = iommu_domain_alloc();
349
350         while (V3_get_guest_mem_region(v3_ctx, &region, gpa)) {
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 hpa = region.start;
367
368                 do {
369                     if (size < page_size) {
370                         page_size = 4096; // less than a 2MB granularity, so we switch to small pages (4KB)
371                     }
372                     
373                     printk("Mapping IOMMU region gpa=%p hpa=%p (size=%d)\n", (void *)gpa, (void *)hpa, page_size);
374                     
375                     if (iommu_map(host_dev->hw_dev.iommu_domain, gpa, hpa, 
376                                   get_order(page_size), flags)) {
377                         printk("ERROR: Could not map sub region (GPA=%p) (HPA=%p) (order=%d)\n", 
378                                (void *)gpa, (void *)hpa, get_order(page_size));
379                         break;
380                     }
381                     
382                     hpa += page_size;
383                     gpa += page_size;
384                     
385                     size -= page_size;
386                 } while (size > 0);
387             }
388 #endif
389         }
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 }
454
455
456 //
457 // Should be a matching teardown function here, otherwise we
458 // are at least leaking the lock from the lockchecker's perspective
459 // we would like to be able to do a palacios_spinlock_deinit() here...