iommu/vt-d: Add initial support for PASID tables
Add CONFIG_INTEL_IOMMU_SVM, and allocate PASID tables on supported hardware. Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
parent
ae853ddb9a
commit
8a94ade4ce
|
@ -135,6 +135,14 @@ config INTEL_IOMMU
|
||||||
and include PCI device scope covered by these DMA
|
and include PCI device scope covered by these DMA
|
||||||
remapping devices.
|
remapping devices.
|
||||||
|
|
||||||
|
config INTEL_IOMMU_SVM
|
||||||
|
bool "Support for Shared Virtual Memory with Intel IOMMU"
|
||||||
|
depends on INTEL_IOMMU && X86
|
||||||
|
help
|
||||||
|
Shared Virtual Memory (SVM) provides a facility for devices
|
||||||
|
to access DMA resources through process address space by
|
||||||
|
means of a Process Address Space ID (PASID).
|
||||||
|
|
||||||
config INTEL_IOMMU_DEFAULT_ON
|
config INTEL_IOMMU_DEFAULT_ON
|
||||||
def_bool y
|
def_bool y
|
||||||
prompt "Enable Intel DMA Remapping Devices by default"
|
prompt "Enable Intel DMA Remapping Devices by default"
|
||||||
|
|
|
@ -12,6 +12,7 @@ obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
|
||||||
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
|
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
|
||||||
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
||||||
obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
|
obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
|
||||||
|
obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o
|
||||||
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
|
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
|
||||||
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
|
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
|
||||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
||||||
|
|
|
@ -1680,6 +1680,11 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
|
||||||
|
|
||||||
/* free context mapping */
|
/* free context mapping */
|
||||||
free_context_table(iommu);
|
free_context_table(iommu);
|
||||||
|
|
||||||
|
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||||
|
if (pasid_enabled(iommu))
|
||||||
|
intel_svm_free_pasid_tables(iommu);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dmar_domain *alloc_domain(int flags)
|
static struct dmar_domain *alloc_domain(int flags)
|
||||||
|
@ -3107,6 +3112,10 @@ static int __init init_dmars(void)
|
||||||
|
|
||||||
if (!ecap_pass_through(iommu->ecap))
|
if (!ecap_pass_through(iommu->ecap))
|
||||||
hw_pass_through = 0;
|
hw_pass_through = 0;
|
||||||
|
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||||
|
if (pasid_enabled(iommu))
|
||||||
|
intel_svm_alloc_pasid_tables(iommu);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iommu_pass_through)
|
if (iommu_pass_through)
|
||||||
|
@ -4122,6 +4131,11 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||||
|
if (pasid_enabled(iommu))
|
||||||
|
intel_svm_alloc_pasid_tables(iommu);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (dmaru->ignored) {
|
if (dmaru->ignored) {
|
||||||
/*
|
/*
|
||||||
* we always have to disable PMRs or DMA may fail on this device
|
* we always have to disable PMRs or DMA may fail on this device
|
||||||
|
|
65
drivers/iommu/intel-svm.c
Normal file
65
drivers/iommu/intel-svm.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2015 Intel Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/intel-iommu.h>
|
||||||
|
|
||||||
|
int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
|
||||||
|
{
|
||||||
|
struct page *pages;
|
||||||
|
int order;
|
||||||
|
|
||||||
|
order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT;
|
||||||
|
if (order < 0)
|
||||||
|
order = 0;
|
||||||
|
|
||||||
|
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||||
|
if (!pages) {
|
||||||
|
pr_warn("IOMMU: %s: Failed to allocate PASID table\n",
|
||||||
|
iommu->name);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
iommu->pasid_table = page_address(pages);
|
||||||
|
pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order);
|
||||||
|
|
||||||
|
if (ecap_dis(iommu->ecap)) {
|
||||||
|
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||||
|
if (pages)
|
||||||
|
iommu->pasid_state_table = page_address(pages);
|
||||||
|
else
|
||||||
|
pr_warn("IOMMU: %s: Failed to allocate PASID state table\n",
|
||||||
|
iommu->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int intel_svm_free_pasid_tables(struct intel_iommu *iommu)
|
||||||
|
{
|
||||||
|
int order;
|
||||||
|
|
||||||
|
order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT;
|
||||||
|
if (order < 0)
|
||||||
|
order = 0;
|
||||||
|
|
||||||
|
if (iommu->pasid_table) {
|
||||||
|
free_pages((unsigned long)iommu->pasid_table, order);
|
||||||
|
iommu->pasid_table = NULL;
|
||||||
|
}
|
||||||
|
if (iommu->pasid_state_table) {
|
||||||
|
free_pages((unsigned long)iommu->pasid_state_table, order);
|
||||||
|
iommu->pasid_state_table = NULL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -325,6 +325,9 @@ enum {
|
||||||
#define VTD_FLAG_TRANS_PRE_ENABLED (1 << 0)
|
#define VTD_FLAG_TRANS_PRE_ENABLED (1 << 0)
|
||||||
#define VTD_FLAG_IRQ_REMAP_PRE_ENABLED (1 << 1)
|
#define VTD_FLAG_IRQ_REMAP_PRE_ENABLED (1 << 1)
|
||||||
|
|
||||||
|
struct pasid_entry;
|
||||||
|
struct pasid_state_entry;
|
||||||
|
|
||||||
struct intel_iommu {
|
struct intel_iommu {
|
||||||
void __iomem *reg; /* Pointer to hardware regs, virtual addr */
|
void __iomem *reg; /* Pointer to hardware regs, virtual addr */
|
||||||
u64 reg_phys; /* physical address of hw register set */
|
u64 reg_phys; /* physical address of hw register set */
|
||||||
|
@ -347,6 +350,15 @@ struct intel_iommu {
|
||||||
struct root_entry *root_entry; /* virtual address */
|
struct root_entry *root_entry; /* virtual address */
|
||||||
|
|
||||||
struct iommu_flush flush;
|
struct iommu_flush flush;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_INTEL_IOMMU_SVM
|
||||||
|
/* These are large and need to be contiguous, so we allocate just
|
||||||
|
* one for now. We'll maybe want to rethink that if we truly give
|
||||||
|
* devices away to userspace processes (e.g. for DPDK) and don't
|
||||||
|
* want to trust that userspace will use *only* the PASID it was
|
||||||
|
* told to. But while it's all driver-arbitrated, we're fine. */
|
||||||
|
struct pasid_entry *pasid_table;
|
||||||
|
struct pasid_state_entry *pasid_state_table;
|
||||||
#endif
|
#endif
|
||||||
struct q_inval *qi; /* Queued invalidation info */
|
struct q_inval *qi; /* Queued invalidation info */
|
||||||
u32 *iommu_state; /* Store iommu states between suspend and resume.*/
|
u32 *iommu_state; /* Store iommu states between suspend and resume.*/
|
||||||
|
@ -387,6 +399,9 @@ extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
|
||||||
|
|
||||||
extern int dmar_ir_support(void);
|
extern int dmar_ir_support(void);
|
||||||
|
|
||||||
|
extern int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu);
|
||||||
|
extern int intel_svm_free_pasid_tables(struct intel_iommu *iommu);
|
||||||
|
|
||||||
extern const struct attribute_group *intel_iommu_groups[];
|
extern const struct attribute_group *intel_iommu_groups[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue