aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/iommu/amd_iommu.c130
-rw-r--r--drivers/iommu/amd_iommu_proto.h4
-rw-r--r--drivers/iommu/amd_iommu_types.h1
3 files changed, 135 insertions, 0 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index b0861739a27d..65a118ce626e 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1637,8 +1637,45 @@ static void free_pagetable(struct protection_domain *domain)
1637 domain->pt_root = NULL; 1637 domain->pt_root = NULL;
1638} 1638}
1639 1639
1640static void free_gcr3_tbl_level1(u64 *tbl)
1641{
1642 u64 *ptr;
1643 int i;
1644
1645 for (i = 0; i < 512; ++i) {
1646 if (!(tbl[i] & GCR3_VALID))
1647 continue;
1648
1649 ptr = __va(tbl[i] & PAGE_MASK);
1650
1651 free_page((unsigned long)ptr);
1652 }
1653}
1654
1655static void free_gcr3_tbl_level2(u64 *tbl)
1656{
1657 u64 *ptr;
1658 int i;
1659
1660 for (i = 0; i < 512; ++i) {
1661 if (!(tbl[i] & GCR3_VALID))
1662 continue;
1663
1664 ptr = __va(tbl[i] & PAGE_MASK);
1665
1666 free_gcr3_tbl_level1(ptr);
1667 }
1668}
1669
1640static void free_gcr3_table(struct protection_domain *domain) 1670static void free_gcr3_table(struct protection_domain *domain)
1641{ 1671{
1672 if (domain->glx == 2)
1673 free_gcr3_tbl_level2(domain->gcr3_tbl);
1674 else if (domain->glx == 1)
1675 free_gcr3_tbl_level1(domain->gcr3_tbl);
1676 else if (domain->glx != 0)
1677 BUG();
1678
1642 free_page((unsigned long)domain->gcr3_tbl); 1679 free_page((unsigned long)domain->gcr3_tbl);
1643} 1680}
1644 1681
@@ -3282,3 +3319,96 @@ int amd_iommu_flush_tlb(struct iommu_domain *dom, int pasid)
3282} 3319}
3283EXPORT_SYMBOL(amd_iommu_flush_tlb); 3320EXPORT_SYMBOL(amd_iommu_flush_tlb);
3284 3321
3322static u64 *__get_gcr3_pte(u64 *root, int level, int pasid, bool alloc)
3323{
3324 int index;
3325 u64 *pte;
3326
3327 while (true) {
3328
3329 index = (pasid >> (9 * level)) & 0x1ff;
3330 pte = &root[index];
3331
3332 if (level == 0)
3333 break;
3334
3335 if (!(*pte & GCR3_VALID)) {
3336 if (!alloc)
3337 return NULL;
3338
3339 root = (void *)get_zeroed_page(GFP_ATOMIC);
3340 if (root == NULL)
3341 return NULL;
3342
3343 *pte = __pa(root) | GCR3_VALID;
3344 }
3345
3346 root = __va(*pte & PAGE_MASK);
3347
3348 level -= 1;
3349 }
3350
3351 return pte;
3352}
3353
3354static int __set_gcr3(struct protection_domain *domain, int pasid,
3355 unsigned long cr3)
3356{
3357 u64 *pte;
3358
3359 if (domain->mode != PAGE_MODE_NONE)
3360 return -EINVAL;
3361
3362 pte = __get_gcr3_pte(domain->gcr3_tbl, domain->glx, pasid, true);
3363 if (pte == NULL)
3364 return -ENOMEM;
3365
3366 *pte = (cr3 & PAGE_MASK) | GCR3_VALID;
3367
3368 return __amd_iommu_flush_tlb(domain, pasid);
3369}
3370
3371static int __clear_gcr3(struct protection_domain *domain, int pasid)
3372{
3373 u64 *pte;
3374
3375 if (domain->mode != PAGE_MODE_NONE)
3376 return -EINVAL;
3377
3378 pte = __get_gcr3_pte(domain->gcr3_tbl, domain->glx, pasid, false);
3379 if (pte == NULL)
3380 return 0;
3381
3382 *pte = 0;
3383
3384 return __amd_iommu_flush_tlb(domain, pasid);
3385}
3386
3387int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, int pasid,
3388 unsigned long cr3)
3389{
3390 struct protection_domain *domain = dom->priv;
3391 unsigned long flags;
3392 int ret;
3393
3394 spin_lock_irqsave(&domain->lock, flags);
3395 ret = __set_gcr3(domain, pasid, cr3);
3396 spin_unlock_irqrestore(&domain->lock, flags);
3397
3398 return ret;
3399}
3400EXPORT_SYMBOL(amd_iommu_domain_set_gcr3);
3401
3402int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, int pasid)
3403{
3404 struct protection_domain *domain = dom->priv;
3405 unsigned long flags;
3406 int ret;
3407
3408 spin_lock_irqsave(&domain->lock, flags);
3409 ret = __clear_gcr3(domain, pasid);
3410 spin_unlock_irqrestore(&domain->lock, flags);
3411
3412 return ret;
3413}
3414EXPORT_SYMBOL(amd_iommu_domain_clear_gcr3);
diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h
index a92dc6117e2f..a951a7090e35 100644
--- a/drivers/iommu/amd_iommu_proto.h
+++ b/drivers/iommu/amd_iommu_proto.h
@@ -43,6 +43,10 @@ extern int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids);
43extern int amd_iommu_flush_page(struct iommu_domain *dom, int pasid, 43extern int amd_iommu_flush_page(struct iommu_domain *dom, int pasid,
44 u64 address); 44 u64 address);
45extern int amd_iommu_flush_tlb(struct iommu_domain *dom, int pasid); 45extern int amd_iommu_flush_tlb(struct iommu_domain *dom, int pasid);
46extern int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, int pasid,
47 unsigned long cr3);
48extern int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, int pasid);
49
46 50
47#ifndef CONFIG_AMD_IOMMU_STATS 51#ifndef CONFIG_AMD_IOMMU_STATS
48 52
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index ff1dfe9ad579..060724e02e9f 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -279,6 +279,7 @@
279#define DTE_GCR3_SHIFT_B 16 279#define DTE_GCR3_SHIFT_B 16
280#define DTE_GCR3_SHIFT_C 43 280#define DTE_GCR3_SHIFT_C 43
281 281
282#define GCR3_VALID 0x01ULL
282 283
283#define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL) 284#define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL)
284#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P) 285#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P)