1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4 */
5
6 #include <linux/delay.h>
7 #include <linux/of.h>
8 #include <linux/firmware/qcom/qcom_pas.h>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
12 #include <linux/tee_drv.h>
13 #include <linux/uuid.h>
14
15 #include "qcom_pas.h"
16
17 /*
18 * Peripheral Authentication Service (PAS) supported.
19 *
20 * [in] params[0].value.a: Unique 32bit remote processor identifier
21 */
22 #define TA_QCOM_PAS_IS_SUPPORTED 1
23
24 /*
25 * PAS capabilities.
26 *
27 * [in] params[0].value.a: Unique 32bit remote processor identifier
28 * [out] params[1].value.a: PAS capability flags
29 */
30 #define TA_QCOM_PAS_CAPABILITIES 2
31
32 /*
33 * PAS image initialization.
34 *
35 * [in] params[0].value.a: Unique 32bit remote processor identifier
36 * [in] params[1].memref: Loadable firmware metadata
37 */
38 #define TA_QCOM_PAS_INIT_IMAGE 3
39
40 /*
41 * PAS memory setup.
42 *
43 * [in] params[0].value.a: Unique 32bit remote processor identifier
44 * [in] params[0].value.b: Relocatable firmware size
45 * [in] params[1].value.a: 32bit LSB relocatable firmware memory address
46 * [in] params[1].value.b: 32bit MSB relocatable firmware memory address
47 */
48 #define TA_QCOM_PAS_MEM_SETUP 4
49
50 /*
51 * PAS get resource table.
52 *
53 * [in] params[0].value.a: Unique 32bit remote processor identifier
54 * [inout] params[1].memref: Resource table config
55 */
56 #define TA_QCOM_PAS_GET_RESOURCE_TABLE 5
57
58 /*
59 * PAS image authentication and co-processor reset.
60 *
61 * [in] params[0].value.a: Unique 32bit remote processor identifier
62 * [in] params[0].value.b: Firmware size
63 * [in] params[1].value.a: 32bit LSB firmware memory address
64 * [in] params[1].value.b: 32bit MSB firmware memory address
65 * [in] params[2].memref: Optional fw memory space shared/lent
66 */
67 #define TA_QCOM_PAS_AUTH_AND_RESET 6
68
69 /*
70 * PAS co-processor set suspend/resume state.
71 *
72 * [in] params[0].value.a: Unique 32bit remote processor identifier
73 * [in] params[0].value.b: Co-processor state identifier
74 */
75 #define TA_QCOM_PAS_SET_REMOTE_STATE 7
76
77 /*
78 * PAS co-processor shutdown.
79 *
80 * [in] params[0].value.a: Unique 32bit remote processor identifier
81 */
82 #define TA_QCOM_PAS_SHUTDOWN 8
83
84 #define TEE_NUM_PARAMS 4
85
86 /**
87 * struct qcom_pas_tee_private - PAS service private data
88 * @dev: PAS service device.
89 * @ctx: TEE context handler.
90 * @session_id: PAS TA session identifier.
91 */
92 struct qcom_pas_tee_private {
93 struct device *dev;
94 struct tee_context *ctx;
95 u32 session_id;
96 };
97
qcom_pas_tee_supported(struct device * dev,u32 pas_id)98 static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id)
99 {
100 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
101 struct tee_ioctl_invoke_arg inv_arg = {
102 .func = TA_QCOM_PAS_IS_SUPPORTED,
103 .session = data->session_id,
104 .num_params = TEE_NUM_PARAMS
105 };
106 struct tee_param param[4] = {
107 [0] = {
108 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
109 .u.value.a = pas_id
110 }
111 };
112 int ret;
113
114 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
115 if (ret < 0 || inv_arg.ret != 0) {
116 dev_err(dev, "PAS not supported, pas_id: %d, ret: %d, err: 0x%x\n",
117 pas_id, ret, inv_arg.ret);
118 return false;
119 }
120
121 return true;
122 }
123
qcom_pas_tee_init_image(struct device * dev,u32 pas_id,const void * metadata,size_t size,struct qcom_pas_context * ctx)124 static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id,
125 const void *metadata, size_t size,
126 struct qcom_pas_context *ctx)
127 {
128 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
129 struct tee_ioctl_invoke_arg inv_arg = {
130 .func = TA_QCOM_PAS_INIT_IMAGE,
131 .session = data->session_id,
132 .num_params = TEE_NUM_PARAMS
133 };
134 struct tee_param param[4] = {
135 [0] = {
136 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
137 .u.value.a = pas_id
138 },
139 [1] = {
140 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
141 }
142 };
143 struct tee_shm *mdata_shm;
144 u8 *mdata_buf = NULL;
145 int ret;
146
147 mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size);
148 if (IS_ERR(mdata_shm)) {
149 dev_err(dev, "mdata_shm allocation failed\n");
150 return PTR_ERR(mdata_shm);
151 }
152
153 mdata_buf = tee_shm_get_va(mdata_shm, 0);
154 if (IS_ERR(mdata_buf)) {
155 dev_err(dev, "mdata_buf get VA failed\n");
156 tee_shm_free(mdata_shm);
157 return PTR_ERR(mdata_buf);
158 }
159 memcpy(mdata_buf, metadata, size);
160
161 param[1].u.memref.shm = mdata_shm;
162 param[1].u.memref.size = size;
163
164 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
165 if (ret < 0 || inv_arg.ret != 0) {
166 dev_err(dev, "PAS init image failed, pas_id: %d, ret: %d, err: 0x%x\n",
167 pas_id, ret, inv_arg.ret);
168 tee_shm_free(mdata_shm);
169 return ret ?: -EINVAL;
170 }
171
172 if (ctx)
173 ctx->ptr = (void *)mdata_shm;
174 else
175 tee_shm_free(mdata_shm);
176
177 return ret;
178 }
179
qcom_pas_tee_mem_setup(struct device * dev,u32 pas_id,phys_addr_t addr,phys_addr_t size)180 static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id,
181 phys_addr_t addr, phys_addr_t size)
182 {
183 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
184 struct tee_ioctl_invoke_arg inv_arg = {
185 .func = TA_QCOM_PAS_MEM_SETUP,
186 .session = data->session_id,
187 .num_params = TEE_NUM_PARAMS
188 };
189 struct tee_param param[4] = {
190 [0] = {
191 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
192 .u.value.a = pas_id,
193 .u.value.b = size,
194 },
195 [1] = {
196 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
197 .u.value.a = lower_32_bits(addr),
198 .u.value.b = upper_32_bits(addr),
199 }
200 };
201 int ret;
202
203 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
204 if (ret < 0 || inv_arg.ret != 0) {
205 dev_err(dev, "PAS mem setup failed, pas_id: %d, ret: %d, err: 0x%x\n",
206 pas_id, ret, inv_arg.ret);
207 return ret ?: -EINVAL;
208 }
209
210 return ret;
211 }
212
DEFINE_FREE(shm_free,struct tee_shm *,tee_shm_free (_T))213 DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T))
214
215 static void *qcom_pas_tee_get_rsc_table(struct device *dev,
216 struct qcom_pas_context *ctx,
217 void *input_rt, size_t input_rt_size,
218 size_t *output_rt_size)
219 {
220 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
221 struct tee_ioctl_invoke_arg inv_arg = {
222 .func = TA_QCOM_PAS_GET_RESOURCE_TABLE,
223 .session = data->session_id,
224 .num_params = TEE_NUM_PARAMS
225 };
226 struct tee_param param[4] = {
227 [0] = {
228 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
229 .u.value.a = ctx->pas_id,
230 },
231 [1] = {
232 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
233 .u.memref.size = input_rt_size,
234 }
235 };
236 void *rt_buf = NULL;
237 int ret;
238
239 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
240 if (ret < 0 || inv_arg.ret != 0) {
241 dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n",
242 ctx->pas_id, ret, inv_arg.ret);
243 return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL);
244 }
245
246 if (param[1].u.memref.size >= input_rt_size) {
247 struct tee_shm *rt_shm __free(shm_free) =
248 tee_shm_alloc_kernel_buf(data->ctx,
249 param[1].u.memref.size);
250 void *rt_shm_va;
251
252 if (IS_ERR_OR_NULL(rt_shm)) {
253 dev_err(dev, "rt_shm allocation failed\n");
254 rt_shm = NULL;
255 return ERR_PTR(-ENOMEM);
256 }
257
258 rt_shm_va = tee_shm_get_va(rt_shm, 0);
259 if (IS_ERR(rt_shm_va)) {
260 dev_err(dev, "rt_shm get VA failed\n");
261 return ERR_CAST(rt_shm_va);
262 }
263 memcpy(rt_shm_va, input_rt, input_rt_size);
264
265 param[1].u.memref.shm = rt_shm;
266 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
267 if (ret < 0 || inv_arg.ret != 0) {
268 dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n",
269 ctx->pas_id, ret, inv_arg.ret);
270 return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL);
271 }
272
273 if (param[1].u.memref.size) {
274 *output_rt_size = param[1].u.memref.size;
275 rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL);
276 if (!rt_buf)
277 return ERR_PTR(-ENOMEM);
278 }
279 } else {
280 *output_rt_size = 0;
281 }
282
283 return rt_buf;
284 }
285
__qcom_pas_tee_auth_and_reset(struct device * dev,u32 pas_id,phys_addr_t mem_phys,size_t mem_size)286 static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id,
287 phys_addr_t mem_phys, size_t mem_size)
288 {
289 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
290 struct tee_ioctl_invoke_arg inv_arg = {
291 .func = TA_QCOM_PAS_AUTH_AND_RESET,
292 .session = data->session_id,
293 .num_params = TEE_NUM_PARAMS
294 };
295 struct tee_param param[4] = {
296 [0] = {
297 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
298 .u.value.a = pas_id,
299 .u.value.b = mem_size,
300 },
301 [1] = {
302 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
303 .u.value.a = lower_32_bits(mem_phys),
304 .u.value.b = upper_32_bits(mem_phys),
305 },
306 /* Reserved for fw memory space to be shared or lent */
307 [2] = {
308 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
309 }
310 };
311 int ret;
312
313 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
314 if (ret < 0 || inv_arg.ret != 0) {
315 dev_err(dev, "PAS auth reset failed, pas_id: %d, ret: %d, err: 0x%x\n",
316 pas_id, ret, inv_arg.ret);
317 return ret ?: -EINVAL;
318 }
319
320 return ret;
321 }
322
qcom_pas_tee_auth_and_reset(struct device * dev,u32 pas_id)323 static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id)
324 {
325 return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0);
326 }
327
qcom_pas_tee_prepare_and_auth_reset(struct device * dev,struct qcom_pas_context * ctx)328 static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev,
329 struct qcom_pas_context *ctx)
330 {
331 return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys,
332 ctx->mem_size);
333 }
334
qcom_pas_tee_set_remote_state(struct device * dev,u32 state,u32 pas_id)335 static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state,
336 u32 pas_id)
337 {
338 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
339 struct tee_ioctl_invoke_arg inv_arg = {
340 .func = TA_QCOM_PAS_SET_REMOTE_STATE,
341 .session = data->session_id,
342 .num_params = TEE_NUM_PARAMS
343 };
344 struct tee_param param[4] = {
345 [0] = {
346 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
347 .u.value.a = pas_id,
348 .u.value.b = state,
349 }
350 };
351 int ret;
352
353 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
354 if (ret < 0 || inv_arg.ret != 0) {
355 dev_err(dev, "PAS set remote state failed, pas_id: %d, ret: %d, err: 0x%x\n",
356 pas_id, ret, inv_arg.ret);
357 return ret ?: -EINVAL;
358 }
359
360 return ret;
361 }
362
qcom_pas_tee_shutdown(struct device * dev,u32 pas_id)363 static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id)
364 {
365 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
366 struct tee_ioctl_invoke_arg inv_arg = {
367 .func = TA_QCOM_PAS_SHUTDOWN,
368 .session = data->session_id,
369 .num_params = TEE_NUM_PARAMS
370 };
371 struct tee_param param[4] = {
372 [0] = {
373 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
374 .u.value.a = pas_id
375 }
376 };
377 int ret;
378
379 ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
380 if (ret < 0 || inv_arg.ret != 0) {
381 dev_err(dev, "PAS shutdown failed, pas_id: %d, ret: %d, err: 0x%x\n",
382 pas_id, ret, inv_arg.ret);
383 return ret ?: -EINVAL;
384 }
385
386 return ret;
387 }
388
qcom_pas_tee_metadata_release(struct device * dev,struct qcom_pas_context * ctx)389 static void qcom_pas_tee_metadata_release(struct device *dev,
390 struct qcom_pas_context *ctx)
391 {
392 struct tee_shm *mdata_shm = ctx->ptr;
393
394 tee_shm_free(mdata_shm);
395 ctx->ptr = NULL;
396 }
397
398 static struct qcom_pas_ops qcom_pas_ops_tee = {
399 .drv_name = "qcom-pas-tee",
400 .supported = qcom_pas_tee_supported,
401 .init_image = qcom_pas_tee_init_image,
402 .mem_setup = qcom_pas_tee_mem_setup,
403 .get_rsc_table = qcom_pas_tee_get_rsc_table,
404 .auth_and_reset = qcom_pas_tee_auth_and_reset,
405 .prepare_and_auth_reset = qcom_pas_tee_prepare_and_auth_reset,
406 .set_remote_state = qcom_pas_tee_set_remote_state,
407 .shutdown = qcom_pas_tee_shutdown,
408 .metadata_release = qcom_pas_tee_metadata_release,
409 };
410
optee_ctx_match(struct tee_ioctl_version_data * ver,const void * data)411 static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
412 {
413 return ver->impl_id == TEE_IMPL_ID_OPTEE;
414 }
415
qcom_pas_tee_probe(struct tee_client_device * pas_dev)416 static int qcom_pas_tee_probe(struct tee_client_device *pas_dev)
417 {
418 struct device *dev = &pas_dev->dev;
419 struct qcom_pas_tee_private *data;
420 struct tee_ioctl_open_session_arg sess_arg = {
421 .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL
422 };
423 int ret;
424
425 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
426 if (!data)
427 return -ENOMEM;
428
429 data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
430 if (IS_ERR(data->ctx))
431 return -ENODEV;
432
433 export_uuid(sess_arg.uuid, &pas_dev->id.uuid);
434 ret = tee_client_open_session(data->ctx, &sess_arg, NULL);
435 if (ret < 0 || sess_arg.ret != 0) {
436 dev_err(dev, "tee_client_open_session failed, ret: %d, err: 0x%x\n",
437 ret, sess_arg.ret);
438 tee_client_close_context(data->ctx);
439 return ret ?: -EINVAL;
440 }
441
442 data->session_id = sess_arg.session;
443 dev_set_drvdata(dev, data);
444 qcom_pas_ops_tee.dev = dev;
445 qcom_pas_ops_register(&qcom_pas_ops_tee);
446
447 return ret;
448 }
449
qcom_pas_tee_remove(struct tee_client_device * pas_dev)450 static void qcom_pas_tee_remove(struct tee_client_device *pas_dev)
451 {
452 struct device *dev = &pas_dev->dev;
453 struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
454
455 qcom_pas_ops_unregister();
456 tee_client_close_session(data->ctx, data->session_id);
457 tee_client_close_context(data->ctx);
458 }
459
460 static const struct tee_client_device_id qcom_pas_tee_id_table[] = {
461 {UUID_INIT(0xcff7d191, 0x7ca0, 0x4784,
462 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)},
463 {}
464 };
465 MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table);
466
467 static struct tee_client_driver optee_pas_tee_driver = {
468 .probe = qcom_pas_tee_probe,
469 .remove = qcom_pas_tee_remove,
470 .id_table = qcom_pas_tee_id_table,
471 .driver = {
472 .name = "qcom-pas-tee",
473 },
474 };
475
476 module_tee_client_driver(optee_pas_tee_driver);
477
478 MODULE_LICENSE("GPL");
479 MODULE_DESCRIPTION("Qualcomm PAS TEE driver");
480