diff options
-rw-r--r-- | drivers/iommu/arm-smmu.c | 110 |
1 files changed, 90 insertions, 20 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 217581469f79..7a80f710ba2d 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c | |||
@@ -404,9 +404,16 @@ struct arm_smmu_cfg { | |||
404 | #define ARM_SMMU_CB_ASID(cfg) ((cfg)->cbndx) | 404 | #define ARM_SMMU_CB_ASID(cfg) ((cfg)->cbndx) |
405 | #define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1) | 405 | #define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1) |
406 | 406 | ||
407 | enum arm_smmu_domain_stage { | ||
408 | ARM_SMMU_DOMAIN_S1 = 0, | ||
409 | ARM_SMMU_DOMAIN_S2, | ||
410 | ARM_SMMU_DOMAIN_NESTED, | ||
411 | }; | ||
412 | |||
407 | struct arm_smmu_domain { | 413 | struct arm_smmu_domain { |
408 | struct arm_smmu_device *smmu; | 414 | struct arm_smmu_device *smmu; |
409 | struct arm_smmu_cfg cfg; | 415 | struct arm_smmu_cfg cfg; |
416 | enum arm_smmu_domain_stage stage; | ||
410 | spinlock_t lock; | 417 | spinlock_t lock; |
411 | }; | 418 | }; |
412 | 419 | ||
@@ -906,19 +913,46 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, | |||
906 | if (smmu_domain->smmu) | 913 | if (smmu_domain->smmu) |
907 | goto out_unlock; | 914 | goto out_unlock; |
908 | 915 | ||
909 | if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) { | 916 | /* |
917 | * Mapping the requested stage onto what we support is surprisingly | ||
918 | * complicated, mainly because the spec allows S1+S2 SMMUs without | ||
919 | * support for nested translation. That means we end up with the | ||
920 | * following table: | ||
921 | * | ||
922 | * Requested Supported Actual | ||
923 | * S1 N S1 | ||
924 | * S1 S1+S2 S1 | ||
925 | * S1 S2 S2 | ||
926 | * S1 S1 S1 | ||
927 | * N N N | ||
928 | * N S1+S2 S2 | ||
929 | * N S2 S2 | ||
930 | * N S1 S1 | ||
931 | * | ||
932 | * Note that you can't actually request stage-2 mappings. | ||
933 | */ | ||
934 | if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) | ||
935 | smmu_domain->stage = ARM_SMMU_DOMAIN_S2; | ||
936 | if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) | ||
937 | smmu_domain->stage = ARM_SMMU_DOMAIN_S1; | ||
938 | |||
939 | switch (smmu_domain->stage) { | ||
940 | case ARM_SMMU_DOMAIN_S1: | ||
941 | cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; | ||
942 | start = smmu->num_s2_context_banks; | ||
943 | break; | ||
944 | case ARM_SMMU_DOMAIN_NESTED: | ||
910 | /* | 945 | /* |
911 | * We will likely want to change this if/when KVM gets | 946 | * We will likely want to change this if/when KVM gets |
912 | * involved. | 947 | * involved. |
913 | */ | 948 | */ |
914 | cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; | 949 | case ARM_SMMU_DOMAIN_S2: |
915 | start = smmu->num_s2_context_banks; | ||
916 | } else if (smmu->features & ARM_SMMU_FEAT_TRANS_S1) { | ||
917 | cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS; | ||
918 | start = smmu->num_s2_context_banks; | ||
919 | } else { | ||
920 | cfg->cbar = CBAR_TYPE_S2_TRANS; | 950 | cfg->cbar = CBAR_TYPE_S2_TRANS; |
921 | start = 0; | 951 | start = 0; |
952 | break; | ||
953 | default: | ||
954 | ret = -EINVAL; | ||
955 | goto out_unlock; | ||
922 | } | 956 | } |
923 | 957 | ||
924 | ret = __arm_smmu_alloc_bitmap(smmu->context_map, start, | 958 | ret = __arm_smmu_alloc_bitmap(smmu->context_map, start, |
@@ -1647,20 +1681,56 @@ static void arm_smmu_remove_device(struct device *dev) | |||
1647 | iommu_group_remove_device(dev); | 1681 | iommu_group_remove_device(dev); |
1648 | } | 1682 | } |
1649 | 1683 | ||
1684 | static int arm_smmu_domain_get_attr(struct iommu_domain *domain, | ||
1685 | enum iommu_attr attr, void *data) | ||
1686 | { | ||
1687 | struct arm_smmu_domain *smmu_domain = domain->priv; | ||
1688 | |||
1689 | switch (attr) { | ||
1690 | case DOMAIN_ATTR_NESTING: | ||
1691 | *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED); | ||
1692 | return 0; | ||
1693 | default: | ||
1694 | return -ENODEV; | ||
1695 | } | ||
1696 | } | ||
1697 | |||
1698 | static int arm_smmu_domain_set_attr(struct iommu_domain *domain, | ||
1699 | enum iommu_attr attr, void *data) | ||
1700 | { | ||
1701 | struct arm_smmu_domain *smmu_domain = domain->priv; | ||
1702 | |||
1703 | switch (attr) { | ||
1704 | case DOMAIN_ATTR_NESTING: | ||
1705 | if (smmu_domain->smmu) | ||
1706 | return -EPERM; | ||
1707 | if (*(int *)data) | ||
1708 | smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; | ||
1709 | else | ||
1710 | smmu_domain->stage = ARM_SMMU_DOMAIN_S1; | ||
1711 | |||
1712 | return 0; | ||
1713 | default: | ||
1714 | return -ENODEV; | ||
1715 | } | ||
1716 | } | ||
1717 | |||
1650 | static const struct iommu_ops arm_smmu_ops = { | 1718 | static const struct iommu_ops arm_smmu_ops = { |
1651 | .capable = arm_smmu_capable, | 1719 | .capable = arm_smmu_capable, |
1652 | .domain_init = arm_smmu_domain_init, | 1720 | .domain_init = arm_smmu_domain_init, |
1653 | .domain_destroy = arm_smmu_domain_destroy, | 1721 | .domain_destroy = arm_smmu_domain_destroy, |
1654 | .attach_dev = arm_smmu_attach_dev, | 1722 | .attach_dev = arm_smmu_attach_dev, |
1655 | .detach_dev = arm_smmu_detach_dev, | 1723 | .detach_dev = arm_smmu_detach_dev, |
1656 | .map = arm_smmu_map, | 1724 | .map = arm_smmu_map, |
1657 | .unmap = arm_smmu_unmap, | 1725 | .unmap = arm_smmu_unmap, |
1658 | .iova_to_phys = arm_smmu_iova_to_phys, | 1726 | .iova_to_phys = arm_smmu_iova_to_phys, |
1659 | .add_device = arm_smmu_add_device, | 1727 | .add_device = arm_smmu_add_device, |
1660 | .remove_device = arm_smmu_remove_device, | 1728 | .remove_device = arm_smmu_remove_device, |
1661 | .pgsize_bitmap = (SECTION_SIZE | | 1729 | .domain_get_attr = arm_smmu_domain_get_attr, |
1662 | ARM_SMMU_PTE_CONT_SIZE | | 1730 | .domain_set_attr = arm_smmu_domain_set_attr, |
1663 | PAGE_SIZE), | 1731 | .pgsize_bitmap = (SECTION_SIZE | |
1732 | ARM_SMMU_PTE_CONT_SIZE | | ||
1733 | PAGE_SIZE), | ||
1664 | }; | 1734 | }; |
1665 | 1735 | ||
1666 | static void arm_smmu_device_reset(struct arm_smmu_device *smmu) | 1736 | static void arm_smmu_device_reset(struct arm_smmu_device *smmu) |