lkml.org 
[lkml]   [2023]   [Jun]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v14 15/25] virt: gunyah: Add Qualcomm Gunyah platform ops
    Date
    Qualcomm platforms have a firmware entity which performs access control
    to physical pages. Dynamically started Gunyah virtual machines use the
    QCOM_SCM_RM_MANAGED_VMID for access. Linux thus needs to assign access
    to the memory used by guest VMs. Gunyah doesn't do this operation for us
    since it is the current VM (typically VMID_HLOS) delegating the access
    and not Gunyah itself. Use the Gunyah platform ops to achieve this so
    that only Qualcomm platforms attempt to make the needed SCM calls.

    Reviewed-by: Alex Elder <elder@linaro.org>
    Co-developed-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
    Signed-off-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
    Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
    ---
    drivers/virt/gunyah/Kconfig | 13 +++
    drivers/virt/gunyah/Makefile | 1 +
    drivers/virt/gunyah/gunyah_qcom.c | 153 ++++++++++++++++++++++++++++++
    3 files changed, 167 insertions(+)
    create mode 100644 drivers/virt/gunyah/gunyah_qcom.c

    diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig
    index de815189dab6c..0421b751aad4f 100644
    --- a/drivers/virt/gunyah/Kconfig
    +++ b/drivers/virt/gunyah/Kconfig
    @@ -5,6 +5,7 @@ config GUNYAH
    depends on ARM64
    depends on MAILBOX
    select GUNYAH_PLATFORM_HOOKS
    + 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
    @@ -15,3 +16,15 @@ 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.
    diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile
    index 4fbeee521d60a..2aa9ff038ed02 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-y += rsc_mgr.o rsc_mgr_rpc.o vm_mgr.o vm_mgr_mm.o
    obj-$(CONFIG_GUNYAH) += gunyah.o
    diff --git a/drivers/virt/gunyah/gunyah_qcom.c b/drivers/virt/gunyah/gunyah_qcom.c
    new file mode 100644
    index 0000000000000..f06a598f2e1b3
    --- /dev/null
    +++ b/drivers/virt/gunyah/gunyah_qcom.c
    @@ -0,0 +1,153 @@
    +// SPDX-License-Identifier: GPL-2.0-only
    +/*
    + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
    + */
    +
    +#include <linux/arm-smccc.h>
    +#include <linux/gunyah_rsc_mgr.h>
    +#include <linux/module.h>
    +#include <linux/firmware/qcom/qcom_scm.h>
    +#include <linux/types.h>
    +#include <linux/uuid.h>
    +
    +#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 = BIT_ULL(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].phys_addr),
    + le64_to_cpu(mem_parcel->mem_entries[i].size),
    + &src_cpy, new_perms, mem_parcel->n_acl_entries);
    + if (ret)
    + break;
    + }
    +
    + if (!ret)
    + goto out;
    +
    + 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 |= BIT_ULL(vmid);
    + else
    + src |= BIT_ULL(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].phys_addr),
    + le64_to_cpu(mem_parcel->mem_entries[i].size),
    + &src_cpy, new_perms, 1));
    + }
    +
    +out:
    + 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].phys_addr),
    + 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;
    + u32 *up;
    +
    + arm_smccc_1_1_smc(GH_QCOM_EXT_CALL_UUID_ID, &res);
    +
    + up = (u32 *)&uuid.b[0];
    + up[0] = lower_32_bits(res.a0);
    + up[1] = lower_32_bits(res.a1);
    + up[2] = lower_32_bits(res.a2);
    + up[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");
    --
    2.40.0
    \
     
     \ /
      Last update: 2023-06-13 19:26    [W:4.124 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site