aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu/amd_iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu/amd_iommu.c')
-rw-r--r--drivers/iommu/amd_iommu.c130
1 files changed, 130 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);