// SPDX-License-Identifier: GPL-2.0-or-later /* * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver * * Copyright (C) 2022, STMicroelectronics - All Rights Reserved */ #include <linux/tee_drv.h> #include "stm32-bsec-optee-ta.h" /* * Read OTP memory * * [in] value[0].a OTP start offset in byte * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock) * [out] memref[1].buffer Output buffer to store read values * [out] memref[1].size Size of OTP to be read * * Return codes: * TEE_SUCCESS - Invoke command success * TEE_ERROR_BAD_PARAMETERS - Incorrect input param * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller */ #define PTA_BSEC_READ_MEM 0x0 /* * Write OTP memory * * [in] value[0].a OTP start offset in byte * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock) * [in] memref[1].buffer Input buffer to read values * [in] memref[1].size Size of OTP to be written * * Return codes: * TEE_SUCCESS - Invoke command success * TEE_ERROR_BAD_PARAMETERS - Incorrect input param * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller */ #define PTA_BSEC_WRITE_MEM 0x1 /* value of PTA_BSEC access type = value[in] b */ #define SHADOW_ACCESS 0 #define FUSE_ACCESS 1 #define LOCK_ACCESS 2 /* Bitfield definition for LOCK status */ #define LOCK_PERM BIT(30) /* OP-TEE STM32MP BSEC TA UUID */ static const uuid_t stm32mp_bsec_ta_uuid = UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5, 0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03); /* * Check whether this driver supports the BSEC TA in the TEE instance * represented by the params (ver/data) to this function. */ static int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver, const void *data) { /* Currently this driver only supports GP compliant, OP-TEE based TA */ if ((ver->impl_id == TEE_IMPL_ID_OPTEE) && (ver->gen_caps & TEE_GEN_CAP_GP)) return 1; else return 0; } /* Open a session to OP-TEE for STM32MP BSEC TA */ static int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id) { struct tee_ioctl_open_session_arg sess_arg; int rc; memset(&sess_arg, 0, sizeof(sess_arg)); export_uuid(sess_arg.uuid, &stm32mp_bsec_ta_uuid); sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; sess_arg.num_params = 0; rc = tee_client_open_session(ctx, &sess_arg, NULL); if ((rc < 0) || (sess_arg.ret != 0)) { pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n", __func__, sess_arg.ret, rc); if (!rc) rc = -EINVAL; } else { *id = sess_arg.session; } return rc; } /* close a session to OP-TEE for STM32MP BSEC TA */ static void stm32_bsec_ta_close_session(void *ctx, u32 id) { tee_client_close_session(ctx, id); } /* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */ int stm32_bsec_optee_ta_open(struct tee_context **ctx) { struct tee_context *tee_ctx; u32 session_id; int rc; /* Open context with TEE driver */ tee_ctx = tee_client_open_context(NULL, stm32_bsec_optee_ta_match, NULL, NULL); if (IS_ERR(tee_ctx)) { rc = PTR_ERR(tee_ctx); if (rc == -ENOENT) return -EPROBE_DEFER; pr_err("%s: tee_client_open_context failed (%d)\n", __func__, rc); return rc; } /* Check STM32MP BSEC TA presence */ rc = stm32_bsec_ta_open_session(tee_ctx, &session_id); if (rc) { tee_client_close_context(tee_ctx); return rc; } stm32_bsec_ta_close_session(tee_ctx, session_id); *ctx = tee_ctx; return 0; } /* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */ void stm32_bsec_optee_ta_close(void *ctx) { tee_client_close_context(ctx); } /* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */ int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset, void *buf, size_t bytes) { struct tee_shm *shm; struct tee_ioctl_invoke_arg arg; struct tee_param param[2]; u8 *shm_buf; u32 start, num_bytes; int ret; u32 session_id; ret = stm32_bsec_ta_open_session(ctx, &session_id); if (ret) return ret; memset(&arg, 0, sizeof(arg)); memset(¶m, 0, sizeof(param)); arg.func = PTA_BSEC_READ_MEM; arg.session = session_id; arg.num_params = 2; /* align access on 32bits */ start = ALIGN_DOWN(offset, 4); num_bytes = round_up(offset + bytes - start, 4); param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; param[0].u.value.a = start; param[0].u.value.b = SHADOW_ACCESS; shm = tee_shm_alloc_kernel_buf(ctx, num_bytes); if (IS_ERR(shm)) { ret = PTR_ERR(shm); goto out_tee_session; } param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; param[1].u.memref.shm = shm; param[1].u.memref.size = num_bytes; ret = tee_client_invoke_func(ctx, &arg, param); if (ret < 0 || arg.ret != 0) { pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret); if (!ret) ret = -EIO; } if (!ret) { shm_buf = tee_shm_get_va(shm, 0); if (IS_ERR(shm_buf)) { ret = PTR_ERR(shm_buf); pr_err("tee_shm_get_va failed for transmit (%d)\n", ret); } else { /* read data from 32 bits aligned buffer */ memcpy(buf, &shm_buf[offset % 4], bytes); } } tee_shm_free(shm); out_tee_session: stm32_bsec_ta_close_session(ctx, session_id); return ret; } /* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */ int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower, unsigned int offset, void *buf, size_t bytes) { struct tee_shm *shm; struct tee_ioctl_invoke_arg arg; struct tee_param param[2]; u8 *shm_buf; int ret; u32 session_id; ret = stm32_bsec_ta_open_session(ctx, &session_id); if (ret) return ret; /* Allow only writing complete 32-bits aligned words */ if ((bytes % 4) || (offset % 4)) return -EINVAL; memset(&arg, 0, sizeof(arg)); memset(¶m, 0, sizeof(param)); arg.func = PTA_BSEC_WRITE_MEM; arg.session = session_id; arg.num_params = 2; param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; param[0].u.value.a = offset; param[0].u.value.b = FUSE_ACCESS; shm = tee_shm_alloc_kernel_buf(ctx, bytes); if (IS_ERR(shm)) { ret = PTR_ERR(shm); goto out_tee_session; } param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; param[1].u.memref.shm = shm; param[1].u.memref.size = bytes; shm_buf = tee_shm_get_va(shm, 0); if (IS_ERR(shm_buf)) { ret = PTR_ERR(shm_buf); pr_err("tee_shm_get_va failed for transmit (%d)\n", ret); tee_shm_free(shm); goto out_tee_session; } memcpy(shm_buf, buf, bytes); ret = tee_client_invoke_func(ctx, &arg, param); if (ret < 0 || arg.ret != 0) { pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret); if (!ret) ret = -EIO; } pr_debug("Write OTPs %d to %zu, ret=%d\n", offset / 4, (offset + bytes) / 4, ret); /* Lock the upper OTPs with ECC protection, word programming only */ if (!ret && ((offset + bytes) >= (lower * 4))) { u32 start, nb_lock; u32 *lock = (u32 *)shm_buf; int i; /* * don't lock the lower OTPs, no ECC protection and incremental * bit programming, a second write is allowed */ start = max_t(u32, offset, lower * 4); nb_lock = (offset + bytes - start) / 4; param[0].u.value.a = start; param[0].u.value.b = LOCK_ACCESS; param[1].u.memref.size = nb_lock * 4; for (i = 0; i < nb_lock; i++) lock[i] = LOCK_PERM; ret = tee_client_invoke_func(ctx, &arg, param); if (ret < 0 || arg.ret != 0) { pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret); if (!ret) ret = -EIO; } pr_debug("Lock upper OTPs %d to %d, ret=%d\n", start / 4, start / 4 + nb_lock, ret); } tee_shm_free(shm); out_tee_session: stm32_bsec_ta_close_session(ctx, session_id); return ret; }