diff options
| -rw-r--r-- | drivers/soc/qcom/scm_qcpe.c | 408 |
1 files changed, 313 insertions, 95 deletions
diff --git a/drivers/soc/qcom/scm_qcpe.c b/drivers/soc/qcom/scm_qcpe.c index a788c8c3673e..c9df0957f359 100644 --- a/drivers/soc/qcom/scm_qcpe.c +++ b/drivers/soc/qcom/scm_qcpe.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "QSEECOM: %s:%d : " fmt, __func__, __LINE__ + #include <linux/slab.h> #include <linux/io.h> #include <linux/module.h> @@ -18,7 +20,6 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/of.h> #include <asm/cacheflush.h> #include <asm/compiler.h> @@ -30,6 +31,12 @@ #include <linux/habmm.h> +#ifdef CONFIG_GHS_VMM +#include <../../staging/android/ion/ion_hvenv_driver.h> +#include <linux/msm_ion.h> +#include <soc/qcom/qseecomi.h> +#endif + #define SCM_ENOMEM (-5) #define SCM_EOPNOTSUPP (-4) #define SCM_EINVAL_ADDR (-3) @@ -68,7 +75,8 @@ DEFINE_MUTEX(scm_lmh_lock); else \ result = x + y; \ result; \ - }) +}) + /** * struct scm_command - one SCM command buffer * @len: total available memory for command and response @@ -113,6 +121,19 @@ struct scm_response { u32 is_complete; }; +struct scm_extra_arg { + union { + u32 args32[N_EXT_SCM_ARGS]; + u64 args64[N_EXT_SCM_ARGS]; + }; +}; + +struct smc_params_s { + uint64_t fn_id; + uint64_t arginfo; + uint64_t args[MAX_SCM_ARGS]; +} __packed; + #ifdef CONFIG_ARM64 #define R0_STR "x0" @@ -141,6 +162,16 @@ struct scm_response { #endif +static enum scm_interface_version { + SCM_UNKNOWN, + SCM_LEGACY, + SCM_ARMV8_32, + SCM_ARMV8_64, +} scm_version = SCM_UNKNOWN; + +/* This will be set to specify SMC32 or SMC64 */ +static u32 scm_version_mask; + /** * scm_command_to_response() - Get a pointer to a scm_response * @cmd: command @@ -195,76 +226,280 @@ static int scm_remap_error(int err) return -EINVAL; } -#define MAX_SCM_ARGS 10 +#ifdef CONFIG_GHS_VMM +enum SCM_QCPE_IONIZE { + /* args[0] - physical addr, args[1] - length */ + IONIZE_IDX_0, -struct qcpe_msg_s { - uint64_t fn_id; - uint64_t arginfo; - uint64_t args[MAX_SCM_ARGS]; + /* args[1] - physical addr, args[2] - length */ + IONIZE_IDX_1, + + /* args[0] - physical addr, args[1] - length */ + /* args[2] - physical addr, args[3] - length */ + IONIZE_IDX_0_2, + + /* args[2] - physical addr, args[3] - length */ + IONIZE_IDX_2, + + /* args[5] - physical addr, args[6] - length */ + IONIZE_IDX_5 }; +static struct ion_client *ion_clnt; + +static int scm_ion_alloc(size_t len, void **vaddr, + ion_phys_addr_t *paddr, struct ion_handle **ihandle) +{ + struct ion_handle *ihndl = NULL; + void *mvaddr; + ion_phys_addr_t mpaddr; + int ret = 0; + + if (!ion_clnt) { + ion_clnt = hvenv_ion_client_create("qseecom-kernel"); + if (IS_ERR_OR_NULL(ion_clnt)) { + pr_err("Ion client cannot be created\n"); + return SCM_ENOMEM; + } + } + + ihndl = ion_alloc(ion_clnt, len, + SZ_4K, ION_HEAP(ION_QSECOM_HEAP_ID), 0); + if (IS_ERR_OR_NULL(ihandle)) { + pr_err("ION alloc failed\n"); + return SCM_ENOMEM; + } + + mvaddr = ion_map_kernel(ion_clnt, ihndl); + if (IS_ERR_OR_NULL(mvaddr)) { + pr_err("ION memory mapping for image loading failed\n"); + ret = SCM_ENOMEM; + goto free_ion; + } + + ret = ion_phys(ion_clnt, ihndl, &mpaddr, &len); + if (ret) { + pr_err("physical memory retrieval failure\n"); + ret = SCM_ENOMEM; + goto unmap_ion; + + } + + *vaddr = mvaddr; + *paddr = mpaddr; + *ihandle = ihndl; + return ret; + +unmap_ion: + ion_unmap_kernel(ion_clnt, ihndl); +free_ion: + ion_free(ion_clnt, ihndl); + return ret; +} + +static int scm_ionize(enum SCM_QCPE_IONIZE idx, + u64 *args, struct ion_handle **ihandle) +{ + ion_phys_addr_t ion_paddr; + void *krn_vaddr; + void *ion_vaddr; + size_t len, len1; + struct ion_handle *ihndl = NULL; + int ret = 0; + + switch (idx) { + case IONIZE_IDX_0: + len = (size_t)args[1]; + ret = scm_ion_alloc(len, &ion_vaddr, &ion_paddr, &ihndl); + if (ret) + break; + krn_vaddr = phys_to_virt(args[0]); + memcpy(ion_vaddr, krn_vaddr, len); + args[0] = ion_paddr; + break; + + case IONIZE_IDX_1: + len = (size_t)args[2]; + ret = scm_ion_alloc(len, &ion_vaddr, &ion_paddr, &ihndl); + if (ret) + break; + krn_vaddr = phys_to_virt(args[1]); + memcpy(ion_vaddr, krn_vaddr, len); + args[1] = ion_paddr; + break; + + case IONIZE_IDX_0_2: + len = (size_t)args[1] + (size_t)args[3]; + ret = scm_ion_alloc(len, &ion_vaddr, &ion_paddr, &ihndl); + if (ret) + break; + krn_vaddr = phys_to_virt(args[0]); + len = (size_t)args[1]; + memcpy(ion_vaddr, krn_vaddr, len); + args[0] = ion_paddr; + + krn_vaddr = phys_to_virt(args[2]); + len1 = (size_t)args[3]; + memcpy((uint8_t *)ion_vaddr + len, krn_vaddr, len1); + args[2] = ion_paddr; + break; + + case IONIZE_IDX_2: + len = (size_t)args[3]; + ret = scm_ion_alloc(len, &ion_vaddr, &ion_paddr, &ihndl); + if (ret) + break; + krn_vaddr = phys_to_virt(args[2]); + memcpy(ion_vaddr, krn_vaddr, len); + args[2] = ion_paddr; + break; + + case IONIZE_IDX_5: + len = (size_t)args[6]; + ret = scm_ion_alloc(len, &ion_vaddr, &ion_paddr, &ihndl); + if (ret) + break; + krn_vaddr = phys_to_virt(args[5]); + memcpy(ion_vaddr, krn_vaddr, len); + args[5] = ion_paddr; + break; + default: + break; + } + *ihandle = ihndl; + return ret; +} + +static int ionize_buffers(u32 fn_id, + struct smc_params_s *desc, struct ion_handle **ihandle) +{ + struct ion_handle *ihndl = NULL; + int ret = 0; + + switch (fn_id) { + case TZ_OS_APP_LOOKUP_ID: + case TZ_OS_KS_GEN_KEY_ID: + case TZ_OS_KS_DEL_KEY_ID: + case TZ_OS_KS_SET_PIPE_KEY_ID: + case TZ_OS_KS_UPDATE_KEY_ID: + ret = scm_ionize(IONIZE_IDX_0, desc->args, &ihndl); + break; + + case TZ_ES_SAVE_PARTITION_HASH_ID: + ret = scm_ionize(IONIZE_IDX_1, desc->args, &ihndl); + break; + + case TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_ID: + ret = scm_ionize(IONIZE_IDX_2, desc->args, &ihndl); + break; + + case TZ_APP_QSAPP_SEND_DATA_WITH_WHITELIST_ID: + case TZ_APP_GPAPP_OPEN_SESSION_WITH_WHITELIST_ID: + case TZ_APP_GPAPP_INVOKE_COMMAND_WITH_WHITELIST_ID: + ret = scm_ionize(IONIZE_IDX_5, desc->args, &ihndl); + break; + default: + break; + } + *ihandle = ihndl; + return ret; +} + +static void free_ion_buffers(struct ion_handle *ihandle) +{ + ion_free(ion_clnt, ihandle); +} +#endif + static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc) { static bool opened; static u32 handle; - u32 ret; u32 size_bytes; - struct qcpe_msg_s msg; + int i; + uint64_t arglen = desc->arginfo & 0xf; + struct smc_params_s smc_params = {0,}; + int ret; +#ifdef CONFIG_GHS_VMM + struct ion_handle *ihandle = NULL; +#endif - pr_info("scm_call_qcpe: IN: 0x%x, 0x%x, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx", - fn_id, desc->arginfo, desc->args[0], desc->args[1], - desc->args[2], desc->args[3], desc->args[4], - desc->args[5], desc->args[6]); + pr_info("\nscm_call_qcpe: IN: 0x%x, 0x%x, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx\n", + fn_id, desc->arginfo, desc->args[0], desc->args[1], + desc->args[2], desc->args[3], desc->x5); if (!opened) { ret = habmm_socket_open(&handle, MM_QCPE_VM1, 0, 0); if (ret) { - pr_err("scm_call_qcpe: habmm_socket_open failed with ret = %d", - ret); + pr_err( + "scm_call_qcpe: habmm_socket_open failed with ret = %d", + ret); return ret; } opened = true; } - msg.fn_id = fn_id | 0x40000000; /* SMC64_MASK */ - msg.arginfo = desc->arginfo; - msg.args[0] = desc->args[0]; - msg.args[1] = desc->args[1]; - msg.args[2] = desc->args[2]; - msg.args[3] = desc->x5; - msg.args[4] = 0; + smc_params.fn_id = fn_id | scm_version_mask; + smc_params.arginfo = desc->arginfo; + smc_params.args[0] = desc->args[0]; + smc_params.args[1] = desc->args[1]; + smc_params.args[2] = desc->args[2]; - ret = habmm_socket_send(handle, &msg, sizeof(msg), 0); - if (ret) { - pr_err("scm_call_qcpe: habmm_socket_send failed with ret = %d", - ret); - return ret; - } +#ifdef CONFIG_GHS_VMM + if (arglen <= N_REGISTER_ARGS) { + smc_params.args[FIRST_EXT_ARG_IDX] = desc->x5; + } else { + struct scm_extra_arg *argbuf = + (struct scm_extra_arg *)desc->extra_arg_buf; + int j = 0; - size_bytes = sizeof(msg); - memset(&msg, 0x0, sizeof(msg)); + if (scm_version == SCM_ARMV8_64) + for (i = FIRST_EXT_ARG_IDX; i < MAX_SCM_ARGS; i++) + smc_params.args[i] = argbuf->args64[j++]; + else + for (i = FIRST_EXT_ARG_IDX; i < MAX_SCM_ARGS; i++) + smc_params.args[i] = argbuf->args32[j++]; + } - ret = habmm_socket_recv(handle, &msg, &size_bytes, 0, 0); - if (ret) { - pr_err("scm_call_qcpe: habmm_socket_recv failed with ret = %d", - ret); + ret = ionize_buffers(fn_id & (~SMC64_MASK), &smc_params, &ihandle); + if (ret) return ret; - } +#else + smc_params.args[3] = desc->x5; + smc_params.args[4] = 0; +#endif - if (size_bytes != sizeof(msg)) { - pr_err("scm_call_qcpe: expected size: %lu, actual=%u\n", - sizeof(msg), size_bytes); - return SCM_ERROR; - } + ret = habmm_socket_send(handle, &smc_params, sizeof(smc_params), 0); + if (ret) + goto err_ret; - desc->ret[0] = msg.args[1]; - desc->ret[1] = msg.args[2]; - desc->ret[2] = msg.args[3]; + size_bytes = sizeof(smc_params); + memset(&smc_params, 0x0, sizeof(smc_params)); - pr_info("scm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx, 0x%llx", - msg.args[0], msg.args[1], msg.args[2], msg.args[3]); + ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, 0); + if (ret) + goto err_ret; + + if (size_bytes != sizeof(smc_params)) { + pr_err("scm_call_qcpe: expected size: %lu, actual=%u\n", + sizeof(smc_params), size_bytes); + ret = SCM_ERROR; + goto err_ret; + } - return msg.args[0]; + desc->ret[0] = smc_params.args[1]; + desc->ret[1] = smc_params.args[2]; + desc->ret[2] = smc_params.args[3]; + ret = smc_params.args[0]; + pr_info("\nscm_call_qcpe: OUT: 0x%llx, 0x%llx, 0x%llx, 0x%llx", + smc_params.args[0], desc->ret[0], desc->ret[1], desc->ret[2]); + +err_ret: +#ifdef CONFIG_GHS_VMM + if (ihandle) + free_ion_buffers(ihandle); +#endif + return ret; } static u32 smc(u32 cmd_addr) @@ -324,7 +559,7 @@ static void scm_inv_range(unsigned long start, unsigned long end) outer_inv_range(start, end); while (start < end) { asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) - : "memory"); + : "memory"); start += cacheline_size; } mb(); /* Make sure memory is visible to TZ */ @@ -357,9 +592,9 @@ static void scm_inv_range(unsigned long start, unsigned long end) */ static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf, - size_t cmd_len, void *resp_buf, size_t resp_len, - struct scm_command *scm_buf, - size_t scm_buf_length) + size_t cmd_len, void *resp_buf, size_t resp_len, + struct scm_command *scm_buf, + size_t scm_buf_length) { int ret; struct scm_response *rsp; @@ -403,15 +638,15 @@ static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf, * since we want the first attempt to be the "fastpath". */ static int _scm_call_retry(u32 svc_id, u32 cmd_id, const void *cmd_buf, - size_t cmd_len, void *resp_buf, size_t resp_len, - struct scm_command *cmd, - size_t len) + size_t cmd_len, void *resp_buf, size_t resp_len, + struct scm_command *cmd, + size_t len) { int ret, retry_count = 0; do { ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, - resp_buf, resp_len, cmd, len); + resp_buf, resp_len, cmd, len); if (ret == SCM_EBUSY) msleep(SCM_EBUSY_WAIT_MS); if (retry_count == 33) @@ -447,28 +682,11 @@ int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf, memset(scm_buf, 0, scm_buf_len); ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf, - resp_len, scm_buf, len); + resp_len, scm_buf, len); return ret; } -struct scm_extra_arg { - union { - u32 args32[N_EXT_SCM_ARGS]; - u64 args64[N_EXT_SCM_ARGS]; - }; -}; - -static enum scm_interface_version { - SCM_UNKNOWN, - SCM_LEGACY, - SCM_ARMV8_32, - SCM_ARMV8_64, -} scm_version = SCM_UNKNOWN; - -/* This will be set to specify SMC32 or SMC64 */ -static u32 scm_version_mask; - bool is_scm_armv8(void) { int ret; @@ -478,7 +696,7 @@ bool is_scm_armv8(void) if (likely(scm_version != SCM_UNKNOWN)) return (scm_version == SCM_ARMV8_32) || - (scm_version == SCM_ARMV8_64); + (scm_version == SCM_ARMV8_64); /* * This is a one time check that runs on the first ever * invocation of is_scm_armv8. We might be called in atomic @@ -515,7 +733,7 @@ bool is_scm_armv8(void) scm_version_mask = SMC64_MASK; pr_debug("scm_call: scm version is %x, mask is %x\n", scm_version, - scm_version_mask); + scm_version_mask); return (scm_version == SCM_ARMV8_32) || (scm_version == SCM_ARMV8_64); @@ -557,7 +775,7 @@ static int allocate_extra_arg_buffer(struct scm_desc *desc, gfp_t flags) desc->x5 = virt_to_phys(argbuf); __cpuc_flush_dcache_area(argbuf, argbuflen); outer_flush_range(virt_to_phys(argbuf), - virt_to_phys(argbuf) + argbuflen); + virt_to_phys(argbuf) + argbuflen); return 0; } @@ -582,7 +800,7 @@ static int allocate_extra_arg_buffer(struct scm_desc *desc, gfp_t flags) * Note that cache maintenance on the argument buffer (desc->args) is taken care * of by scm_call2; however, callers are responsible for any other cached * buffers passed over to the secure world. -*/ + */ int scm_call2(u32 fn_id, struct scm_desc *desc) { int arglen = desc->arginfo & 0xf; @@ -652,16 +870,16 @@ int scm_call2_atomic(u32 fn_id, struct scm_desc *desc) x0 = fn_id | BIT(SMC_ATOMIC_SYSCALL) | scm_version_mask; pr_debug("scm_call: func id %#llx, args: %#x, %#llx, %#llx, %#llx, %#llx\n", - x0, desc->arginfo, desc->args[0], desc->args[1], - desc->args[2], desc->x5); + x0, desc->arginfo, desc->args[0], desc->args[1], + desc->args[2], desc->x5); ret = scm_call_qcpe(x0, desc); if (ret < 0) pr_err("scm_call failed: func id %#llx, arginfo: %#x, args: %#llx, %#llx, %#llx, %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n", - x0, desc->arginfo, desc->args[0], desc->args[1], - desc->args[2], desc->x5, ret, desc->ret[0], - desc->ret[1], desc->ret[2]); + x0, desc->arginfo, desc->args[0], desc->args[1], + desc->args[2], desc->x5, ret, desc->ret[0], + desc->ret[1], desc->ret[2]); if (arglen > N_REGISTER_ARGS) kfree(desc->extra_arg_buf); @@ -703,10 +921,10 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, return -ENOMEM; ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf, - resp_len, cmd, len); + resp_len, cmd, len); if (unlikely(ret == SCM_EBUSY)) ret = _scm_call_retry(svc_id, cmd_id, cmd_buf, cmd_len, - resp_buf, resp_len, cmd, PAGE_ALIGN(len)); + resp_buf, resp_len, cmd, PAGE_ALIGN(len)); kfree(cmd); return ret; } @@ -715,9 +933,9 @@ EXPORT_SYMBOL(scm_call); #define SCM_CLASS_REGISTER (0x2 << 8) #define SCM_MASK_IRQS BIT(5) #define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ - SCM_CLASS_REGISTER | \ - SCM_MASK_IRQS | \ - (n & 0xf)) + SCM_CLASS_REGISTER | \ + SCM_MASK_IRQS | \ + (n & 0xf)) /** * scm_call_atomic1() - Send an atomic SCM command with one argument @@ -914,7 +1132,7 @@ EXPORT_SYMBOL(scm_call_atomic4_3); * uninterruptable, atomic and SMP safe. */ s32 scm_call_atomic5_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, - u32 arg3, u32 arg4, u32 arg5, u32 *ret1, u32 *ret2, u32 *ret3) + u32 arg3, u32 arg4, u32 arg5, u32 *ret1, u32 *ret2, u32 *ret3) { int ret; int context_id; @@ -991,8 +1209,8 @@ EXPORT_SYMBOL(scm_get_version); u32 scm_io_read(phys_addr_t address) { struct scm_desc desc = { - .args[0] = address, - .arginfo = SCM_ARGS(1), + .args[0] = address, + .arginfo = SCM_ARGS(1), }; if (!is_scm_armv8()) @@ -1011,12 +1229,12 @@ int scm_io_write(phys_addr_t address, u32 val) ret = scm_call_atomic2(SCM_SVC_IO, SCM_IO_WRITE, address, val); else { struct scm_desc desc = { - .args[0] = address, - .args[1] = val, - .arginfo = SCM_ARGS(2), + .args[0] = address, + .args[1] = val, + .arginfo = SCM_ARGS(2), }; ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_IO, SCM_IO_WRITE), - &desc); + &desc); } return ret; } @@ -1032,7 +1250,7 @@ int scm_is_call_available(u32 svc_id, u32 cmd_id) u32 svc_cmd = (svc_id << 10) | cmd_id; ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd, - sizeof(svc_cmd), &ret_val, sizeof(ret_val)); + sizeof(svc_cmd), &ret_val, sizeof(ret_val)); if (!ret && ret_val) return 1; else @@ -1130,7 +1348,7 @@ bool scm_is_secure_device(void) desc.arginfo = 0; if (!is_scm_armv8()) { ret = scm_call(SCM_SVC_INFO, TZ_INFO_GET_SECURE_STATE, NULL, - 0, &resp, sizeof(resp)); + 0, &resp, sizeof(resp)); } else { ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, TZ_INFO_GET_SECURE_STATE), |
