Commit | Line | Data |
---|---|---|
6a0bc352 PD |
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(sess_arg.uuid, &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, &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, 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, stm32_bsec_optee_ta_match, NULL, NULL); | |
109 | if (IS_ERR(tee_ctx)) { | |
110 | rc = PTR_ERR(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(tee_ctx, &session_id); | |
120 | if (rc) { | |
121 | tee_client_close_context(tee_ctx); | |
122 | return rc; | |
123 | } | |
124 | ||
125 | stm32_bsec_ta_close_session(tee_ctx, 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, &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, num_bytes); | |
169 | if (IS_ERR(shm)) { | |
170 | ret = PTR_ERR(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, 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, 0); | |
187 | if (IS_ERR(shm_buf)) { | |
188 | ret = PTR_ERR(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, 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, &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, bytes); | |
234 | if (IS_ERR(shm)) { | |
235 | ret = PTR_ERR(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, 0); | |
244 | if (IS_ERR(shm_buf)) { | |
245 | ret = PTR_ERR(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, 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, 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, session_id); | |
296 | ||
297 | return ret; | |
298 | } |