diff options
author | Joerg Roedel <joerg.roedel@amd.com> | 2010-05-11 11:40:57 -0400 |
---|---|---|
committer | Joerg Roedel <joerg.roedel@amd.com> | 2010-05-11 11:40:57 -0400 |
commit | 795e74f7a69f9c08afa4fa7c86cc4f18a62bd630 (patch) | |
tree | 8448ece35101d8db945c49df50d0d5889687de9f /arch/x86/kernel/amd_iommu.c | |
parent | a52357259680fe5368c2fabf5949209e231f2aa2 (diff) | |
parent | 12c7389abe5786349d3ea6da1961cf78d0c1c7cd (diff) |
Merge branch 'iommu/largepages' into amd-iommu/2.6.35
Conflicts:
arch/x86/kernel/amd_iommu.c
Diffstat (limited to 'arch/x86/kernel/amd_iommu.c')
-rw-r--r-- | arch/x86/kernel/amd_iommu.c | 197 |
1 files changed, 126 insertions, 71 deletions
diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c index f854d89b7edf..fa5a1474cd18 100644 --- a/arch/x86/kernel/amd_iommu.c +++ b/arch/x86/kernel/amd_iommu.c | |||
@@ -731,18 +731,22 @@ static bool increase_address_space(struct protection_domain *domain, | |||
731 | 731 | ||
732 | static u64 *alloc_pte(struct protection_domain *domain, | 732 | static u64 *alloc_pte(struct protection_domain *domain, |
733 | unsigned long address, | 733 | unsigned long address, |
734 | int end_lvl, | 734 | unsigned long page_size, |
735 | u64 **pte_page, | 735 | u64 **pte_page, |
736 | gfp_t gfp) | 736 | gfp_t gfp) |
737 | { | 737 | { |
738 | int level, end_lvl; | ||
738 | u64 *pte, *page; | 739 | u64 *pte, *page; |
739 | int level; | 740 | |
741 | BUG_ON(!is_power_of_2(page_size)); | ||
740 | 742 | ||
741 | while (address > PM_LEVEL_SIZE(domain->mode)) | 743 | while (address > PM_LEVEL_SIZE(domain->mode)) |
742 | increase_address_space(domain, gfp); | 744 | increase_address_space(domain, gfp); |
743 | 745 | ||
744 | level = domain->mode - 1; | 746 | level = domain->mode - 1; |
745 | pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; | 747 | pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; |
748 | address = PAGE_SIZE_ALIGN(address, page_size); | ||
749 | end_lvl = PAGE_SIZE_LEVEL(page_size); | ||
746 | 750 | ||
747 | while (level > end_lvl) { | 751 | while (level > end_lvl) { |
748 | if (!IOMMU_PTE_PRESENT(*pte)) { | 752 | if (!IOMMU_PTE_PRESENT(*pte)) { |
@@ -752,6 +756,10 @@ static u64 *alloc_pte(struct protection_domain *domain, | |||
752 | *pte = PM_LEVEL_PDE(level, virt_to_phys(page)); | 756 | *pte = PM_LEVEL_PDE(level, virt_to_phys(page)); |
753 | } | 757 | } |
754 | 758 | ||
759 | /* No level skipping support yet */ | ||
760 | if (PM_PTE_LEVEL(*pte) != level) | ||
761 | return NULL; | ||
762 | |||
755 | level -= 1; | 763 | level -= 1; |
756 | 764 | ||
757 | pte = IOMMU_PTE_PAGE(*pte); | 765 | pte = IOMMU_PTE_PAGE(*pte); |
@@ -769,28 +777,47 @@ static u64 *alloc_pte(struct protection_domain *domain, | |||
769 | * This function checks if there is a PTE for a given dma address. If | 777 | * This function checks if there is a PTE for a given dma address. If |
770 | * there is one, it returns the pointer to it. | 778 | * there is one, it returns the pointer to it. |
771 | */ | 779 | */ |
772 | static u64 *fetch_pte(struct protection_domain *domain, | 780 | static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) |
773 | unsigned long address, int map_size) | ||
774 | { | 781 | { |
775 | int level; | 782 | int level; |
776 | u64 *pte; | 783 | u64 *pte; |
777 | 784 | ||
778 | level = domain->mode - 1; | 785 | if (address > PM_LEVEL_SIZE(domain->mode)) |
779 | pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; | 786 | return NULL; |
787 | |||
788 | level = domain->mode - 1; | ||
789 | pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; | ||
780 | 790 | ||
781 | while (level > map_size) { | 791 | while (level > 0) { |
792 | |||
793 | /* Not Present */ | ||
782 | if (!IOMMU_PTE_PRESENT(*pte)) | 794 | if (!IOMMU_PTE_PRESENT(*pte)) |
783 | return NULL; | 795 | return NULL; |
784 | 796 | ||
797 | /* Large PTE */ | ||
798 | if (PM_PTE_LEVEL(*pte) == 0x07) { | ||
799 | unsigned long pte_mask, __pte; | ||
800 | |||
801 | /* | ||
802 | * If we have a series of large PTEs, make | ||
803 | * sure to return a pointer to the first one. | ||
804 | */ | ||
805 | pte_mask = PTE_PAGE_SIZE(*pte); | ||
806 | pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1); | ||
807 | __pte = ((unsigned long)pte) & pte_mask; | ||
808 | |||
809 | return (u64 *)__pte; | ||
810 | } | ||
811 | |||
812 | /* No level skipping support yet */ | ||
813 | if (PM_PTE_LEVEL(*pte) != level) | ||
814 | return NULL; | ||
815 | |||
785 | level -= 1; | 816 | level -= 1; |
786 | 817 | ||
818 | /* Walk to the next level */ | ||
787 | pte = IOMMU_PTE_PAGE(*pte); | 819 | pte = IOMMU_PTE_PAGE(*pte); |
788 | pte = &pte[PM_LEVEL_INDEX(level, address)]; | 820 | pte = &pte[PM_LEVEL_INDEX(level, address)]; |
789 | |||
790 | if ((PM_PTE_LEVEL(*pte) == 0) && level != map_size) { | ||
791 | pte = NULL; | ||
792 | break; | ||
793 | } | ||
794 | } | 821 | } |
795 | 822 | ||
796 | return pte; | 823 | return pte; |
@@ -807,44 +834,84 @@ static int iommu_map_page(struct protection_domain *dom, | |||
807 | unsigned long bus_addr, | 834 | unsigned long bus_addr, |
808 | unsigned long phys_addr, | 835 | unsigned long phys_addr, |
809 | int prot, | 836 | int prot, |
810 | int map_size) | 837 | unsigned long page_size) |
811 | { | 838 | { |
812 | u64 __pte, *pte; | 839 | u64 __pte, *pte; |
813 | 840 | int i, count; | |
814 | bus_addr = PAGE_ALIGN(bus_addr); | ||
815 | phys_addr = PAGE_ALIGN(phys_addr); | ||
816 | |||
817 | BUG_ON(!PM_ALIGNED(map_size, bus_addr)); | ||
818 | BUG_ON(!PM_ALIGNED(map_size, phys_addr)); | ||
819 | 841 | ||
820 | if (!(prot & IOMMU_PROT_MASK)) | 842 | if (!(prot & IOMMU_PROT_MASK)) |
821 | return -EINVAL; | 843 | return -EINVAL; |
822 | 844 | ||
823 | pte = alloc_pte(dom, bus_addr, map_size, NULL, GFP_KERNEL); | 845 | bus_addr = PAGE_ALIGN(bus_addr); |
846 | phys_addr = PAGE_ALIGN(phys_addr); | ||
847 | count = PAGE_SIZE_PTE_COUNT(page_size); | ||
848 | pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL); | ||
849 | |||
850 | for (i = 0; i < count; ++i) | ||
851 | if (IOMMU_PTE_PRESENT(pte[i])) | ||
852 | return -EBUSY; | ||
824 | 853 | ||
825 | if (IOMMU_PTE_PRESENT(*pte)) | 854 | if (page_size > PAGE_SIZE) { |
826 | return -EBUSY; | 855 | __pte = PAGE_SIZE_PTE(phys_addr, page_size); |
856 | __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC; | ||
857 | } else | ||
858 | __pte = phys_addr | IOMMU_PTE_P | IOMMU_PTE_FC; | ||
827 | 859 | ||
828 | __pte = phys_addr | IOMMU_PTE_P; | ||
829 | if (prot & IOMMU_PROT_IR) | 860 | if (prot & IOMMU_PROT_IR) |
830 | __pte |= IOMMU_PTE_IR; | 861 | __pte |= IOMMU_PTE_IR; |
831 | if (prot & IOMMU_PROT_IW) | 862 | if (prot & IOMMU_PROT_IW) |
832 | __pte |= IOMMU_PTE_IW; | 863 | __pte |= IOMMU_PTE_IW; |
833 | 864 | ||
834 | *pte = __pte; | 865 | for (i = 0; i < count; ++i) |
866 | pte[i] = __pte; | ||
835 | 867 | ||
836 | update_domain(dom); | 868 | update_domain(dom); |
837 | 869 | ||
838 | return 0; | 870 | return 0; |
839 | } | 871 | } |
840 | 872 | ||
841 | static void iommu_unmap_page(struct protection_domain *dom, | 873 | static unsigned long iommu_unmap_page(struct protection_domain *dom, |
842 | unsigned long bus_addr, int map_size) | 874 | unsigned long bus_addr, |
875 | unsigned long page_size) | ||
843 | { | 876 | { |
844 | u64 *pte = fetch_pte(dom, bus_addr, map_size); | 877 | unsigned long long unmap_size, unmapped; |
878 | u64 *pte; | ||
879 | |||
880 | BUG_ON(!is_power_of_2(page_size)); | ||
881 | |||
882 | unmapped = 0; | ||
845 | 883 | ||
846 | if (pte) | 884 | while (unmapped < page_size) { |
847 | *pte = 0; | 885 | |
886 | pte = fetch_pte(dom, bus_addr); | ||
887 | |||
888 | if (!pte) { | ||
889 | /* | ||
890 | * No PTE for this address | ||
891 | * move forward in 4kb steps | ||
892 | */ | ||
893 | unmap_size = PAGE_SIZE; | ||
894 | } else if (PM_PTE_LEVEL(*pte) == 0) { | ||
895 | /* 4kb PTE found for this address */ | ||
896 | unmap_size = PAGE_SIZE; | ||
897 | *pte = 0ULL; | ||
898 | } else { | ||
899 | int count, i; | ||
900 | |||
901 | /* Large PTE found which maps this address */ | ||
902 | unmap_size = PTE_PAGE_SIZE(*pte); | ||
903 | count = PAGE_SIZE_PTE_COUNT(unmap_size); | ||
904 | for (i = 0; i < count; i++) | ||
905 | pte[i] = 0ULL; | ||
906 | } | ||
907 | |||
908 | bus_addr = (bus_addr & ~(unmap_size - 1)) + unmap_size; | ||
909 | unmapped += unmap_size; | ||
910 | } | ||
911 | |||
912 | BUG_ON(!is_power_of_2(unmapped)); | ||
913 | |||
914 | return unmapped; | ||
848 | } | 915 | } |
849 | 916 | ||
850 | /* | 917 | /* |
@@ -878,7 +945,7 @@ static int dma_ops_unity_map(struct dma_ops_domain *dma_dom, | |||
878 | for (addr = e->address_start; addr < e->address_end; | 945 | for (addr = e->address_start; addr < e->address_end; |
879 | addr += PAGE_SIZE) { | 946 | addr += PAGE_SIZE) { |
880 | ret = iommu_map_page(&dma_dom->domain, addr, addr, e->prot, | 947 | ret = iommu_map_page(&dma_dom->domain, addr, addr, e->prot, |
881 | PM_MAP_4k); | 948 | PAGE_SIZE); |
882 | if (ret) | 949 | if (ret) |
883 | return ret; | 950 | return ret; |
884 | /* | 951 | /* |
@@ -1006,7 +1073,7 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, | |||
1006 | u64 *pte, *pte_page; | 1073 | u64 *pte, *pte_page; |
1007 | 1074 | ||
1008 | for (i = 0; i < num_ptes; ++i) { | 1075 | for (i = 0; i < num_ptes; ++i) { |
1009 | pte = alloc_pte(&dma_dom->domain, address, PM_MAP_4k, | 1076 | pte = alloc_pte(&dma_dom->domain, address, PAGE_SIZE, |
1010 | &pte_page, gfp); | 1077 | &pte_page, gfp); |
1011 | if (!pte) | 1078 | if (!pte) |
1012 | goto out_free; | 1079 | goto out_free; |
@@ -1042,7 +1109,7 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, | |||
1042 | for (i = dma_dom->aperture[index]->offset; | 1109 | for (i = dma_dom->aperture[index]->offset; |
1043 | i < dma_dom->aperture_size; | 1110 | i < dma_dom->aperture_size; |
1044 | i += PAGE_SIZE) { | 1111 | i += PAGE_SIZE) { |
1045 | u64 *pte = fetch_pte(&dma_dom->domain, i, PM_MAP_4k); | 1112 | u64 *pte = fetch_pte(&dma_dom->domain, i); |
1046 | if (!pte || !IOMMU_PTE_PRESENT(*pte)) | 1113 | if (!pte || !IOMMU_PTE_PRESENT(*pte)) |
1047 | continue; | 1114 | continue; |
1048 | 1115 | ||
@@ -1712,7 +1779,7 @@ static u64* dma_ops_get_pte(struct dma_ops_domain *dom, | |||
1712 | 1779 | ||
1713 | pte = aperture->pte_pages[APERTURE_PAGE_INDEX(address)]; | 1780 | pte = aperture->pte_pages[APERTURE_PAGE_INDEX(address)]; |
1714 | if (!pte) { | 1781 | if (!pte) { |
1715 | pte = alloc_pte(&dom->domain, address, PM_MAP_4k, &pte_page, | 1782 | pte = alloc_pte(&dom->domain, address, PAGE_SIZE, &pte_page, |
1716 | GFP_ATOMIC); | 1783 | GFP_ATOMIC); |
1717 | aperture->pte_pages[APERTURE_PAGE_INDEX(address)] = pte_page; | 1784 | aperture->pte_pages[APERTURE_PAGE_INDEX(address)] = pte_page; |
1718 | } else | 1785 | } else |
@@ -2439,12 +2506,11 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, | |||
2439 | return ret; | 2506 | return ret; |
2440 | } | 2507 | } |
2441 | 2508 | ||
2442 | static int amd_iommu_map_range(struct iommu_domain *dom, | 2509 | static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova, |
2443 | unsigned long iova, phys_addr_t paddr, | 2510 | phys_addr_t paddr, int gfp_order, int iommu_prot) |
2444 | size_t size, int iommu_prot) | ||
2445 | { | 2511 | { |
2512 | unsigned long page_size = 0x1000UL << gfp_order; | ||
2446 | struct protection_domain *domain = dom->priv; | 2513 | struct protection_domain *domain = dom->priv; |
2447 | unsigned long i, npages = iommu_num_pages(paddr, size, PAGE_SIZE); | ||
2448 | int prot = 0; | 2514 | int prot = 0; |
2449 | int ret; | 2515 | int ret; |
2450 | 2516 | ||
@@ -2453,61 +2519,50 @@ static int amd_iommu_map_range(struct iommu_domain *dom, | |||
2453 | if (iommu_prot & IOMMU_WRITE) | 2519 | if (iommu_prot & IOMMU_WRITE) |
2454 | prot |= IOMMU_PROT_IW; | 2520 | prot |= IOMMU_PROT_IW; |
2455 | 2521 | ||
2456 | iova &= PAGE_MASK; | ||
2457 | paddr &= PAGE_MASK; | ||
2458 | |||
2459 | mutex_lock(&domain->api_lock); | 2522 | mutex_lock(&domain->api_lock); |
2460 | 2523 | ret = iommu_map_page(domain, iova, paddr, prot, page_size); | |
2461 | for (i = 0; i < npages; ++i) { | ||
2462 | ret = iommu_map_page(domain, iova, paddr, prot, PM_MAP_4k); | ||
2463 | if (ret) | ||
2464 | return ret; | ||
2465 | |||
2466 | iova += PAGE_SIZE; | ||
2467 | paddr += PAGE_SIZE; | ||
2468 | } | ||
2469 | |||
2470 | mutex_unlock(&domain->api_lock); | 2524 | mutex_unlock(&domain->api_lock); |
2471 | 2525 | ||
2472 | return 0; | 2526 | return ret; |
2473 | } | 2527 | } |
2474 | 2528 | ||
2475 | static void amd_iommu_unmap_range(struct iommu_domain *dom, | 2529 | static int amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, |
2476 | unsigned long iova, size_t size) | 2530 | int gfp_order) |
2477 | { | 2531 | { |
2478 | |||
2479 | struct protection_domain *domain = dom->priv; | 2532 | struct protection_domain *domain = dom->priv; |
2480 | unsigned long i, npages = iommu_num_pages(iova, size, PAGE_SIZE); | 2533 | unsigned long page_size, unmap_size; |
2481 | 2534 | ||
2482 | iova &= PAGE_MASK; | 2535 | page_size = 0x1000UL << gfp_order; |
2483 | 2536 | ||
2484 | mutex_lock(&domain->api_lock); | 2537 | mutex_lock(&domain->api_lock); |
2485 | 2538 | unmap_size = iommu_unmap_page(domain, iova, page_size); | |
2486 | for (i = 0; i < npages; ++i) { | 2539 | mutex_unlock(&domain->api_lock); |
2487 | iommu_unmap_page(domain, iova, PM_MAP_4k); | ||
2488 | iova += PAGE_SIZE; | ||
2489 | } | ||
2490 | 2540 | ||
2491 | iommu_flush_tlb_pde(domain); | 2541 | iommu_flush_tlb_pde(domain); |
2492 | 2542 | ||
2493 | mutex_unlock(&domain->api_lock); | 2543 | return get_order(unmap_size); |
2494 | } | 2544 | } |
2495 | 2545 | ||
2496 | static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, | 2546 | static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, |
2497 | unsigned long iova) | 2547 | unsigned long iova) |
2498 | { | 2548 | { |
2499 | struct protection_domain *domain = dom->priv; | 2549 | struct protection_domain *domain = dom->priv; |
2500 | unsigned long offset = iova & ~PAGE_MASK; | 2550 | unsigned long offset_mask; |
2501 | phys_addr_t paddr; | 2551 | phys_addr_t paddr; |
2502 | u64 *pte; | 2552 | u64 *pte, __pte; |
2503 | 2553 | ||
2504 | pte = fetch_pte(domain, iova, PM_MAP_4k); | 2554 | pte = fetch_pte(domain, iova); |
2505 | 2555 | ||
2506 | if (!pte || !IOMMU_PTE_PRESENT(*pte)) | 2556 | if (!pte || !IOMMU_PTE_PRESENT(*pte)) |
2507 | return 0; | 2557 | return 0; |
2508 | 2558 | ||
2509 | paddr = *pte & IOMMU_PAGE_MASK; | 2559 | if (PM_PTE_LEVEL(*pte) == 0) |
2510 | paddr |= offset; | 2560 | offset_mask = PAGE_SIZE - 1; |
2561 | else | ||
2562 | offset_mask = PTE_PAGE_SIZE(*pte) - 1; | ||
2563 | |||
2564 | __pte = *pte & PM_ADDR_MASK; | ||
2565 | paddr = (__pte & ~offset_mask) | (iova & offset_mask); | ||
2511 | 2566 | ||
2512 | return paddr; | 2567 | return paddr; |
2513 | } | 2568 | } |
@@ -2523,8 +2578,8 @@ static struct iommu_ops amd_iommu_ops = { | |||
2523 | .domain_destroy = amd_iommu_domain_destroy, | 2578 | .domain_destroy = amd_iommu_domain_destroy, |
2524 | .attach_dev = amd_iommu_attach_device, | 2579 | .attach_dev = amd_iommu_attach_device, |
2525 | .detach_dev = amd_iommu_detach_device, | 2580 | .detach_dev = amd_iommu_detach_device, |
2526 | .map = amd_iommu_map_range, | 2581 | .map = amd_iommu_map, |
2527 | .unmap = amd_iommu_unmap_range, | 2582 | .unmap = amd_iommu_unmap, |
2528 | .iova_to_phys = amd_iommu_iova_to_phys, | 2583 | .iova_to_phys = amd_iommu_iova_to_phys, |
2529 | .domain_has_cap = amd_iommu_domain_has_cap, | 2584 | .domain_has_cap = amd_iommu_domain_has_cap, |
2530 | }; | 2585 | }; |