Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | // 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; } |