diff options
| -rw-r--r-- | drivers/iommu/intel-iommu.c | 135 |
1 files changed, 60 insertions, 75 deletions
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index e3276ee60340..3df27c5d18a6 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
| @@ -182,32 +182,11 @@ static int force_on = 0; | |||
| 182 | * 64-127: Reserved | 182 | * 64-127: Reserved |
| 183 | */ | 183 | */ |
| 184 | struct root_entry { | 184 | struct root_entry { |
| 185 | u64 val; | 185 | u64 lo; |
| 186 | u64 rsvd1; | 186 | u64 hi; |
| 187 | }; | 187 | }; |
| 188 | #define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) | 188 | #define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) |
| 189 | static inline bool root_present(struct root_entry *root) | ||
| 190 | { | ||
| 191 | return (root->val & 1); | ||
| 192 | } | ||
| 193 | static inline void set_root_present(struct root_entry *root) | ||
| 194 | { | ||
| 195 | root->val |= 1; | ||
| 196 | } | ||
| 197 | static inline void set_root_value(struct root_entry *root, unsigned long value) | ||
| 198 | { | ||
| 199 | root->val &= ~VTD_PAGE_MASK; | ||
| 200 | root->val |= value & VTD_PAGE_MASK; | ||
| 201 | } | ||
| 202 | 189 | ||
| 203 | static inline struct context_entry * | ||
| 204 | get_context_addr_from_root(struct root_entry *root) | ||
| 205 | { | ||
| 206 | return (struct context_entry *) | ||
| 207 | (root_present(root)?phys_to_virt( | ||
| 208 | root->val & VTD_PAGE_MASK) : | ||
| 209 | NULL); | ||
| 210 | } | ||
| 211 | 190 | ||
| 212 | /* | 191 | /* |
| 213 | * low 64 bits: | 192 | * low 64 bits: |
| @@ -681,6 +660,40 @@ static void domain_update_iommu_cap(struct dmar_domain *domain) | |||
| 681 | domain->iommu_superpage = domain_update_iommu_superpage(NULL); | 660 | domain->iommu_superpage = domain_update_iommu_superpage(NULL); |
| 682 | } | 661 | } |
| 683 | 662 | ||
| 663 | static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu, | ||
| 664 | u8 bus, u8 devfn, int alloc) | ||
| 665 | { | ||
| 666 | struct root_entry *root = &iommu->root_entry[bus]; | ||
| 667 | struct context_entry *context; | ||
| 668 | u64 *entry; | ||
| 669 | |||
| 670 | if (ecap_ecs(iommu->ecap)) { | ||
| 671 | if (devfn >= 0x80) { | ||
| 672 | devfn -= 0x80; | ||
| 673 | entry = &root->hi; | ||
| 674 | } | ||
| 675 | devfn *= 2; | ||
| 676 | } | ||
| 677 | entry = &root->lo; | ||
| 678 | if (*entry & 1) | ||
| 679 | context = phys_to_virt(*entry & VTD_PAGE_MASK); | ||
| 680 | else { | ||
| 681 | unsigned long phy_addr; | ||
| 682 | if (!alloc) | ||
| 683 | return NULL; | ||
| 684 | |||
| 685 | context = alloc_pgtable_page(iommu->node); | ||
| 686 | if (!context) | ||
| 687 | return NULL; | ||
| 688 | |||
| 689 | __iommu_flush_cache(iommu, (void *)context, CONTEXT_SIZE); | ||
| 690 | phy_addr = virt_to_phys((void *)context); | ||
| 691 | *entry = phy_addr | 1; | ||
| 692 | __iommu_flush_cache(iommu, entry, sizeof(*entry)); | ||
| 693 | } | ||
| 694 | return &context[devfn]; | ||
| 695 | } | ||
| 696 | |||
| 684 | static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn) | 697 | static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn) |
| 685 | { | 698 | { |
| 686 | struct dmar_drhd_unit *drhd = NULL; | 699 | struct dmar_drhd_unit *drhd = NULL; |
| @@ -740,75 +753,36 @@ static void domain_flush_cache(struct dmar_domain *domain, | |||
| 740 | clflush_cache_range(addr, size); | 753 | clflush_cache_range(addr, size); |
| 741 | } | 754 | } |
| 742 | 755 | ||
| 743 | /* Gets context entry for a given bus and devfn */ | ||
| 744 | static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, | ||
| 745 | u8 bus, u8 devfn) | ||
| 746 | { | ||
| 747 | struct root_entry *root; | ||
| 748 | struct context_entry *context; | ||
| 749 | unsigned long phy_addr; | ||
| 750 | unsigned long flags; | ||
| 751 | |||
| 752 | spin_lock_irqsave(&iommu->lock, flags); | ||
| 753 | root = &iommu->root_entry[bus]; | ||
| 754 | context = get_context_addr_from_root(root); | ||
| 755 | if (!context) { | ||
| 756 | context = (struct context_entry *) | ||
| 757 | alloc_pgtable_page(iommu->node); | ||
| 758 | if (!context) { | ||
| 759 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
| 760 | return NULL; | ||
| 761 | } | ||
| 762 | __iommu_flush_cache(iommu, (void *)context, CONTEXT_SIZE); | ||
| 763 | phy_addr = virt_to_phys((void *)context); | ||
| 764 | set_root_value(root, phy_addr); | ||
| 765 | set_root_present(root); | ||
| 766 | __iommu_flush_cache(iommu, root, sizeof(*root)); | ||
| 767 | } | ||
| 768 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
| 769 | return &context[devfn]; | ||
| 770 | } | ||
| 771 | |||
| 772 | static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn) | 756 | static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn) |
| 773 | { | 757 | { |
| 774 | struct root_entry *root; | ||
| 775 | struct context_entry *context; | 758 | struct context_entry *context; |
| 776 | int ret; | 759 | int ret = 0; |
| 777 | unsigned long flags; | 760 | unsigned long flags; |
| 778 | 761 | ||
| 779 | spin_lock_irqsave(&iommu->lock, flags); | 762 | spin_lock_irqsave(&iommu->lock, flags); |
| 780 | root = &iommu->root_entry[bus]; | 763 | context = iommu_context_addr(iommu, bus, devfn, 0); |
| 781 | context = get_context_addr_from_root(root); | 764 | if (context) |
| 782 | if (!context) { | 765 | ret = context_present(context); |
| 783 | ret = 0; | ||
| 784 | goto out; | ||
| 785 | } | ||
| 786 | ret = context_present(&context[devfn]); | ||
| 787 | out: | ||
| 788 | spin_unlock_irqrestore(&iommu->lock, flags); | 766 | spin_unlock_irqrestore(&iommu->lock, flags); |
| 789 | return ret; | 767 | return ret; |
| 790 | } | 768 | } |
| 791 | 769 | ||
| 792 | static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn) | 770 | static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn) |
| 793 | { | 771 | { |
| 794 | struct root_entry *root; | ||
| 795 | struct context_entry *context; | 772 | struct context_entry *context; |
| 796 | unsigned long flags; | 773 | unsigned long flags; |
| 797 | 774 | ||
| 798 | spin_lock_irqsave(&iommu->lock, flags); | 775 | spin_lock_irqsave(&iommu->lock, flags); |
| 799 | root = &iommu->root_entry[bus]; | 776 | context = iommu_context_addr(iommu, bus, devfn, 0); |
| 800 | context = get_context_addr_from_root(root); | ||
| 801 | if (context) { | 777 | if (context) { |
| 802 | context_clear_entry(&context[devfn]); | 778 | context_clear_entry(context); |
| 803 | __iommu_flush_cache(iommu, &context[devfn], \ | 779 | __iommu_flush_cache(iommu, context, sizeof(*context)); |
| 804 | sizeof(*context)); | ||
| 805 | } | 780 | } |
| 806 | spin_unlock_irqrestore(&iommu->lock, flags); | 781 | spin_unlock_irqrestore(&iommu->lock, flags); |
| 807 | } | 782 | } |
| 808 | 783 | ||
| 809 | static void free_context_table(struct intel_iommu *iommu) | 784 | static void free_context_table(struct intel_iommu *iommu) |
| 810 | { | 785 | { |
| 811 | struct root_entry *root; | ||
| 812 | int i; | 786 | int i; |
| 813 | unsigned long flags; | 787 | unsigned long flags; |
| 814 | struct context_entry *context; | 788 | struct context_entry *context; |
| @@ -818,10 +792,17 @@ static void free_context_table(struct intel_iommu *iommu) | |||
| 818 | goto out; | 792 | goto out; |
| 819 | } | 793 | } |
| 820 | for (i = 0; i < ROOT_ENTRY_NR; i++) { | 794 | for (i = 0; i < ROOT_ENTRY_NR; i++) { |
| 821 | root = &iommu->root_entry[i]; | 795 | context = iommu_context_addr(iommu, i, 0, 0); |
| 822 | context = get_context_addr_from_root(root); | 796 | if (context) |
| 797 | free_pgtable_page(context); | ||
| 798 | |||
| 799 | if (!ecap_ecs(iommu->ecap)) | ||
| 800 | continue; | ||
| 801 | |||
| 802 | context = iommu_context_addr(iommu, i, 0x80, 0); | ||
| 823 | if (context) | 803 | if (context) |
| 824 | free_pgtable_page(context); | 804 | free_pgtable_page(context); |
| 805 | |||
| 825 | } | 806 | } |
| 826 | free_pgtable_page(iommu->root_entry); | 807 | free_pgtable_page(iommu->root_entry); |
| 827 | iommu->root_entry = NULL; | 808 | iommu->root_entry = NULL; |
| @@ -1145,14 +1126,16 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu) | |||
| 1145 | 1126 | ||
| 1146 | static void iommu_set_root_entry(struct intel_iommu *iommu) | 1127 | static void iommu_set_root_entry(struct intel_iommu *iommu) |
| 1147 | { | 1128 | { |
| 1148 | void *addr; | 1129 | u64 addr; |
| 1149 | u32 sts; | 1130 | u32 sts; |
| 1150 | unsigned long flag; | 1131 | unsigned long flag; |
| 1151 | 1132 | ||
| 1152 | addr = iommu->root_entry; | 1133 | addr = virt_to_phys(iommu->root_entry); |
| 1134 | if (ecap_ecs(iommu->ecap)) | ||
| 1135 | addr |= DMA_RTADDR_RTT; | ||
| 1153 | 1136 | ||
| 1154 | raw_spin_lock_irqsave(&iommu->register_lock, flag); | 1137 | raw_spin_lock_irqsave(&iommu->register_lock, flag); |
| 1155 | dmar_writeq(iommu->reg + DMAR_RTADDR_REG, virt_to_phys(addr)); | 1138 | dmar_writeq(iommu->reg + DMAR_RTADDR_REG, addr); |
| 1156 | 1139 | ||
| 1157 | writel(iommu->gcmd | DMA_GCMD_SRTP, iommu->reg + DMAR_GCMD_REG); | 1140 | writel(iommu->gcmd | DMA_GCMD_SRTP, iommu->reg + DMAR_GCMD_REG); |
| 1158 | 1141 | ||
| @@ -1798,7 +1781,9 @@ static int domain_context_mapping_one(struct dmar_domain *domain, | |||
| 1798 | BUG_ON(translation != CONTEXT_TT_PASS_THROUGH && | 1781 | BUG_ON(translation != CONTEXT_TT_PASS_THROUGH && |
| 1799 | translation != CONTEXT_TT_MULTI_LEVEL); | 1782 | translation != CONTEXT_TT_MULTI_LEVEL); |
| 1800 | 1783 | ||
| 1801 | context = device_to_context_entry(iommu, bus, devfn); | 1784 | spin_lock_irqsave(&iommu->lock, flags); |
| 1785 | context = iommu_context_addr(iommu, bus, devfn, 1); | ||
| 1786 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
| 1802 | if (!context) | 1787 | if (!context) |
| 1803 | return -ENOMEM; | 1788 | return -ENOMEM; |
| 1804 | spin_lock_irqsave(&iommu->lock, flags); | 1789 | spin_lock_irqsave(&iommu->lock, flags); |
