From b3f59a9b33183e778f8e024d1ee28bde85be6f41 Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Mon, 17 Apr 2023 11:41:51 -0700 Subject: [PATCH] ANDROID: gunyah: Sync with latest "firmware: qcom_scm: Register Gunyah platform ops" The QCOM platform hooks are not enabled in gki_defconfig, but backport to align to Gunyah v13 patches posted to kernel.org: https://lore.kernel.org/all/20230509204801.2824351-15-quic_eberman@quicinc.com/ Bug: 279506910 Change-Id: Idce927cfa89cfea137b96024dd3c5a2bf297da82 Signed-off-by: Elliot Berman --- arch/arm64/configs/gki_defconfig | 1 + drivers/virt/gunyah/Kconfig | 13 +++ drivers/virt/gunyah/Makefile | 1 + drivers/virt/gunyah/gunyah_qcom.c | 147 ++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 drivers/virt/gunyah/gunyah_qcom.c diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig index fb4106db4a43..260befab3d4b 100644 --- a/arch/arm64/configs/gki_defconfig +++ b/arch/arm64/configs/gki_defconfig @@ -538,6 +538,7 @@ CONFIG_DMABUF_HEAPS_PAGE_POOL=y CONFIG_UIO=y CONFIG_VIRT_DRIVERS=y CONFIG_GUNYAH=y +# CONFIG_GUNYAH_QCOM_PLATFORM is not set CONFIG_GUNYAH_VCPU=y CONFIG_GUNYAH_IRQFD=y CONFIG_GUNYAH_IOEVENTFD=y diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig index 2eac0b68fcec..02c7a8b60eae 100644 --- a/drivers/virt/gunyah/Kconfig +++ b/drivers/virt/gunyah/Kconfig @@ -6,6 +6,7 @@ config GUNYAH depends on MAILBOX select GUNYAH_PLATFORM_HOOKS select AUXILIARY_BUS + imply GUNYAH_QCOM_PLATFORM if ARCH_QCOM help The Gunyah drivers are the helper interfaces that run in a guest VM such as basic inter-VM IPC and signaling mechanisms, and higher level @@ -17,6 +18,18 @@ config GUNYAH config GUNYAH_PLATFORM_HOOKS tristate +config GUNYAH_QCOM_PLATFORM + tristate "Support for Gunyah on Qualcomm platforms" + depends on GUNYAH + select GUNYAH_PLATFORM_HOOKS + select QCOM_SCM + help + Enable support for interacting with Gunyah on Qualcomm + platforms. Interaction with Qualcomm firmware requires + extra platform-specific support. + + Say Y/M here to use Gunyah on Qualcomm platforms. + config GUNYAH_VCPU tristate "Runnable Gunyah vCPUs" depends on GUNYAH diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile index c9fb17d3a5b0..efda8f732f8a 100644 --- a/drivers/virt/gunyah/Makefile +++ b/drivers/virt/gunyah/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_GUNYAH_PLATFORM_HOOKS) += gunyah_platform_hooks.o +obj-$(CONFIG_GUNYAH_QCOM_PLATFORM) += gunyah_qcom.o gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_rpc.o vm_mgr.o vm_mgr_mm.o obj-$(CONFIG_GUNYAH) += gunyah_rsc_mgr.o diff --git a/drivers/virt/gunyah/gunyah_qcom.c b/drivers/virt/gunyah/gunyah_qcom.c new file mode 100644 index 000000000000..ec62f1e23724 --- /dev/null +++ b/drivers/virt/gunyah/gunyah_qcom.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define QCOM_SCM_RM_MANAGED_VMID 0x3A +#define QCOM_SCM_MAX_MANAGED_VMID 0x3F + +static int qcom_scm_gh_rm_pre_mem_share(struct gh_rm *rm, struct gh_rm_mem_parcel *mem_parcel) +{ + struct qcom_scm_vmperm *new_perms; + u64 src, src_cpy; + int ret = 0, i, n; + u16 vmid; + + new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms), GFP_KERNEL); + if (!new_perms) + return -ENOMEM; + + for (n = 0; n < mem_parcel->n_acl_entries; n++) { + vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid); + if (vmid <= QCOM_SCM_MAX_MANAGED_VMID) + new_perms[n].vmid = vmid; + else + new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID; + if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_X) + new_perms[n].perm |= QCOM_SCM_PERM_EXEC; + if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_W) + new_perms[n].perm |= QCOM_SCM_PERM_WRITE; + if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_R) + new_perms[n].perm |= QCOM_SCM_PERM_READ; + } + + src = (1ull << QCOM_SCM_VMID_HLOS); + + for (i = 0; i < mem_parcel->n_mem_entries; i++) { + src_cpy = src; + ret = qcom_scm_assign_mem(le64_to_cpu(mem_parcel->mem_entries[i].ipa_base), + le64_to_cpu(mem_parcel->mem_entries[i].size), + &src_cpy, new_perms, mem_parcel->n_acl_entries); + if (ret) { + src = 0; + for (n = 0; n < mem_parcel->n_acl_entries; n++) { + vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid); + if (vmid <= QCOM_SCM_MAX_MANAGED_VMID) + src |= (1ull << vmid); + else + src |= (1ull << QCOM_SCM_RM_MANAGED_VMID); + } + + new_perms[0].vmid = QCOM_SCM_VMID_HLOS; + + for (i--; i >= 0; i--) { + src_cpy = src; + WARN_ON_ONCE(qcom_scm_assign_mem( + le64_to_cpu(mem_parcel->mem_entries[i].ipa_base), + le64_to_cpu(mem_parcel->mem_entries[i].size), + &src_cpy, new_perms, 1)); + } + break; + } + } + + kfree(new_perms); + return ret; +} + +static int qcom_scm_gh_rm_post_mem_reclaim(struct gh_rm *rm, struct gh_rm_mem_parcel *mem_parcel) +{ + struct qcom_scm_vmperm new_perms; + u64 src = 0, src_cpy; + int ret = 0, i, n; + u16 vmid; + + new_perms.vmid = QCOM_SCM_VMID_HLOS; + new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | QCOM_SCM_PERM_READ; + + for (n = 0; n < mem_parcel->n_acl_entries; n++) { + vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid); + if (vmid <= QCOM_SCM_MAX_MANAGED_VMID) + src |= (1ull << vmid); + else + src |= (1ull << QCOM_SCM_RM_MANAGED_VMID); + } + + for (i = 0; i < mem_parcel->n_mem_entries; i++) { + src_cpy = src; + ret = qcom_scm_assign_mem(le64_to_cpu(mem_parcel->mem_entries[i].ipa_base), + le64_to_cpu(mem_parcel->mem_entries[i].size), + &src_cpy, &new_perms, 1); + WARN_ON_ONCE(ret); + } + + return ret; +} + +static struct gh_rm_platform_ops qcom_scm_gh_rm_platform_ops = { + .pre_mem_share = qcom_scm_gh_rm_pre_mem_share, + .post_mem_reclaim = qcom_scm_gh_rm_post_mem_reclaim, +}; + +/* {19bd54bd-0b37-571b-946f-609b54539de6} */ +static const uuid_t QCOM_EXT_UUID = + UUID_INIT(0x19bd54bd, 0x0b37, 0x571b, 0x94, 0x6f, 0x60, 0x9b, 0x54, 0x53, 0x9d, 0xe6); + +#define GH_QCOM_EXT_CALL_UUID_ID ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_VENDOR_HYP, 0x3f01) + +static bool gh_has_qcom_extensions(void) +{ + struct arm_smccc_res res; + uuid_t uuid; + + arm_smccc_1_1_smc(GH_QCOM_EXT_CALL_UUID_ID, &res); + + ((u32 *)&uuid.b[0])[0] = lower_32_bits(res.a0); + ((u32 *)&uuid.b[0])[1] = lower_32_bits(res.a1); + ((u32 *)&uuid.b[0])[2] = lower_32_bits(res.a2); + ((u32 *)&uuid.b[0])[3] = lower_32_bits(res.a3); + + return uuid_equal(&uuid, &QCOM_EXT_UUID); +} + +static int __init qcom_gh_platform_hooks_register(void) +{ + if (!gh_has_qcom_extensions()) + return -ENODEV; + + return gh_rm_register_platform_ops(&qcom_scm_gh_rm_platform_ops); +} + +static void __exit qcom_gh_platform_hooks_unregister(void) +{ + gh_rm_unregister_platform_ops(&qcom_scm_gh_rm_platform_ops); +} + +module_init(qcom_gh_platform_hooks_register); +module_exit(qcom_gh_platform_hooks_unregister); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Platform Hooks for Gunyah"); +MODULE_LICENSE("GPL");