@@ -7,6 +7,8 @@
#include <asm/arm-smmu-v3-common.h>
#include <asm/kvm_hyp.h>
#include <kvm/arm_smmu_v3.h>
+#include <linux/io-pgtable-arm.h>
+#include <nvhe/alloc.h>
#include <nvhe/iommu.h>
#include <nvhe/mem_protect.h>
#include <nvhe/mm.h>
@@ -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);
@@ -541,7 +559,53 @@ static int smmu_init(void)
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,
};
@@ -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 */
Add SMMUv3 alloc/free domain, as this operations are not tied to the IOMMU, we can't do much with the io-pgtable allocation or configuration. Signed-off-by: Mostafa Saleh <smostafa@google.com> --- arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c | 64 +++++++++++++++++++++ include/kvm/arm_smmu_v3.h | 6 ++ 2 files changed, 70 insertions(+)