diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-07 23:12:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-07 23:12:15 -0400 |
commit | a68a7509d3af8ee458d32b2416b0c2aaf2a4a7e3 (patch) | |
tree | a0e6a40ff4c384364d9eca02d490eab7dca21eee | |
parent | 639b4ac691c6f6e48921dc576379c176f82f3250 (diff) | |
parent | fd49c81f080a997aad4b0e73541cd42772b6a772 (diff) |
Merge tag 'vfio-v3.16-rc1' of git://github.com/awilliam/linux-vfio into next
Pull VFIO updates from Alex Williamson:
"A handful of VFIO bug fixes for v3.16"
* tag 'vfio-v3.16-rc1' of git://github.com/awilliam/linux-vfio:
drivers/vfio/pci: Fix wrong MSI interrupt count
drivers/vfio: Rework offsetofend()
vfio/iommu_type1: Avoid overflow
vfio/pci: Fix unchecked return value
vfio/pci: Fix sizing of DPA and THP express capabilities
-rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 6 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_config.c | 7 | ||||
-rw-r--r-- | drivers/vfio/vfio_iommu_type1.c | 45 | ||||
-rw-r--r-- | include/linux/vfio.h | 5 |
4 files changed, 26 insertions, 37 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 7ba042498857..010e0f8b8e4f 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
@@ -57,7 +57,8 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
57 | 57 | ||
58 | ret = vfio_config_init(vdev); | 58 | ret = vfio_config_init(vdev); |
59 | if (ret) { | 59 | if (ret) { |
60 | pci_load_and_free_saved_state(pdev, &vdev->pci_saved_state); | 60 | kfree(vdev->pci_saved_state); |
61 | vdev->pci_saved_state = NULL; | ||
61 | pci_disable_device(pdev); | 62 | pci_disable_device(pdev); |
62 | return ret; | 63 | return ret; |
63 | } | 64 | } |
@@ -196,8 +197,7 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) | |||
196 | if (pos) { | 197 | if (pos) { |
197 | pci_read_config_word(vdev->pdev, | 198 | pci_read_config_word(vdev->pdev, |
198 | pos + PCI_MSI_FLAGS, &flags); | 199 | pos + PCI_MSI_FLAGS, &flags); |
199 | 200 | return 1 << ((flags & PCI_MSI_FLAGS_QMASK) >> 1); | |
200 | return 1 << (flags & PCI_MSI_FLAGS_QMASK); | ||
201 | } | 201 | } |
202 | } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) { | 202 | } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) { |
203 | u8 pos; | 203 | u8 pos; |
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 83cd1574c810..e50790e91f76 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c | |||
@@ -1126,8 +1126,7 @@ static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos) | |||
1126 | return pcibios_err_to_errno(ret); | 1126 | return pcibios_err_to_errno(ret); |
1127 | 1127 | ||
1128 | byte &= PCI_DPA_CAP_SUBSTATE_MASK; | 1128 | byte &= PCI_DPA_CAP_SUBSTATE_MASK; |
1129 | byte = round_up(byte + 1, 4); | 1129 | return PCI_DPA_BASE_SIZEOF + byte + 1; |
1130 | return PCI_DPA_BASE_SIZEOF + byte; | ||
1131 | case PCI_EXT_CAP_ID_TPH: | 1130 | case PCI_EXT_CAP_ID_TPH: |
1132 | ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword); | 1131 | ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword); |
1133 | if (ret) | 1132 | if (ret) |
@@ -1136,9 +1135,9 @@ static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos) | |||
1136 | if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) { | 1135 | if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) { |
1137 | int sts; | 1136 | int sts; |
1138 | 1137 | ||
1139 | sts = byte & PCI_TPH_CAP_ST_MASK; | 1138 | sts = dword & PCI_TPH_CAP_ST_MASK; |
1140 | sts >>= PCI_TPH_CAP_ST_SHIFT; | 1139 | sts >>= PCI_TPH_CAP_ST_SHIFT; |
1141 | return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4); | 1140 | return PCI_TPH_BASE_SIZEOF + (sts * 2) + 2; |
1142 | } | 1141 | } |
1143 | return PCI_TPH_BASE_SIZEOF; | 1142 | return PCI_TPH_BASE_SIZEOF; |
1144 | default: | 1143 | default: |
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 6673e7be507f..0734fbe5b651 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c | |||
@@ -524,7 +524,7 @@ unwind: | |||
524 | static int vfio_dma_do_map(struct vfio_iommu *iommu, | 524 | static int vfio_dma_do_map(struct vfio_iommu *iommu, |
525 | struct vfio_iommu_type1_dma_map *map) | 525 | struct vfio_iommu_type1_dma_map *map) |
526 | { | 526 | { |
527 | dma_addr_t end, iova; | 527 | dma_addr_t iova = map->iova; |
528 | unsigned long vaddr = map->vaddr; | 528 | unsigned long vaddr = map->vaddr; |
529 | size_t size = map->size; | 529 | size_t size = map->size; |
530 | long npage; | 530 | long npage; |
@@ -533,39 +533,30 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, | |||
533 | struct vfio_dma *dma; | 533 | struct vfio_dma *dma; |
534 | unsigned long pfn; | 534 | unsigned long pfn; |
535 | 535 | ||
536 | end = map->iova + map->size; | 536 | /* Verify that none of our __u64 fields overflow */ |
537 | if (map->size != size || map->vaddr != vaddr || map->iova != iova) | ||
538 | return -EINVAL; | ||
537 | 539 | ||
538 | mask = ((uint64_t)1 << __ffs(vfio_pgsize_bitmap(iommu))) - 1; | 540 | mask = ((uint64_t)1 << __ffs(vfio_pgsize_bitmap(iommu))) - 1; |
539 | 541 | ||
542 | WARN_ON(mask & PAGE_MASK); | ||
543 | |||
540 | /* READ/WRITE from device perspective */ | 544 | /* READ/WRITE from device perspective */ |
541 | if (map->flags & VFIO_DMA_MAP_FLAG_WRITE) | 545 | if (map->flags & VFIO_DMA_MAP_FLAG_WRITE) |
542 | prot |= IOMMU_WRITE; | 546 | prot |= IOMMU_WRITE; |
543 | if (map->flags & VFIO_DMA_MAP_FLAG_READ) | 547 | if (map->flags & VFIO_DMA_MAP_FLAG_READ) |
544 | prot |= IOMMU_READ; | 548 | prot |= IOMMU_READ; |
545 | 549 | ||
546 | if (!prot) | 550 | if (!prot || !size || (size | iova | vaddr) & mask) |
547 | return -EINVAL; /* No READ/WRITE? */ | ||
548 | |||
549 | if (vaddr & mask) | ||
550 | return -EINVAL; | ||
551 | if (map->iova & mask) | ||
552 | return -EINVAL; | ||
553 | if (!map->size || map->size & mask) | ||
554 | return -EINVAL; | ||
555 | |||
556 | WARN_ON(mask & PAGE_MASK); | ||
557 | |||
558 | /* Don't allow IOVA wrap */ | ||
559 | if (end && end < map->iova) | ||
560 | return -EINVAL; | 551 | return -EINVAL; |
561 | 552 | ||
562 | /* Don't allow virtual address wrap */ | 553 | /* Don't allow IOVA or virtual address wrap */ |
563 | if (vaddr + map->size && vaddr + map->size < vaddr) | 554 | if (iova + size - 1 < iova || vaddr + size - 1 < vaddr) |
564 | return -EINVAL; | 555 | return -EINVAL; |
565 | 556 | ||
566 | mutex_lock(&iommu->lock); | 557 | mutex_lock(&iommu->lock); |
567 | 558 | ||
568 | if (vfio_find_dma(iommu, map->iova, map->size)) { | 559 | if (vfio_find_dma(iommu, iova, size)) { |
569 | mutex_unlock(&iommu->lock); | 560 | mutex_unlock(&iommu->lock); |
570 | return -EEXIST; | 561 | return -EEXIST; |
571 | } | 562 | } |
@@ -576,17 +567,17 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, | |||
576 | return -ENOMEM; | 567 | return -ENOMEM; |
577 | } | 568 | } |
578 | 569 | ||
579 | dma->iova = map->iova; | 570 | dma->iova = iova; |
580 | dma->vaddr = map->vaddr; | 571 | dma->vaddr = vaddr; |
581 | dma->prot = prot; | 572 | dma->prot = prot; |
582 | 573 | ||
583 | /* Insert zero-sized and grow as we map chunks of it */ | 574 | /* Insert zero-sized and grow as we map chunks of it */ |
584 | vfio_link_dma(iommu, dma); | 575 | vfio_link_dma(iommu, dma); |
585 | 576 | ||
586 | for (iova = map->iova; iova < end; iova += size, vaddr += size) { | 577 | while (size) { |
587 | /* Pin a contiguous chunk of memory */ | 578 | /* Pin a contiguous chunk of memory */ |
588 | npage = vfio_pin_pages(vaddr, (end - iova) >> PAGE_SHIFT, | 579 | npage = vfio_pin_pages(vaddr + dma->size, |
589 | prot, &pfn); | 580 | size >> PAGE_SHIFT, prot, &pfn); |
590 | if (npage <= 0) { | 581 | if (npage <= 0) { |
591 | WARN_ON(!npage); | 582 | WARN_ON(!npage); |
592 | ret = (int)npage; | 583 | ret = (int)npage; |
@@ -594,14 +585,14 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, | |||
594 | } | 585 | } |
595 | 586 | ||
596 | /* Map it! */ | 587 | /* Map it! */ |
597 | ret = vfio_iommu_map(iommu, iova, pfn, npage, prot); | 588 | ret = vfio_iommu_map(iommu, iova + dma->size, pfn, npage, prot); |
598 | if (ret) { | 589 | if (ret) { |
599 | vfio_unpin_pages(pfn, npage, prot, true); | 590 | vfio_unpin_pages(pfn, npage, prot, true); |
600 | break; | 591 | break; |
601 | } | 592 | } |
602 | 593 | ||
603 | size = npage << PAGE_SHIFT; | 594 | size -= npage << PAGE_SHIFT; |
604 | dma->size += size; | 595 | dma->size += npage << PAGE_SHIFT; |
605 | } | 596 | } |
606 | 597 | ||
607 | if (ret) | 598 | if (ret) |
diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 81022a52bc34..8ec980b5e3af 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h | |||
@@ -86,9 +86,8 @@ extern void vfio_unregister_iommu_driver( | |||
86 | * from user space. This allows us to easily determine if the provided | 86 | * from user space. This allows us to easily determine if the provided |
87 | * structure is sized to include various fields. | 87 | * structure is sized to include various fields. |
88 | */ | 88 | */ |
89 | #define offsetofend(TYPE, MEMBER) ({ \ | 89 | #define offsetofend(TYPE, MEMBER) \ |
90 | TYPE tmp; \ | 90 | (offsetof(TYPE, MEMBER) + sizeof(((TYPE *)0)->MEMBER)) |
91 | offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); }) \ | ||
92 | 91 | ||
93 | /* | 92 | /* |
94 | * External user API | 93 | * External user API |