Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/amd-iommu: Make iommu_unmap_page and fetch_pte aware of page sizes

This patch extends the functionality of iommu_unmap_page
and fetch_pte to support arbitrary page sizes.

Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>

+78 -18
+6
arch/x86/include/asm/amd_iommu_types.h
··· 200 200 (((address) | ((pagesize) - 1)) & \ 201 201 (~(pagesize >> 1)) & PM_ADDR_MASK) 202 202 203 + /* 204 + * Takes a PTE value with mode=0x07 and returns the page size it maps 205 + */ 206 + #define PTE_PAGE_SIZE(pte) \ 207 + (1ULL << (1 + ffz(((pte) | 0xfffULL)))) 208 + 203 209 #define IOMMU_PTE_P (1ULL << 0) 204 210 #define IOMMU_PTE_TV (1ULL << 1) 205 211 #define IOMMU_PTE_U (1ULL << 59)
+72 -18
arch/x86/kernel/amd_iommu.c
··· 776 776 * This function checks if there is a PTE for a given dma address. If 777 777 * there is one, it returns the pointer to it. 778 778 */ 779 - static u64 *fetch_pte(struct protection_domain *domain, 780 - unsigned long address, int map_size) 779 + static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) 781 780 { 782 781 int level; 783 782 u64 *pte; 784 783 785 - level = domain->mode - 1; 786 - pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; 784 + if (address > PM_LEVEL_SIZE(domain->mode)) 785 + return NULL; 787 786 788 - while (level > map_size) { 787 + level = domain->mode - 1; 788 + pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; 789 + 790 + while (level > 0) { 791 + 792 + /* Not Present */ 789 793 if (!IOMMU_PTE_PRESENT(*pte)) 794 + return NULL; 795 + 796 + /* Large PTE */ 797 + if (PM_PTE_LEVEL(*pte) == 0x07) { 798 + unsigned long pte_mask, __pte; 799 + 800 + /* 801 + * If we have a series of large PTEs, make 802 + * sure to return a pointer to the first one. 803 + */ 804 + pte_mask = PTE_PAGE_SIZE(*pte); 805 + pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1); 806 + __pte = ((unsigned long)pte) & pte_mask; 807 + 808 + return (u64 *)__pte; 809 + } 810 + 811 + /* No level skipping support yet */ 812 + if (PM_PTE_LEVEL(*pte) != level) 790 813 return NULL; 791 814 792 815 level -= 1; 793 816 817 + /* Walk to the next level */ 794 818 pte = IOMMU_PTE_PAGE(*pte); 795 819 pte = &pte[PM_LEVEL_INDEX(level, address)]; 796 - 797 - if ((PM_PTE_LEVEL(*pte) == 0) && level != map_size) { 798 - pte = NULL; 799 - break; 800 - } 801 820 } 802 821 803 822 return pte; ··· 869 850 return 0; 870 851 } 871 852 872 - static void iommu_unmap_page(struct protection_domain *dom, 873 - unsigned long bus_addr, int map_size) 853 + static unsigned long iommu_unmap_page(struct protection_domain *dom, 854 + unsigned long bus_addr, 855 + unsigned long page_size) 874 856 { 875 - u64 *pte = fetch_pte(dom, bus_addr, map_size); 857 + unsigned long long unmap_size, unmapped; 858 + u64 *pte; 876 859 877 - if (pte) 878 - *pte = 0; 860 + BUG_ON(!is_power_of_2(page_size)); 861 + 862 + unmapped = 0; 863 + 864 + while (unmapped < page_size) { 865 + 866 + pte = fetch_pte(dom, bus_addr); 867 + 868 + if (!pte) { 869 + /* 870 + * No PTE for this address 871 + * move forward in 4kb steps 872 + */ 873 + unmap_size = PAGE_SIZE; 874 + } else if (PM_PTE_LEVEL(*pte) == 0) { 875 + /* 4kb PTE found for this address */ 876 + unmap_size = PAGE_SIZE; 877 + *pte = 0ULL; 878 + } else { 879 + int count, i; 880 + 881 + /* Large PTE found which maps this address */ 882 + unmap_size = PTE_PAGE_SIZE(*pte); 883 + count = PAGE_SIZE_PTE_COUNT(unmap_size); 884 + for (i = 0; i < count; i++) 885 + pte[i] = 0ULL; 886 + } 887 + 888 + bus_addr = (bus_addr & ~(unmap_size - 1)) + unmap_size; 889 + unmapped += unmap_size; 890 + } 891 + 892 + BUG_ON(!is_power_of_2(unmapped)); 893 + 894 + return unmapped; 879 895 } 880 896 881 897 /* ··· 1108 1054 for (i = dma_dom->aperture[index]->offset; 1109 1055 i < dma_dom->aperture_size; 1110 1056 i += PAGE_SIZE) { 1111 - u64 *pte = fetch_pte(&dma_dom->domain, i, PM_MAP_4k); 1057 + u64 *pte = fetch_pte(&dma_dom->domain, i); 1112 1058 if (!pte || !IOMMU_PTE_PRESENT(*pte)) 1113 1059 continue; 1114 1060 ··· 2545 2491 iova &= PAGE_MASK; 2546 2492 2547 2493 for (i = 0; i < npages; ++i) { 2548 - iommu_unmap_page(domain, iova, PM_MAP_4k); 2494 + iommu_unmap_page(domain, iova, PAGE_SIZE); 2549 2495 iova += PAGE_SIZE; 2550 2496 } 2551 2497 ··· 2560 2506 phys_addr_t paddr; 2561 2507 u64 *pte; 2562 2508 2563 - pte = fetch_pte(domain, iova, PM_MAP_4k); 2509 + pte = fetch_pte(domain, iova); 2564 2510 2565 2511 if (!pte || !IOMMU_PTE_PRESENT(*pte)) 2566 2512 return 0;