1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2018 Linaro Limited
10 #include <linux/arm-smccc.h>
13 #include "optee_smc.h"
14 #include "optee_msg.h"
15 #include "optee_private.h"
17 #define PAGELIST_ENTRIES_PER_PAGE \
18 ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
20 typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
21 unsigned long, unsigned long, unsigned long,
22 unsigned long, unsigned long,
23 struct arm_smccc_res *);
26 optee_invoke_fn *invoke_fn;
41 * reg_pair_to_ptr() - Make a pointer of 2 32-bit values
42 * @reg0: High bits of the pointer
43 * @reg1: Low bits of the pointer
45 * Returns the combined result, note that if a pointer is 32-bit wide @reg0
48 static void *reg_pair_to_ptr(u32 reg0, u32 reg1)
50 return (void *)(ulong)(((u64)reg0 << 32) | reg1);
54 * reg_pair_from_64() - Split a 64-bit value into two 32-bit values
55 * @reg0: High bits of @val
56 * @reg1: Low bits of @val
57 * @val: The value to split
59 static void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
66 * optee_alloc_and_init_page_list() - Provide page list of memory buffer
67 * @buf: Start of buffer
68 * @len: Length of buffer
69 * @phys_buf_ptr Physical pointer with coded offset to page list
71 * Secure world doesn't share mapping with Normal world (U-Boot in this case)
72 * so physical pointers are needed when sharing pointers.
74 * Returns a pointer page list on success or NULL on failure
76 void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr)
78 const unsigned int page_size = OPTEE_MSG_NONCONTIG_PAGE_SIZE;
79 const phys_addr_t page_mask = page_size - 1;
81 unsigned int page_offset;
82 unsigned int num_pages;
83 unsigned int list_size;
87 u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
92 * A Memory buffer is described in chunks of 4k. The list of
93 * physical addresses has to be represented by a physical pointer
94 * too and a single list has to start at a 4k page and fit into
95 * that page. In order to be able to describe large memory buffers
96 * these 4k pages carrying physical addresses are linked together
97 * in a list. See OPTEE_MSG_ATTR_NONCONTIG in
98 * drivers/tee/optee/optee_msg.h for more information.
101 page_offset = (ulong)buf & page_mask;
102 num_pages = roundup(page_offset + len, page_size) / page_size;
103 list_size = DIV_ROUND_UP(num_pages, PAGELIST_ENTRIES_PER_PAGE) *
105 page_list = memalign(page_size, list_size);
109 pages_data = page_list;
110 buf_base = (u8 *)rounddown((ulong)buf, page_size);
113 pages_data->pages_list[n] = virt_to_phys(buf_base);
115 buf_base += page_size;
118 if (n == PAGELIST_ENTRIES_PER_PAGE) {
119 pages_data->next_page_data =
120 virt_to_phys(pages_data + 1);
126 *phys_buf_ptr = virt_to_phys(page_list) | page_offset;
130 static void optee_get_version(struct udevice *dev,
131 struct tee_version_data *vers)
133 struct tee_version_data v = {
134 .gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM,
140 static int get_msg_arg(struct udevice *dev, uint num_params,
141 struct tee_shm **shmp, struct optee_msg_arg **msg_arg)
144 struct optee_msg_arg *ma;
146 rc = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
147 OPTEE_MSG_GET_ARG_SIZE(num_params), TEE_SHM_ALLOC,
153 memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params));
154 ma->num_params = num_params;
160 static int to_msg_param(struct optee_msg_param *msg_params, uint num_params,
161 const struct tee_param *params)
165 for (n = 0; n < num_params; n++) {
166 const struct tee_param *p = params + n;
167 struct optee_msg_param *mp = msg_params + n;
170 case TEE_PARAM_ATTR_TYPE_NONE:
171 mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
172 memset(&mp->u, 0, sizeof(mp->u));
174 case TEE_PARAM_ATTR_TYPE_VALUE_INPUT:
175 case TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT:
176 case TEE_PARAM_ATTR_TYPE_VALUE_INOUT:
177 mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
178 TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
179 mp->u.value.a = p->u.value.a;
180 mp->u.value.b = p->u.value.b;
181 mp->u.value.c = p->u.value.c;
183 case TEE_PARAM_ATTR_TYPE_MEMREF_INPUT:
184 case TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
185 case TEE_PARAM_ATTR_TYPE_MEMREF_INOUT:
186 mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
187 TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
188 mp->u.rmem.shm_ref = (ulong)p->u.memref.shm;
189 mp->u.rmem.size = p->u.memref.size;
190 mp->u.rmem.offs = p->u.memref.shm_offs;
199 static int from_msg_param(struct tee_param *params, uint num_params,
200 const struct optee_msg_param *msg_params)
205 for (n = 0; n < num_params; n++) {
206 struct tee_param *p = params + n;
207 const struct optee_msg_param *mp = msg_params + n;
208 u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
211 case OPTEE_MSG_ATTR_TYPE_NONE:
212 p->attr = TEE_PARAM_ATTR_TYPE_NONE;
213 memset(&p->u, 0, sizeof(p->u));
215 case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
216 case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
217 case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
218 p->attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT + attr -
219 OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
220 p->u.value.a = mp->u.value.a;
221 p->u.value.b = mp->u.value.b;
222 p->u.value.c = mp->u.value.c;
224 case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
225 case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
226 case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
227 p->attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT + attr -
228 OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
229 p->u.memref.size = mp->u.rmem.size;
230 shm = (struct tee_shm *)(ulong)mp->u.rmem.shm_ref;
233 p->u.memref.shm_offs = 0;
234 p->u.memref.shm = NULL;
237 p->u.memref.shm_offs = mp->u.rmem.offs;
238 p->u.memref.shm = shm;
247 static void handle_rpc(struct udevice *dev, struct rpc_param *param,
252 switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
253 case OPTEE_SMC_RPC_FUNC_ALLOC:
254 if (!__tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
255 param->a1, TEE_SHM_ALLOC | TEE_SHM_REGISTER,
257 reg_pair_from_64(¶m->a1, ¶m->a2,
258 virt_to_phys(shm->addr));
260 reg_pair_from_64(¶m->a4, ¶m->a5, (ulong)shm);
268 case OPTEE_SMC_RPC_FUNC_FREE:
269 shm = reg_pair_to_ptr(param->a1, param->a2);
272 case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR:
274 case OPTEE_SMC_RPC_FUNC_CMD:
275 shm = reg_pair_to_ptr(param->a1, param->a2);
276 optee_suppl_cmd(dev, shm, page_list);
282 param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
285 static u32 call_err_to_res(u32 call_err)
288 case OPTEE_SMC_RETURN_OK:
291 return TEE_ERROR_BAD_PARAMETERS;
295 static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg)
297 struct optee_pdata *pdata = dev_get_platdata(dev);
298 struct rpc_param param = { .a0 = OPTEE_SMC_CALL_WITH_ARG };
299 void *page_list = NULL;
301 reg_pair_from_64(¶m.a1, ¶m.a2, virt_to_phys(arg));
303 struct arm_smccc_res res;
305 pdata->invoke_fn(param.a0, param.a1, param.a2, param.a3,
306 param.a4, param.a5, param.a6, param.a7, &res);
311 if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
316 handle_rpc(dev, ¶m, &page_list);
318 return call_err_to_res(res.a0);
323 static int optee_close_session(struct udevice *dev, u32 session)
327 struct optee_msg_arg *msg_arg;
329 rc = get_msg_arg(dev, 0, &shm, &msg_arg);
333 msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
334 msg_arg->session = session;
335 do_call_with_arg(dev, msg_arg);
342 static int optee_open_session(struct udevice *dev,
343 struct tee_open_session_arg *arg,
344 uint num_params, struct tee_param *params)
348 struct optee_msg_arg *msg_arg;
350 rc = get_msg_arg(dev, num_params + 2, &shm, &msg_arg);
354 msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION;
356 * Initialize and add the meta parameters needed when opening a
359 msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
361 msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
363 memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid));
364 memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid));
365 msg_arg->params[1].u.value.c = arg->clnt_login;
367 rc = to_msg_param(msg_arg->params + 2, num_params, params);
371 arg->ret = do_call_with_arg(dev, msg_arg);
373 arg->ret_origin = TEE_ORIGIN_COMMS;
377 if (from_msg_param(params, num_params, msg_arg->params + 2)) {
378 arg->ret = TEE_ERROR_COMMUNICATION;
379 arg->ret_origin = TEE_ORIGIN_COMMS;
380 /* Close session again to avoid leakage */
381 optee_close_session(dev, msg_arg->session);
385 arg->session = msg_arg->session;
386 arg->ret = msg_arg->ret;
387 arg->ret_origin = msg_arg->ret_origin;
394 static int optee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
395 uint num_params, struct tee_param *params)
398 struct optee_msg_arg *msg_arg;
401 rc = get_msg_arg(dev, num_params, &shm, &msg_arg);
404 msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND;
405 msg_arg->func = arg->func;
406 msg_arg->session = arg->session;
408 rc = to_msg_param(msg_arg->params, num_params, params);
412 arg->ret = do_call_with_arg(dev, msg_arg);
414 arg->ret_origin = TEE_ORIGIN_COMMS;
418 if (from_msg_param(params, num_params, msg_arg->params)) {
419 arg->ret = TEE_ERROR_COMMUNICATION;
420 arg->ret_origin = TEE_ORIGIN_COMMS;
424 arg->ret = msg_arg->ret;
425 arg->ret_origin = msg_arg->ret_origin;
431 static int optee_shm_register(struct udevice *dev, struct tee_shm *shm)
433 struct tee_shm *shm_arg;
434 struct optee_msg_arg *msg_arg;
439 rc = get_msg_arg(dev, 1, &shm_arg, &msg_arg);
443 pl = optee_alloc_and_init_page_list(shm->addr, shm->size, &ph_ptr);
449 msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
450 msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
451 OPTEE_MSG_ATTR_NONCONTIG;
452 msg_arg->params->u.tmem.buf_ptr = ph_ptr;
453 msg_arg->params->u.tmem.shm_ref = (ulong)shm;
454 msg_arg->params->u.tmem.size = shm->size;
456 if (do_call_with_arg(dev, msg_arg) || msg_arg->ret)
461 tee_shm_free(shm_arg);
466 static int optee_shm_unregister(struct udevice *dev, struct tee_shm *shm)
468 struct tee_shm *shm_arg;
469 struct optee_msg_arg *msg_arg;
472 rc = get_msg_arg(dev, 1, &shm_arg, &msg_arg);
476 msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
477 msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
478 msg_arg->params[0].u.rmem.shm_ref = (ulong)shm;
480 if (do_call_with_arg(dev, msg_arg) || msg_arg->ret)
482 tee_shm_free(shm_arg);
487 static const struct tee_driver_ops optee_ops = {
488 .get_version = optee_get_version,
489 .open_session = optee_open_session,
490 .close_session = optee_close_session,
491 .invoke_func = optee_invoke_func,
492 .shm_register = optee_shm_register,
493 .shm_unregister = optee_shm_unregister,
496 static bool is_optee_api(optee_invoke_fn *invoke_fn)
498 struct arm_smccc_res res;
500 invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
502 return res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
503 res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3;
506 static void print_os_revision(optee_invoke_fn *invoke_fn)
509 struct arm_smccc_res smccc;
510 struct optee_smc_call_get_os_revision_result result;
517 invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
520 if (res.result.build_id)
521 debug("OP-TEE revision %lu.%lu (%08lx)\n", res.result.major,
522 res.result.minor, res.result.build_id);
524 debug("OP-TEE revision %lu.%lu\n", res.result.major,
528 static bool api_revision_is_compatible(optee_invoke_fn *invoke_fn)
531 struct arm_smccc_res smccc;
532 struct optee_smc_calls_revision_result result;
535 invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
537 return res.result.major == OPTEE_MSG_REVISION_MAJOR &&
538 (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR;
541 static bool exchange_capabilities(optee_invoke_fn *invoke_fn, u32 *sec_caps)
544 struct arm_smccc_res smccc;
545 struct optee_smc_exchange_capabilities_result result;
548 invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES,
549 OPTEE_SMC_NSEC_CAP_UNIPROCESSOR, 0, 0, 0, 0, 0, 0,
552 if (res.result.status != OPTEE_SMC_RETURN_OK)
555 *sec_caps = res.result.capabilities;
560 /* Simple wrapper functions to be able to use a function pointer */
561 static void optee_smccc_smc(unsigned long a0, unsigned long a1,
562 unsigned long a2, unsigned long a3,
563 unsigned long a4, unsigned long a5,
564 unsigned long a6, unsigned long a7,
565 struct arm_smccc_res *res)
567 arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
570 static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
571 unsigned long a2, unsigned long a3,
572 unsigned long a4, unsigned long a5,
573 unsigned long a6, unsigned long a7,
574 struct arm_smccc_res *res)
576 arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
579 static optee_invoke_fn *get_invoke_func(struct udevice *dev)
583 debug("optee: looking for conduit method in DT.\n");
584 method = ofnode_get_property(dev->node, "method", NULL);
586 debug("optee: missing \"method\" property\n");
587 return ERR_PTR(-ENXIO);
590 if (!strcmp("hvc", method))
591 return optee_smccc_hvc;
592 else if (!strcmp("smc", method))
593 return optee_smccc_smc;
595 debug("optee: invalid \"method\" property: %s\n", method);
596 return ERR_PTR(-EINVAL);
599 static int optee_ofdata_to_platdata(struct udevice *dev)
601 struct optee_pdata *pdata = dev_get_platdata(dev);
603 pdata->invoke_fn = get_invoke_func(dev);
604 if (IS_ERR(pdata->invoke_fn))
605 return PTR_ERR(pdata->invoke_fn);
610 static int optee_probe(struct udevice *dev)
612 struct optee_pdata *pdata = dev_get_platdata(dev);
615 if (!is_optee_api(pdata->invoke_fn)) {
616 debug("%s: OP-TEE api uid mismatch\n", __func__);
620 print_os_revision(pdata->invoke_fn);
622 if (!api_revision_is_compatible(pdata->invoke_fn)) {
623 debug("%s: OP-TEE api revision mismatch\n", __func__);
628 * OP-TEE can use both shared memory via predefined pool or as
629 * dynamic shared memory provided by normal world. To keep things
630 * simple we're only using dynamic shared memory in this driver.
632 if (!exchange_capabilities(pdata->invoke_fn, &sec_caps) ||
633 !(sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)) {
634 debug("%s: OP-TEE capabilities mismatch\n", __func__);
641 static const struct udevice_id optee_match[] = {
642 { .compatible = "linaro,optee-tz" },
646 U_BOOT_DRIVER(optee) = {
649 .of_match = optee_match,
650 .ofdata_to_platdata = optee_ofdata_to_platdata,
651 .probe = optee_probe,
653 .platdata_auto_alloc_size = sizeof(struct optee_pdata),