|
|
|
@@ -612,6 +612,12 @@ struct intel_iommu *domain_get_iommu(struct dmar_domain *domain)
|
|
|
|
|
return g_iommus[iommu_id];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool iommu_paging_structure_coherency(struct intel_iommu *iommu)
|
|
|
|
|
{
|
|
|
|
|
return sm_supported(iommu) ?
|
|
|
|
|
ecap_smpwc(iommu->ecap) : ecap_coherent(iommu->ecap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void domain_update_iommu_coherency(struct dmar_domain *domain)
|
|
|
|
|
{
|
|
|
|
|
struct dmar_drhd_unit *drhd;
|
|
|
|
@@ -623,7 +629,7 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain)
|
|
|
|
|
|
|
|
|
|
for_each_domain_iommu(i, domain) {
|
|
|
|
|
found = true;
|
|
|
|
|
if (!ecap_coherent(g_iommus[i]->ecap)) {
|
|
|
|
|
if (!iommu_paging_structure_coherency(g_iommus[i])) {
|
|
|
|
|
domain->iommu_coherency = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@@ -634,7 +640,7 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain)
|
|
|
|
|
/* No hardware attached; use lowest common denominator */
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
for_each_active_iommu(iommu, drhd) {
|
|
|
|
|
if (!ecap_coherent(iommu->ecap)) {
|
|
|
|
|
if (!iommu_paging_structure_coherency(iommu)) {
|
|
|
|
|
domain->iommu_coherency = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@@ -921,7 +927,7 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
|
|
|
|
|
domain_flush_cache(domain, tmp_page, VTD_PAGE_SIZE);
|
|
|
|
|
pteval = ((uint64_t)virt_to_dma_pfn(tmp_page) << VTD_PAGE_SHIFT) | DMA_PTE_READ | DMA_PTE_WRITE;
|
|
|
|
|
if (domain_use_first_level(domain))
|
|
|
|
|
pteval |= DMA_FL_PTE_XD;
|
|
|
|
|
pteval |= DMA_FL_PTE_XD | DMA_FL_PTE_US;
|
|
|
|
|
if (cmpxchg64(&pte->val, 0ULL, pteval))
|
|
|
|
|
/* Someone else set it while we were thinking; use theirs. */
|
|
|
|
|
free_pgtable_page(tmp_page);
|
|
|
|
@@ -1951,7 +1957,6 @@ static inline void
|
|
|
|
|
context_set_sm_rid2pasid(struct context_entry *context, unsigned long pasid)
|
|
|
|
|
{
|
|
|
|
|
context->hi |= pasid & ((1 << 20) - 1);
|
|
|
|
|
context->hi |= (1 << 20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -2095,7 +2100,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
|
|
|
|
|
|
|
|
|
|
context_set_fault_enable(context);
|
|
|
|
|
context_set_present(context);
|
|
|
|
|
domain_flush_cache(domain, context, sizeof(*context));
|
|
|
|
|
if (!ecap_coherent(iommu->ecap))
|
|
|
|
|
clflush_cache_range(context, sizeof(*context));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* It's a non-present to present mapping. If hardware doesn't cache
|
|
|
|
@@ -2243,7 +2249,7 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
|
|
|
|
|
|
|
|
|
|
attr = prot & (DMA_PTE_READ | DMA_PTE_WRITE | DMA_PTE_SNP);
|
|
|
|
|
if (domain_use_first_level(domain))
|
|
|
|
|
attr |= DMA_FL_PTE_PRESENT | DMA_FL_PTE_XD;
|
|
|
|
|
attr |= DMA_FL_PTE_PRESENT | DMA_FL_PTE_XD | DMA_FL_PTE_US;
|
|
|
|
|
|
|
|
|
|
if (!sg) {
|
|
|
|
|
sg_res = nr_pages;
|
|
|
|
@@ -2695,7 +2701,9 @@ static int __init si_domain_init(int hw)
|
|
|
|
|
end >> agaw_to_width(si_domain->agaw)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ret = iommu_domain_identity_map(si_domain, start, end);
|
|
|
|
|
ret = iommu_domain_identity_map(si_domain,
|
|
|
|
|
mm_to_dma_pfn(start >> PAGE_SHIFT),
|
|
|
|
|
mm_to_dma_pfn(end >> PAGE_SHIFT));
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
@@ -6021,6 +6029,23 @@ intel_iommu_domain_set_attr(struct iommu_domain *domain,
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check that the device does not live on an external facing PCI port that is
|
|
|
|
|
* marked as untrusted. Such devices should not be able to apply quirks and
|
|
|
|
|
* thus not be able to bypass the IOMMU restrictions.
|
|
|
|
|
*/
|
|
|
|
|
static bool risky_device(struct pci_dev *pdev)
|
|
|
|
|
{
|
|
|
|
|
if (pdev->untrusted) {
|
|
|
|
|
pci_info(pdev,
|
|
|
|
|
"Skipping IOMMU quirk for dev [%04X:%04X] on untrusted PCI link\n",
|
|
|
|
|
pdev->vendor, pdev->device);
|
|
|
|
|
pci_info(pdev, "Please check with your BIOS/Platform vendor about this\n");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct iommu_ops intel_iommu_ops = {
|
|
|
|
|
.capable = intel_iommu_capable,
|
|
|
|
|
.domain_alloc = intel_iommu_domain_alloc,
|
|
|
|
@@ -6060,6 +6085,9 @@ const struct iommu_ops intel_iommu_ops = {
|
|
|
|
|
|
|
|
|
|
static void quirk_iommu_igfx(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (risky_device(dev))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
pci_info(dev, "Disabling IOMMU for graphics on this chipset\n");
|
|
|
|
|
dmar_map_gfx = 0;
|
|
|
|
|
}
|
|
|
|
@@ -6101,6 +6129,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
|
|
|
|
|
|
|
|
|
|
static void quirk_iommu_rwbf(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (risky_device(dev))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Mobile 4 Series Chipset neglects to set RWBF capability,
|
|
|
|
|
* but needs it. Same seems to hold for the desktop versions.
|
|
|
|
@@ -6131,6 +6162,9 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
unsigned short ggc;
|
|
|
|
|
|
|
|
|
|
if (risky_device(dev))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (pci_read_config_word(dev, GGC, &ggc))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
@@ -6164,6 +6198,12 @@ static void __init check_tylersburg_isoch(void)
|
|
|
|
|
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x3a3e, NULL);
|
|
|
|
|
if (!pdev)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (risky_device(pdev)) {
|
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
|
|
|
|
|
|
/* System Management Registers. Might be hidden, in which case
|
|
|
|
@@ -6173,6 +6213,11 @@ static void __init check_tylersburg_isoch(void)
|
|
|
|
|
if (!pdev)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (risky_device(pdev)) {
|
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pci_read_config_dword(pdev, 0x188, &vtisochctrl)) {
|
|
|
|
|
pci_dev_put(pdev);
|
|
|
|
|
return;
|
|
|
|
|