drivers: soc: qcom: add qtee_shmbridge driver
Add qtee_shmbridge driver to share memory between non-secure world and trustzone through shared memory bridge. Change-Id: I9b2606726d23056b7acb00bb5e28e93ddf99fddf Signed-off-by: Zhen Kong <zkong@codeaurora.org>
This commit is contained in:
parent
84e0c97df1
commit
8e87859624
4 changed files with 396 additions and 0 deletions
|
@ -666,6 +666,15 @@ config MSM_JTAGV8
|
|||
and ETM registers are saved and restored across power collapse.
|
||||
If unsure, say 'N' here to avoid potential power, performance and
|
||||
memory penalty.
|
||||
config QTEE_SHM_BRIDGE
|
||||
bool "QTI TEE shared memory bridge"
|
||||
help
|
||||
QTEE shared memory bridge driver provides kernel APIs to share
|
||||
memory between trustzone & other VMs through shared memory bridge.
|
||||
It allows kernel clients to create bridge, delete bridge, and do
|
||||
memory sub-allocation and free from the default kernel bridge
|
||||
created by bridge driver.
|
||||
|
||||
endmenu
|
||||
|
||||
source "drivers/soc/qcom/memshare/Kconfig"
|
||||
|
|
|
@ -32,6 +32,7 @@ CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
|
|||
obj-$(CONFIG_QCOM_SCM) += scm.o
|
||||
obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o
|
||||
obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o
|
||||
obj-$(CONFIG_QTEE_SHM_BRIDGE) += qtee_shmbridge.o
|
||||
obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
|
||||
obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o
|
||||
obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
|
||||
|
|
310
drivers/soc/qcom/qtee_shmbridge.c
Normal file
310
drivers/soc/qcom/qtee_shmbridge.c
Normal file
|
@ -0,0 +1,310 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* QTI TEE shared memory bridge driver
|
||||
*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/genalloc.h>
|
||||
|
||||
#include <soc/qcom/scm.h>
|
||||
#include <soc/qcom/qseecomi.h>
|
||||
#include <soc/qcom/qtee_shmbridge.h>
|
||||
#include <soc/qcom/secure_buffer.h>
|
||||
|
||||
#define DEFAULT_BRIDGE_SIZE SZ_4M /*4M*/
|
||||
/*
|
||||
* tz_enable_shm_bridge
|
||||
* smc_id: 0x02000C1C
|
||||
*/
|
||||
#define TZ_SVC_MEMORY_PROTECTION 12
|
||||
|
||||
#define TZ_SHM_BRIDGE_ENABLE \
|
||||
TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_MEMORY_PROTECTION, 0x1C)
|
||||
|
||||
#define TZ_SHM_BRIDGE_ENABLE_PARAM_ID \
|
||||
TZ_SYSCALL_CREATE_PARAM_ID_0
|
||||
|
||||
/*
|
||||
* tz_create_shm_bridge
|
||||
* smc_id: 0x02000C1E
|
||||
*/
|
||||
|
||||
#define TZ_SHM_BRIDGE_CREATE \
|
||||
TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_MEMORY_PROTECTION, 0x1E)
|
||||
|
||||
#define TZ_SHM_BRIDGE_CREATE_PARAM_ID \
|
||||
TZ_SYSCALL_CREATE_PARAM_ID_4( \
|
||||
TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_VAL, \
|
||||
TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_VAL)
|
||||
|
||||
/**
|
||||
* tz_delete_shm_bridge
|
||||
* smc_id: 0x02000C1D
|
||||
*/
|
||||
#define TZ_SHM_BRIDGE_DELETE \
|
||||
TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_MEMORY_PROTECTION, 0x1D)
|
||||
|
||||
#define TZ_SHM_BRIDGE_DELETE_PARAM_ID \
|
||||
TZ_SYSCALL_CREATE_PARAM_ID_1( \
|
||||
TZ_SYSCALL_PARAM_TYPE_VAL)
|
||||
|
||||
#define PERM_BITS 3
|
||||
#define VM_BITS 16
|
||||
#define SELF_OWNER_BIT 1
|
||||
#define SHM_NUM_VM_SHIFT 9
|
||||
|
||||
#define VM_PERM_R PERM_READ
|
||||
#define VM_PERM_W PERM_WRITE
|
||||
|
||||
/* ns_vmids = ns_vmid as destination number is only 1 */
|
||||
#define UPDATE_NS_VMIDS(ns_vmid) ((uint64_t)(ns_vmid))
|
||||
|
||||
/* ns_perms = ns_vm_perm as destination number is only 1 */
|
||||
#define UPDATE_NS_PERMS(ns_vm_perm) ((uint64_t)(ns_vm_perm))
|
||||
|
||||
/* pfn_and_ns_perm_flags = paddr | ns_perms */
|
||||
#define UPDATE_PFN_AND_NS_PERM_FLAGS(paddr, ns_perms) \
|
||||
((uint64_t)(paddr) | (ns_perms))
|
||||
|
||||
|
||||
/* ipfn_and_s_perm_flags = ipaddr | tz_perm */
|
||||
#define UPDATE_IPFN_AND_S_PERM_FLAGS(ipaddr, tz_perm) \
|
||||
((uint64_t)(ipaddr) | (uint64_t)(tz_perm))
|
||||
|
||||
/* size_and_flags when dest_vm is not HYP */
|
||||
#define UPDATE_SIZE_AND_FLAGS(size, destnum) \
|
||||
((size) | (destnum) << SHM_NUM_VM_SHIFT)
|
||||
|
||||
struct bridge_info {
|
||||
phys_addr_t paddr;
|
||||
void *vaddr;
|
||||
size_t size;
|
||||
uint64_t handle;
|
||||
int min_alloc_order;
|
||||
struct gen_pool *genpool;
|
||||
};
|
||||
static struct bridge_info default_bridge;
|
||||
static bool qtee_shmbridge_enabled;
|
||||
|
||||
|
||||
/* enable shared memory bridge mechanism in HYP */
|
||||
static int32_t qtee_shmbridge_enable(bool enable)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
struct scm_desc desc = {0};
|
||||
|
||||
qtee_shmbridge_enabled = false;
|
||||
if (!enable) {
|
||||
pr_warn("shmbridge isn't enabled\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
desc.arginfo = TZ_SHM_BRIDGE_ENABLE_PARAM_ID;
|
||||
ret = scm_call2(TZ_SHM_BRIDGE_ENABLE, &desc);
|
||||
if (ret) {
|
||||
pr_err("Failed to enable shmbridge, rsp = %d, ret = %d\n",
|
||||
desc.ret[0], ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
qtee_shmbridge_enabled = true;
|
||||
pr_warn("shmbridge is enabled\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check whether shmbridge mechanism is enabled in HYP or not */
|
||||
bool qtee_shmbridge_is_enabled(void)
|
||||
{
|
||||
return qtee_shmbridge_enabled;
|
||||
}
|
||||
EXPORT_SYMBOL(qtee_shmbridge_is_enabled);
|
||||
|
||||
/* Register paddr & size as a bridge, return bridge handle */
|
||||
int32_t qtee_shmbridge_register(
|
||||
phys_addr_t paddr,
|
||||
size_t size,
|
||||
uint32_t ns_vmid,
|
||||
uint32_t ns_vm_perm,
|
||||
uint32_t tz_perm,
|
||||
uint64_t *handle)
|
||||
|
||||
{
|
||||
int32_t ret = 0;
|
||||
uint64_t ns_perms = 0;
|
||||
uint64_t destnum = 1;
|
||||
struct scm_desc desc = {0};
|
||||
|
||||
if (!handle) {
|
||||
pr_err("shmb handle pointer is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_debug("%s: paddr %lx, size %zu, ns_vmid %x, ns_vm_perm %x, ns_perms %s, tz_perm %x\n",
|
||||
__func__, (uint64_t)paddr, size, ns_vmid,
|
||||
ns_vm_perm, ns_perms, tz_perm);
|
||||
|
||||
ns_perms = UPDATE_NS_PERMS(ns_vm_perm);
|
||||
desc.arginfo = TZ_SHM_BRIDGE_CREATE_PARAM_ID;
|
||||
desc.args[0] = UPDATE_PFN_AND_NS_PERM_FLAGS(paddr, ns_perms);
|
||||
desc.args[1] = UPDATE_IPFN_AND_S_PERM_FLAGS(paddr, tz_perm);
|
||||
desc.args[2] = UPDATE_SIZE_AND_FLAGS(size, destnum);
|
||||
desc.args[3] = UPDATE_NS_VMIDS(ns_vmid);
|
||||
|
||||
pr_debug("%s: arginfo %lx, desc.args[0] %lx, args[1] %lx, args[2] %lx, args[3] %lx\n",
|
||||
__func__, desc.arginfo, desc.args[0],
|
||||
desc.args[1], desc.args[2], desc.args[3]);
|
||||
ret = scm_call2(TZ_SHM_BRIDGE_CREATE, &desc);
|
||||
if (ret || desc.ret[0]) {
|
||||
pr_err("create shmbridge failed, ret = %d, status = %x\n",
|
||||
ret, desc.ret[0]);
|
||||
return ret;
|
||||
}
|
||||
*handle = desc.ret[1];
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(qtee_shmbridge_register);
|
||||
|
||||
/* Deregister bridge */
|
||||
int32_t qtee_shmbridge_deregister(uint64_t handle)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
struct scm_desc desc = {0};
|
||||
|
||||
desc.arginfo = TZ_SHM_BRIDGE_DELETE_PARAM_ID;
|
||||
desc.args[0] = handle;
|
||||
ret = scm_call2(TZ_SHM_BRIDGE_DELETE, &desc);
|
||||
if (ret) {
|
||||
pr_err("scm_call to delete shmbridge failed, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(qtee_shmbridge_deregister);
|
||||
|
||||
|
||||
/* Sub-allocate from default kernel bridge created by shmb driver */
|
||||
int32_t qtee_shmbridge_allocate_shm(size_t size, struct qtee_shm *shm)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
unsigned long va;
|
||||
|
||||
if (size > DEFAULT_BRIDGE_SIZE) {
|
||||
pr_err("requestd size %zu is larger than bridge size %zu\n",
|
||||
size, DEFAULT_BRIDGE_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(shm)) {
|
||||
pr_err("qtee_shm is NULL\n");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
size = roundup(size, 1 << default_bridge.min_alloc_order);
|
||||
|
||||
va = gen_pool_alloc(default_bridge.genpool, size);
|
||||
if (!va) {
|
||||
pr_err("failed to sub-allocate %zu bytes from bridge\n", size);
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset((void *)va, 0, size);
|
||||
shm->vaddr = (void *)va;
|
||||
shm->paddr = gen_pool_virt_to_phys(default_bridge.genpool, va);
|
||||
shm->size = size;
|
||||
|
||||
pr_debug("%s: shm->paddr %lx, size %zu\n",
|
||||
__func__, (uint64_t)shm->paddr, shm->size);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qtee_shmbridge_allocate_shm);
|
||||
|
||||
|
||||
/* Free buffer that is sub-allocated from default kernel bridge */
|
||||
void qtee_shmbridge_free_shm(struct qtee_shm *shm)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(shm))
|
||||
return;
|
||||
gen_pool_free(default_bridge.genpool, (unsigned long)shm->vaddr,
|
||||
shm->size);
|
||||
}
|
||||
EXPORT_SYMBOL(qtee_shmbridge_free_shm);
|
||||
|
||||
/*
|
||||
* shared memory bridge initialization
|
||||
*
|
||||
*/
|
||||
static int __init qtee_shmbridge_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (default_bridge.vaddr) {
|
||||
pr_warn("qtee shmbridge is already initialized\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* do not enable shm bridge mechanism for now*/
|
||||
ret = qtee_shmbridge_enable(false);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
/* allocate a contiguous buffer */
|
||||
default_bridge.size = DEFAULT_BRIDGE_SIZE;
|
||||
default_bridge.vaddr = kzalloc(default_bridge.size, GFP_KERNEL);
|
||||
if (!default_bridge.vaddr) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
default_bridge.paddr = virt_to_phys(default_bridge.vaddr);
|
||||
|
||||
/*register default bridge*/
|
||||
ret = qtee_shmbridge_register(default_bridge.paddr,
|
||||
default_bridge.size, VMID_HLOS,
|
||||
VM_PERM_R|VM_PERM_W, VM_PERM_R|VM_PERM_W,
|
||||
&default_bridge.handle);
|
||||
if (ret) {
|
||||
pr_err("Failed to register default bridge, size %zu\n",
|
||||
default_bridge.size);
|
||||
goto exit_freebuf;
|
||||
}
|
||||
|
||||
/* create a general mem pool */
|
||||
default_bridge.min_alloc_order = 3; /* 8 byte aligned */
|
||||
default_bridge.genpool = gen_pool_create(
|
||||
default_bridge.min_alloc_order, -1);
|
||||
if (!default_bridge.genpool) {
|
||||
pr_err("gen_pool_add_virt() failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto exit_dereg;
|
||||
}
|
||||
|
||||
gen_pool_set_algo(default_bridge.genpool, gen_pool_best_fit, NULL);
|
||||
ret = gen_pool_add_virt(default_bridge.genpool,
|
||||
(uintptr_t)default_bridge.vaddr,
|
||||
default_bridge.paddr, default_bridge.size, -1);
|
||||
if (ret) {
|
||||
pr_err("gen_pool_add_virt() failed\n");
|
||||
goto exit_destroy_pool;
|
||||
}
|
||||
|
||||
pr_warn("qtee shmbridge registered default bridge with size %d bytes\n",
|
||||
DEFAULT_BRIDGE_SIZE);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_destroy_pool:
|
||||
gen_pool_destroy(default_bridge.genpool);
|
||||
exit_dereg:
|
||||
qtee_shmbridge_deregister(default_bridge.handle);
|
||||
exit_freebuf:
|
||||
kfree(default_bridge.vaddr);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
early_initcall(qtee_shmbridge_init);
|
76
include/soc/qcom/qtee_shmbridge.h
Normal file
76
include/soc/qcom/qtee_shmbridge.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QTEE_SHMBRIDGE_H__
|
||||
#define __QTEE_SHMBRIDGE_H__
|
||||
|
||||
/**
|
||||
* struct qtee_shm - info of shared memory allocated from the default bridge
|
||||
* @ paddr: physical address of the shm allocated from the default bridge
|
||||
* @ vaddr: virtual address of the shm
|
||||
* @ size: size of the shm
|
||||
*/
|
||||
struct qtee_shm {
|
||||
phys_addr_t paddr;
|
||||
void *vaddr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether shmbridge mechanism is enabled in HYP or not
|
||||
*
|
||||
* return true when enabled, false when not enabled
|
||||
*/
|
||||
bool qtee_shmbridge_is_enabled(void);
|
||||
|
||||
/**
|
||||
* Register paddr & size as a bridge, get bridge handle
|
||||
*
|
||||
* @ paddr: paddr of buffer to be turned into bridge
|
||||
* @ size: size of the bridge
|
||||
* @ ns_vmid: non-secure vmid, like VMID_HLOS
|
||||
* @ ns_vm_perm: NS VM permission, like PERM_READ, PERM_WRITE
|
||||
* @ tz_perm: TZ permission
|
||||
* @ *handle: output shmbridge handle
|
||||
*
|
||||
* return success or error
|
||||
*/
|
||||
int32_t qtee_shmbridge_register(
|
||||
phys_addr_t paddr,
|
||||
size_t size,
|
||||
uint32_t ns_vmid,
|
||||
uint32_t ns_vm_perm,
|
||||
uint32_t tz_perm,
|
||||
uint64_t *handle);
|
||||
|
||||
/**
|
||||
* Deregister bridge
|
||||
*
|
||||
* @ handle: shmbridge handle
|
||||
*
|
||||
* return success or error
|
||||
*/
|
||||
int32_t qtee_shmbridge_deregister(uint64_t handle);
|
||||
|
||||
/**
|
||||
* Sub-allocate from default kernel bridge created by shmb driver
|
||||
*
|
||||
* @ size: size of the buffer to be sub-allocated from the bridge
|
||||
* @ *shm: output qtee_shm structure with buffer paddr, vaddr and
|
||||
* size; returns ERR_PTR or NULL otherwise
|
||||
*
|
||||
* return success or error
|
||||
*/
|
||||
int32_t qtee_shmbridge_allocate_shm(size_t size, struct qtee_shm *shm);
|
||||
|
||||
/*
|
||||
* Free buffer that is sub-allocated from default kernel bridge
|
||||
*
|
||||
* @ shm: qtee_shm structure to be freed
|
||||
*
|
||||
*/
|
||||
void qtee_shmbridge_free_shm(struct qtee_shm *shm);
|
||||
|
||||
#endif /*__QTEE_SHMBRIDGE_H__*/
|
Loading…
Reference in a new issue