aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/iommu/intel-iommu.c135
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 */
184struct root_entry { 184struct 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))
189static inline bool root_present(struct root_entry *root)
190{
191 return (root->val & 1);
192}
193static inline void set_root_present(struct root_entry *root)
194{
195 root->val |= 1;
196}
197static 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
203static inline struct context_entry *
204get_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
663static 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
684static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn) 697static 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 */
744static 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
772static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn) 756static 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]);
787out:
788 spin_unlock_irqrestore(&iommu->lock, flags); 766 spin_unlock_irqrestore(&iommu->lock, flags);
789 return ret; 767 return ret;
790} 768}
791 769
792static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn) 770static 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
809static void free_context_table(struct intel_iommu *iommu) 784static 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
1146static void iommu_set_root_entry(struct intel_iommu *iommu) 1127static 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);