HLOS can create s1 pagetable and request firmware to share the same pgtable to different entity(vmid). Use arm-smmu vendor implementation call to define custom alloc/free pgtable callback, And use these callbacks to share pgtable to different entity while creating pgtable. Signed-off-by: Gaurav Kohli <quic_gkohli@xxxxxxxxxxx> --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 81 ++++++++++++++++++++++ drivers/iommu/arm/arm-smmu/arm-smmu.h | 8 +++ 2 files changed, 89 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index c71afda79d64..e04079988787 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -4,6 +4,7 @@ */ #include <linux/acpi.h> +#include <linux/dev_printk.h> #include <linux/adreno-smmu-priv.h> #include <linux/delay.h> #include <linux/of_device.h> @@ -258,11 +259,91 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { { } }; +static void *qcom_alloc_pages(void *cookie, size_t size, gfp_t gfp) +{ + struct page *p; + struct qcom_scm_vmperm perms[2]; + u64 src = BIT(QCOM_SCM_VMID_HLOS); + int ret; + + struct arm_smmu_domain *domain = (void *)cookie; + /* + * qcom_scm_assign_mem call during atomic allocation can sleep, Using GFP flags + * to detect allocation path and return failure for atomic allocations. + */ + if (!gfpflags_allow_blocking(gfp)) { + dev_err(domain->smmu->dev, + "qcom_scm_assign_mem call are not allowed during atomic allocations\n"); + return NULL; + } + p = alloc_page(gfp); + if (!p) + return NULL; + + perms[0].vmid = QCOM_SCM_VMID_HLOS; + perms[0].perm = QCOM_SCM_PERM_RW; + perms[1].vmid = domain->secure_vmid; + perms[1].perm = QCOM_SCM_PERM_READ; + ret = qcom_scm_assign_mem(page_to_phys(p), PAGE_SIZE, + &src, perms, 2); + if (ret < 0) { + dev_err(domain->smmu->dev, + "assign memory failed for vmid=%x ret=%d\n", + domain->secure_vmid, ret); + __free_page(p); + return NULL; + } + + return page_address(p); +} + +static void qcom_free_pages(void *cookie, void *pages, size_t size) +{ + struct qcom_scm_vmperm perms; + struct page *p; + u64 src; + int ret; + + struct arm_smmu_domain *domain = (void *)cookie; + + p = virt_to_page(pages); + + perms.vmid = QCOM_SCM_VMID_HLOS; + perms.perm = QCOM_SCM_PERM_RWX; + src = BIT(domain->secure_vmid) | BIT(QCOM_SCM_VMID_HLOS); + ret = qcom_scm_assign_mem(page_to_phys(p), PAGE_SIZE, + &src, &perms, 1); + /* + * For assign failure scenario, it is not safe to use these pages by HLOS. + * So returning from here instead of freeing the page. + */ + if (ret < 0) { + dev_err(domain->smmu->dev, + "assign memory failed to HLOS for vmid=%x ret=%d\n", + domain->secure_vmid, ret); + return; + } + + __free_page(p); +} + static int qcom_smmu_init_context(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *pgtbl_cfg, struct device *dev) { + u32 val; + smmu_domain->cfg.flush_walk_prefer_tlbiasid = true; + /* + * For those client where qcom,iommu-vmid is not defined, default arm-smmu pgtable + * alloc/free handler will be used. + */ + if (of_property_read_u32(dev->of_node, "qcom,iommu-vmid", &val) == 0) { + smmu_domain->secure_vmid = val; + pgtbl_cfg->alloc = qcom_alloc_pages; + pgtbl_cfg->free = qcom_free_pages; + } + return 0; } diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h index 703fd5817ec1..98e1c5369e58 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.h +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h @@ -374,6 +374,14 @@ struct arm_smmu_domain { struct mutex init_mutex; /* Protects smmu pointer */ spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */ struct iommu_domain domain; + /* + * Use to store parse vmid value for those clients which want HLOS + * to share pgtable to different entity(VMID). + * Fix Me: Ideally this should be implemented on arm-smmu vendor implementation + * driver, but as per current design of arm_smmu_domain_alloc there is no way + * to call implementation callbacks. + */ + u32 secure_vmid; }; struct arm_smmu_master_cfg { -- 2.17.1