diff options
Diffstat (limited to 'drivers/pci/intel-iommu.c')
-rw-r--r-- | drivers/pci/intel-iommu.c | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 171f6c61fa1d..8a204d5bb427 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
@@ -1454,6 +1454,11 @@ static int domain_context_mapping_one(struct dmar_domain *domain, | |||
1454 | struct context_entry *context; | 1454 | struct context_entry *context; |
1455 | unsigned long flags; | 1455 | unsigned long flags; |
1456 | struct intel_iommu *iommu; | 1456 | struct intel_iommu *iommu; |
1457 | struct dma_pte *pgd; | ||
1458 | unsigned long num; | ||
1459 | unsigned long ndomains; | ||
1460 | int id; | ||
1461 | int agaw; | ||
1457 | 1462 | ||
1458 | pr_debug("Set context mapping for %02x:%02x.%d\n", | 1463 | pr_debug("Set context mapping for %02x:%02x.%d\n", |
1459 | bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); | 1464 | bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
@@ -1472,9 +1477,53 @@ static int domain_context_mapping_one(struct dmar_domain *domain, | |||
1472 | return 0; | 1477 | return 0; |
1473 | } | 1478 | } |
1474 | 1479 | ||
1475 | context_set_domain_id(context, domain->id); | 1480 | id = domain->id; |
1476 | context_set_address_width(context, domain->agaw); | 1481 | pgd = domain->pgd; |
1477 | context_set_address_root(context, virt_to_phys(domain->pgd)); | 1482 | |
1483 | if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) { | ||
1484 | int found = 0; | ||
1485 | |||
1486 | /* find an available domain id for this device in iommu */ | ||
1487 | ndomains = cap_ndoms(iommu->cap); | ||
1488 | num = find_first_bit(iommu->domain_ids, ndomains); | ||
1489 | for (; num < ndomains; ) { | ||
1490 | if (iommu->domains[num] == domain) { | ||
1491 | id = num; | ||
1492 | found = 1; | ||
1493 | break; | ||
1494 | } | ||
1495 | num = find_next_bit(iommu->domain_ids, | ||
1496 | cap_ndoms(iommu->cap), num+1); | ||
1497 | } | ||
1498 | |||
1499 | if (found == 0) { | ||
1500 | num = find_first_zero_bit(iommu->domain_ids, ndomains); | ||
1501 | if (num >= ndomains) { | ||
1502 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
1503 | printk(KERN_ERR "IOMMU: no free domain ids\n"); | ||
1504 | return -EFAULT; | ||
1505 | } | ||
1506 | |||
1507 | set_bit(num, iommu->domain_ids); | ||
1508 | iommu->domains[num] = domain; | ||
1509 | id = num; | ||
1510 | } | ||
1511 | |||
1512 | /* Skip top levels of page tables for | ||
1513 | * iommu which has less agaw than default. | ||
1514 | */ | ||
1515 | for (agaw = domain->agaw; agaw != iommu->agaw; agaw--) { | ||
1516 | pgd = phys_to_virt(dma_pte_addr(pgd)); | ||
1517 | if (!dma_pte_present(pgd)) { | ||
1518 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
1519 | return -ENOMEM; | ||
1520 | } | ||
1521 | } | ||
1522 | } | ||
1523 | |||
1524 | context_set_domain_id(context, id); | ||
1525 | context_set_address_width(context, iommu->agaw); | ||
1526 | context_set_address_root(context, virt_to_phys(pgd)); | ||
1478 | context_set_translation_type(context, CONTEXT_TT_MULTI_LEVEL); | 1527 | context_set_translation_type(context, CONTEXT_TT_MULTI_LEVEL); |
1479 | context_set_fault_enable(context); | 1528 | context_set_fault_enable(context); |
1480 | context_set_present(context); | 1529 | context_set_present(context); |