diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c b/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c index f6c28d714f3e..ac94dd3e6b54 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -50,6 +52,22 @@ struct hyp_arm_smmu_v3_device *kvm_hyp_arm_smmu_v3_smmus; smmu_wait(_cond); \ }) +/* + * SMMUv3 domain: + * @domain: Pointer to the IOMMU domain. + * @smmu: SMMU instance for this domain. + * @type: Type of domain (S1, S2) + * @pgt_lock: Lock for page table + * @pgtable: io_pgtable instance for this domain + */ +struct hyp_arm_smmu_v3_domain { + struct kvm_hyp_iommu_domain *domain; + struct hyp_arm_smmu_v3_device *smmu; + u32 type; + hyp_spinlock_t pgt_lock; + struct io_pgtable *pgtable; +}; + static int smmu_write_cr0(struct hyp_arm_smmu_v3_device *smmu, u32 val) { writel_relaxed(val, smmu->base + ARM_SMMU_CR0); @@ -546,7 +564,53 @@ out_reclaim_smmu: return ret; } +static struct kvm_hyp_iommu *smmu_id_to_iommu(pkvm_handle_t smmu_id) +{ + if (smmu_id >= kvm_hyp_arm_smmu_v3_count) + return NULL; + smmu_id = array_index_nospec(smmu_id, kvm_hyp_arm_smmu_v3_count); + + return &kvm_hyp_arm_smmu_v3_smmus[smmu_id].iommu; +} + +static int smmu_alloc_domain(struct kvm_hyp_iommu_domain *domain, int type) +{ + struct hyp_arm_smmu_v3_domain *smmu_domain; + + if (type >= KVM_ARM_SMMU_DOMAIN_MAX) + return -EINVAL; + + smmu_domain = hyp_alloc(sizeof(*smmu_domain)); + if (!smmu_domain) + return -ENOMEM; + + /* + * Can't do much without knowing the SMMUv3. + * Page table will be allocated at attach_dev, but can be + * freed from free domain. + */ + smmu_domain->domain = domain; + smmu_domain->type = type; + hyp_spin_lock_init(&smmu_domain->pgt_lock); + domain->priv = (void *)smmu_domain; + + return 0; +} + +static void smmu_free_domain(struct kvm_hyp_iommu_domain *domain) +{ + struct hyp_arm_smmu_v3_domain *smmu_domain = domain->priv; + + if (smmu_domain->pgtable) + kvm_arm_io_pgtable_free(smmu_domain->pgtable); + + hyp_free(smmu_domain); +} + /* Shared with the kernel driver in EL1 */ struct kvm_iommu_ops smmu_ops = { .init = smmu_init, + .get_iommu_by_id = smmu_id_to_iommu, + .alloc_domain = smmu_alloc_domain, + .free_domain = smmu_free_domain, }; diff --git a/include/kvm/arm_smmu_v3.h b/include/kvm/arm_smmu_v3.h index 352c1b2dc72a..ded98cbaebc1 100644 --- a/include/kvm/arm_smmu_v3.h +++ b/include/kvm/arm_smmu_v3.h @@ -33,4 +33,10 @@ extern size_t kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_count); extern struct hyp_arm_smmu_v3_device *kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_smmus); #define kvm_hyp_arm_smmu_v3_smmus kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_smmus) +enum kvm_arm_smmu_domain_type { + KVM_ARM_SMMU_DOMAIN_S1, + KVM_ARM_SMMU_DOMAIN_S2, + KVM_ARM_SMMU_DOMAIN_MAX, +}; + #endif /* __KVM_ARM_SMMU_V3_H */