1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver |
4 | * |
5 | * Copyright (C) 2022, STMicroelectronics - All Rights Reserved |
6 | */ |
7 | |
8 | #include <linux/tee_drv.h> |
9 | |
10 | #include "stm32-bsec-optee-ta.h" |
11 | |
12 | /* |
13 | * Read OTP memory |
14 | * |
15 | * [in] value[0].a OTP start offset in byte |
16 | * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock) |
17 | * [out] memref[1].buffer Output buffer to store read values |
18 | * [out] memref[1].size Size of OTP to be read |
19 | * |
20 | * Return codes: |
21 | * TEE_SUCCESS - Invoke command success |
22 | * TEE_ERROR_BAD_PARAMETERS - Incorrect input param |
23 | * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller |
24 | */ |
25 | #define PTA_BSEC_READ_MEM 0x0 |
26 | |
27 | /* |
28 | * Write OTP memory |
29 | * |
30 | * [in] value[0].a OTP start offset in byte |
31 | * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock) |
32 | * [in] memref[1].buffer Input buffer to read values |
33 | * [in] memref[1].size Size of OTP to be written |
34 | * |
35 | * Return codes: |
36 | * TEE_SUCCESS - Invoke command success |
37 | * TEE_ERROR_BAD_PARAMETERS - Incorrect input param |
38 | * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller |
39 | */ |
40 | #define PTA_BSEC_WRITE_MEM 0x1 |
41 | |
42 | /* value of PTA_BSEC access type = value[in] b */ |
43 | #define SHADOW_ACCESS 0 |
44 | #define FUSE_ACCESS 1 |
45 | #define LOCK_ACCESS 2 |
46 | |
47 | /* Bitfield definition for LOCK status */ |
48 | #define LOCK_PERM BIT(30) |
49 | |
50 | /* OP-TEE STM32MP BSEC TA UUID */ |
51 | static const uuid_t stm32mp_bsec_ta_uuid = |
52 | UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5, |
53 | 0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03); |
54 | |
55 | /* |
56 | * Check whether this driver supports the BSEC TA in the TEE instance |
57 | * represented by the params (ver/data) to this function. |
58 | */ |
59 | static int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver, |
60 | const void *data) |
61 | { |
62 | /* Currently this driver only supports GP compliant, OP-TEE based TA */ |
63 | if ((ver->impl_id == TEE_IMPL_ID_OPTEE) && |
64 | (ver->gen_caps & TEE_GEN_CAP_GP)) |
65 | return 1; |
66 | else |
67 | return 0; |
68 | } |
69 | |
70 | /* Open a session to OP-TEE for STM32MP BSEC TA */ |
71 | static int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id) |
72 | { |
73 | struct tee_ioctl_open_session_arg sess_arg; |
74 | int rc; |
75 | |
76 | memset(&sess_arg, 0, sizeof(sess_arg)); |
77 | export_uuid(dst: sess_arg.uuid, src: &stm32mp_bsec_ta_uuid); |
78 | sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; |
79 | sess_arg.num_params = 0; |
80 | |
81 | rc = tee_client_open_session(ctx, arg: &sess_arg, NULL); |
82 | if ((rc < 0) || (sess_arg.ret != 0)) { |
83 | pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n" , |
84 | __func__, sess_arg.ret, rc); |
85 | if (!rc) |
86 | rc = -EINVAL; |
87 | } else { |
88 | *id = sess_arg.session; |
89 | } |
90 | |
91 | return rc; |
92 | } |
93 | |
94 | /* close a session to OP-TEE for STM32MP BSEC TA */ |
95 | static void stm32_bsec_ta_close_session(void *ctx, u32 id) |
96 | { |
97 | tee_client_close_session(ctx, session: id); |
98 | } |
99 | |
100 | /* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */ |
101 | int stm32_bsec_optee_ta_open(struct tee_context **ctx) |
102 | { |
103 | struct tee_context *tee_ctx; |
104 | u32 session_id; |
105 | int rc; |
106 | |
107 | /* Open context with TEE driver */ |
108 | tee_ctx = tee_client_open_context(NULL, match: stm32_bsec_optee_ta_match, NULL, NULL); |
109 | if (IS_ERR(ptr: tee_ctx)) { |
110 | rc = PTR_ERR(ptr: tee_ctx); |
111 | if (rc == -ENOENT) |
112 | return -EPROBE_DEFER; |
113 | pr_err("%s: tee_client_open_context failed (%d)\n" , __func__, rc); |
114 | |
115 | return rc; |
116 | } |
117 | |
118 | /* Check STM32MP BSEC TA presence */ |
119 | rc = stm32_bsec_ta_open_session(ctx: tee_ctx, id: &session_id); |
120 | if (rc) { |
121 | tee_client_close_context(ctx: tee_ctx); |
122 | return rc; |
123 | } |
124 | |
125 | stm32_bsec_ta_close_session(ctx: tee_ctx, id: session_id); |
126 | |
127 | *ctx = tee_ctx; |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | /* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */ |
133 | void stm32_bsec_optee_ta_close(void *ctx) |
134 | { |
135 | tee_client_close_context(ctx); |
136 | } |
137 | |
138 | /* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */ |
139 | int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset, |
140 | void *buf, size_t bytes) |
141 | { |
142 | struct tee_shm *shm; |
143 | struct tee_ioctl_invoke_arg arg; |
144 | struct tee_param param[2]; |
145 | u8 *shm_buf; |
146 | u32 start, num_bytes; |
147 | int ret; |
148 | u32 session_id; |
149 | |
150 | ret = stm32_bsec_ta_open_session(ctx, id: &session_id); |
151 | if (ret) |
152 | return ret; |
153 | |
154 | memset(&arg, 0, sizeof(arg)); |
155 | memset(¶m, 0, sizeof(param)); |
156 | |
157 | arg.func = PTA_BSEC_READ_MEM; |
158 | arg.session = session_id; |
159 | arg.num_params = 2; |
160 | |
161 | /* align access on 32bits */ |
162 | start = ALIGN_DOWN(offset, 4); |
163 | num_bytes = round_up(offset + bytes - start, 4); |
164 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; |
165 | param[0].u.value.a = start; |
166 | param[0].u.value.b = SHADOW_ACCESS; |
167 | |
168 | shm = tee_shm_alloc_kernel_buf(ctx, size: num_bytes); |
169 | if (IS_ERR(ptr: shm)) { |
170 | ret = PTR_ERR(ptr: shm); |
171 | goto out_tee_session; |
172 | } |
173 | |
174 | param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
175 | param[1].u.memref.shm = shm; |
176 | param[1].u.memref.size = num_bytes; |
177 | |
178 | ret = tee_client_invoke_func(ctx, arg: &arg, param); |
179 | if (ret < 0 || arg.ret != 0) { |
180 | pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n" , |
181 | arg.ret, ret); |
182 | if (!ret) |
183 | ret = -EIO; |
184 | } |
185 | if (!ret) { |
186 | shm_buf = tee_shm_get_va(shm, offs: 0); |
187 | if (IS_ERR(ptr: shm_buf)) { |
188 | ret = PTR_ERR(ptr: shm_buf); |
189 | pr_err("tee_shm_get_va failed for transmit (%d)\n" , ret); |
190 | } else { |
191 | /* read data from 32 bits aligned buffer */ |
192 | memcpy(buf, &shm_buf[offset % 4], bytes); |
193 | } |
194 | } |
195 | |
196 | tee_shm_free(shm); |
197 | |
198 | out_tee_session: |
199 | stm32_bsec_ta_close_session(ctx, id: session_id); |
200 | |
201 | return ret; |
202 | } |
203 | |
204 | /* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */ |
205 | int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower, |
206 | unsigned int offset, void *buf, size_t bytes) |
207 | { struct tee_shm *shm; |
208 | struct tee_ioctl_invoke_arg arg; |
209 | struct tee_param param[2]; |
210 | u8 *shm_buf; |
211 | int ret; |
212 | u32 session_id; |
213 | |
214 | ret = stm32_bsec_ta_open_session(ctx, id: &session_id); |
215 | if (ret) |
216 | return ret; |
217 | |
218 | /* Allow only writing complete 32-bits aligned words */ |
219 | if ((bytes % 4) || (offset % 4)) |
220 | return -EINVAL; |
221 | |
222 | memset(&arg, 0, sizeof(arg)); |
223 | memset(¶m, 0, sizeof(param)); |
224 | |
225 | arg.func = PTA_BSEC_WRITE_MEM; |
226 | arg.session = session_id; |
227 | arg.num_params = 2; |
228 | |
229 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; |
230 | param[0].u.value.a = offset; |
231 | param[0].u.value.b = FUSE_ACCESS; |
232 | |
233 | shm = tee_shm_alloc_kernel_buf(ctx, size: bytes); |
234 | if (IS_ERR(ptr: shm)) { |
235 | ret = PTR_ERR(ptr: shm); |
236 | goto out_tee_session; |
237 | } |
238 | |
239 | param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
240 | param[1].u.memref.shm = shm; |
241 | param[1].u.memref.size = bytes; |
242 | |
243 | shm_buf = tee_shm_get_va(shm, offs: 0); |
244 | if (IS_ERR(ptr: shm_buf)) { |
245 | ret = PTR_ERR(ptr: shm_buf); |
246 | pr_err("tee_shm_get_va failed for transmit (%d)\n" , ret); |
247 | tee_shm_free(shm); |
248 | |
249 | goto out_tee_session; |
250 | } |
251 | |
252 | memcpy(shm_buf, buf, bytes); |
253 | |
254 | ret = tee_client_invoke_func(ctx, arg: &arg, param); |
255 | if (ret < 0 || arg.ret != 0) { |
256 | pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n" , arg.ret, ret); |
257 | if (!ret) |
258 | ret = -EIO; |
259 | } |
260 | pr_debug("Write OTPs %d to %zu, ret=%d\n" , offset / 4, (offset + bytes) / 4, ret); |
261 | |
262 | /* Lock the upper OTPs with ECC protection, word programming only */ |
263 | if (!ret && ((offset + bytes) >= (lower * 4))) { |
264 | u32 start, nb_lock; |
265 | u32 *lock = (u32 *)shm_buf; |
266 | int i; |
267 | |
268 | /* |
269 | * don't lock the lower OTPs, no ECC protection and incremental |
270 | * bit programming, a second write is allowed |
271 | */ |
272 | start = max_t(u32, offset, lower * 4); |
273 | nb_lock = (offset + bytes - start) / 4; |
274 | |
275 | param[0].u.value.a = start; |
276 | param[0].u.value.b = LOCK_ACCESS; |
277 | param[1].u.memref.size = nb_lock * 4; |
278 | |
279 | for (i = 0; i < nb_lock; i++) |
280 | lock[i] = LOCK_PERM; |
281 | |
282 | ret = tee_client_invoke_func(ctx, arg: &arg, param); |
283 | if (ret < 0 || arg.ret != 0) { |
284 | pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n" , arg.ret, ret); |
285 | if (!ret) |
286 | ret = -EIO; |
287 | } |
288 | pr_debug("Lock upper OTPs %d to %d, ret=%d\n" , |
289 | start / 4, start / 4 + nb_lock, ret); |
290 | } |
291 | |
292 | tee_shm_free(shm); |
293 | |
294 | out_tee_session: |
295 | stm32_bsec_ta_close_session(ctx, id: session_id); |
296 | |
297 | return ret; |
298 | } |
299 | |