Merge branch 'for-joerg/arm-smmu/updates' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into arm/smmu
This commit is contained in:
commit
2d565a57f2
|
@ -3411,6 +3411,8 @@ static bool amd_iommu_capable(enum iommu_cap cap)
|
|||
return true;
|
||||
case IOMMU_CAP_INTR_REMAP:
|
||||
return (irq_remapping_enabled == 1);
|
||||
case IOMMU_CAP_NOEXEC:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -404,9 +404,16 @@ struct arm_smmu_cfg {
|
|||
#define ARM_SMMU_CB_ASID(cfg) ((cfg)->cbndx)
|
||||
#define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1)
|
||||
|
||||
enum arm_smmu_domain_stage {
|
||||
ARM_SMMU_DOMAIN_S1 = 0,
|
||||
ARM_SMMU_DOMAIN_S2,
|
||||
ARM_SMMU_DOMAIN_NESTED,
|
||||
};
|
||||
|
||||
struct arm_smmu_domain {
|
||||
struct arm_smmu_device *smmu;
|
||||
struct arm_smmu_cfg cfg;
|
||||
enum arm_smmu_domain_stage stage;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
@ -906,19 +913,46 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
if (smmu_domain->smmu)
|
||||
goto out_unlock;
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) {
|
||||
/*
|
||||
* Mapping the requested stage onto what we support is surprisingly
|
||||
* complicated, mainly because the spec allows S1+S2 SMMUs without
|
||||
* support for nested translation. That means we end up with the
|
||||
* following table:
|
||||
*
|
||||
* Requested Supported Actual
|
||||
* S1 N S1
|
||||
* S1 S1+S2 S1
|
||||
* S1 S2 S2
|
||||
* S1 S1 S1
|
||||
* N N N
|
||||
* N S1+S2 S2
|
||||
* N S2 S2
|
||||
* N S1 S1
|
||||
*
|
||||
* Note that you can't actually request stage-2 mappings.
|
||||
*/
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
|
||||
|
||||
switch (smmu_domain->stage) {
|
||||
case ARM_SMMU_DOMAIN_S1:
|
||||
cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
|
||||
start = smmu->num_s2_context_banks;
|
||||
break;
|
||||
case ARM_SMMU_DOMAIN_NESTED:
|
||||
/*
|
||||
* We will likely want to change this if/when KVM gets
|
||||
* involved.
|
||||
*/
|
||||
cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
|
||||
start = smmu->num_s2_context_banks;
|
||||
} else if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) {
|
||||
cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
|
||||
start = smmu->num_s2_context_banks;
|
||||
} else {
|
||||
case ARM_SMMU_DOMAIN_S2:
|
||||
cfg->cbar = CBAR_TYPE_S2_TRANS;
|
||||
start = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
|
||||
|
@ -1281,7 +1315,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
|
|||
unsigned long pfn, int prot, int stage)
|
||||
{
|
||||
pte_t *pte, *start;
|
||||
pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN;
|
||||
pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF;
|
||||
|
||||
if (pmd_none(*pmd)) {
|
||||
/* Allocate a new set of tables */
|
||||
|
@ -1315,10 +1349,11 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
|
|||
pteval |= ARM_SMMU_PTE_MEMATTR_NC;
|
||||
}
|
||||
|
||||
if (prot & IOMMU_NOEXEC)
|
||||
pteval |= ARM_SMMU_PTE_XN;
|
||||
|
||||
/* If no access, create a faulting entry to avoid TLB fills */
|
||||
if (prot & IOMMU_EXEC)
|
||||
pteval &= ~ARM_SMMU_PTE_XN;
|
||||
else if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
|
||||
if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
|
||||
pteval &= ~ARM_SMMU_PTE_PAGE;
|
||||
|
||||
pteval |= ARM_SMMU_PTE_SH_IS;
|
||||
|
@ -1568,6 +1603,8 @@ static bool arm_smmu_capable(enum iommu_cap cap)
|
|||
return true;
|
||||
case IOMMU_CAP_INTR_REMAP:
|
||||
return true; /* MSIs are just memory writes */
|
||||
case IOMMU_CAP_NOEXEC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -1644,20 +1681,56 @@ static void arm_smmu_remove_device(struct device *dev)
|
|||
iommu_group_remove_device(dev);
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
|
||||
enum iommu_attr attr, void *data)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain = domain->priv;
|
||||
|
||||
switch (attr) {
|
||||
case DOMAIN_ATTR_NESTING:
|
||||
*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
|
||||
return 0;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
|
||||
enum iommu_attr attr, void *data)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain = domain->priv;
|
||||
|
||||
switch (attr) {
|
||||
case DOMAIN_ATTR_NESTING:
|
||||
if (smmu_domain->smmu)
|
||||
return -EPERM;
|
||||
if (*(int *)data)
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
|
||||
else
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iommu_ops arm_smmu_ops = {
|
||||
.capable = arm_smmu_capable,
|
||||
.domain_init = arm_smmu_domain_init,
|
||||
.domain_destroy = arm_smmu_domain_destroy,
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
.detach_dev = arm_smmu_detach_dev,
|
||||
.map = arm_smmu_map,
|
||||
.unmap = arm_smmu_unmap,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.add_device = arm_smmu_add_device,
|
||||
.remove_device = arm_smmu_remove_device,
|
||||
.pgsize_bitmap = (SECTION_SIZE |
|
||||
ARM_SMMU_PTE_CONT_SIZE |
|
||||
PAGE_SIZE),
|
||||
.capable = arm_smmu_capable,
|
||||
.domain_init = arm_smmu_domain_init,
|
||||
.domain_destroy = arm_smmu_domain_destroy,
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
.detach_dev = arm_smmu_detach_dev,
|
||||
.map = arm_smmu_map,
|
||||
.unmap = arm_smmu_unmap,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.add_device = arm_smmu_add_device,
|
||||
.remove_device = arm_smmu_remove_device,
|
||||
.domain_get_attr = arm_smmu_domain_get_attr,
|
||||
.domain_set_attr = arm_smmu_domain_set_attr,
|
||||
.pgsize_bitmap = (SECTION_SIZE |
|
||||
ARM_SMMU_PTE_CONT_SIZE |
|
||||
PAGE_SIZE),
|
||||
};
|
||||
|
||||
static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
||||
|
@ -2072,8 +2145,20 @@ static struct platform_driver arm_smmu_driver = {
|
|||
|
||||
static int __init arm_smmu_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Play nice with systems that don't have an ARM SMMU by checking that
|
||||
* an ARM SMMU exists in the system before proceeding with the driver
|
||||
* and IOMMU bus operation registration.
|
||||
*/
|
||||
np = of_find_matching_node(NULL, arm_smmu_of_match);
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
ret = platform_driver_register(&arm_smmu_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#define IOMMU_READ (1 << 0)
|
||||
#define IOMMU_WRITE (1 << 1)
|
||||
#define IOMMU_CACHE (1 << 2) /* DMA cache coherency */
|
||||
#define IOMMU_EXEC (1 << 3)
|
||||
#define IOMMU_NOEXEC (1 << 3)
|
||||
|
||||
struct iommu_ops;
|
||||
struct iommu_group;
|
||||
|
@ -61,6 +61,7 @@ enum iommu_cap {
|
|||
IOMMU_CAP_CACHE_COHERENCY, /* IOMMU can enforce cache coherent DMA
|
||||
transactions */
|
||||
IOMMU_CAP_INTR_REMAP, /* IOMMU supports interrupt isolation */
|
||||
IOMMU_CAP_NOEXEC, /* IOMMU_NOEXEC flag */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue