1d4bd3d25SJens Wiklander // SPDX-License-Identifier: GPL-2.0+
2d4bd3d25SJens Wiklander /*
3d4bd3d25SJens Wiklander * Copyright (c) 2018 Linaro Limited
4d4bd3d25SJens Wiklander */
5d4bd3d25SJens Wiklander
6d4bd3d25SJens Wiklander #include <common.h>
7d4bd3d25SJens Wiklander #include <dm.h>
8d4bd3d25SJens Wiklander #include <log.h>
9d4bd3d25SJens Wiklander #include <tee.h>
10d4bd3d25SJens Wiklander #include <linux/arm-smccc.h>
11d4bd3d25SJens Wiklander #include <linux/io.h>
12d4bd3d25SJens Wiklander
13d4bd3d25SJens Wiklander #include "optee_smc.h"
14d4bd3d25SJens Wiklander #include "optee_msg.h"
15d4bd3d25SJens Wiklander #include "optee_private.h"
16d4bd3d25SJens Wiklander
17d4bd3d25SJens Wiklander #define PAGELIST_ENTRIES_PER_PAGE \
18d4bd3d25SJens Wiklander ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
19d4bd3d25SJens Wiklander
20d4bd3d25SJens Wiklander typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
21d4bd3d25SJens Wiklander unsigned long, unsigned long, unsigned long,
22d4bd3d25SJens Wiklander unsigned long, unsigned long,
23d4bd3d25SJens Wiklander struct arm_smccc_res *);
24d4bd3d25SJens Wiklander
25d4bd3d25SJens Wiklander struct optee_pdata {
26d4bd3d25SJens Wiklander optee_invoke_fn *invoke_fn;
27d4bd3d25SJens Wiklander };
28d4bd3d25SJens Wiklander
29d4bd3d25SJens Wiklander struct rpc_param {
30d4bd3d25SJens Wiklander u32 a0;
31d4bd3d25SJens Wiklander u32 a1;
32d4bd3d25SJens Wiklander u32 a2;
33d4bd3d25SJens Wiklander u32 a3;
34d4bd3d25SJens Wiklander u32 a4;
35d4bd3d25SJens Wiklander u32 a5;
36d4bd3d25SJens Wiklander u32 a6;
37d4bd3d25SJens Wiklander u32 a7;
38d4bd3d25SJens Wiklander };
39d4bd3d25SJens Wiklander
40d4bd3d25SJens Wiklander /**
41d4bd3d25SJens Wiklander * reg_pair_to_ptr() - Make a pointer of 2 32-bit values
42d4bd3d25SJens Wiklander * @reg0: High bits of the pointer
43d4bd3d25SJens Wiklander * @reg1: Low bits of the pointer
44d4bd3d25SJens Wiklander *
45d4bd3d25SJens Wiklander * Returns the combined result, note that if a pointer is 32-bit wide @reg0
46d4bd3d25SJens Wiklander * will be discarded.
47d4bd3d25SJens Wiklander */
reg_pair_to_ptr(u32 reg0,u32 reg1)48d4bd3d25SJens Wiklander static void *reg_pair_to_ptr(u32 reg0, u32 reg1)
49d4bd3d25SJens Wiklander {
50d4bd3d25SJens Wiklander return (void *)(ulong)(((u64)reg0 << 32) | reg1);
51d4bd3d25SJens Wiklander }
52d4bd3d25SJens Wiklander
53d4bd3d25SJens Wiklander /**
54d4bd3d25SJens Wiklander * reg_pair_from_64() - Split a 64-bit value into two 32-bit values
55d4bd3d25SJens Wiklander * @reg0: High bits of @val
56d4bd3d25SJens Wiklander * @reg1: Low bits of @val
57d4bd3d25SJens Wiklander * @val: The value to split
58d4bd3d25SJens Wiklander */
reg_pair_from_64(u32 * reg0,u32 * reg1,u64 val)59d4bd3d25SJens Wiklander static void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
60d4bd3d25SJens Wiklander {
61d4bd3d25SJens Wiklander *reg0 = val >> 32;
62d4bd3d25SJens Wiklander *reg1 = val;
63d4bd3d25SJens Wiklander }
64d4bd3d25SJens Wiklander
65d4bd3d25SJens Wiklander /**
66d4bd3d25SJens Wiklander * optee_alloc_and_init_page_list() - Provide page list of memory buffer
67d4bd3d25SJens Wiklander * @buf: Start of buffer
68d4bd3d25SJens Wiklander * @len: Length of buffer
69d4bd3d25SJens Wiklander * @phys_buf_ptr Physical pointer with coded offset to page list
70d4bd3d25SJens Wiklander *
71d4bd3d25SJens Wiklander * Secure world doesn't share mapping with Normal world (U-Boot in this case)
72d4bd3d25SJens Wiklander * so physical pointers are needed when sharing pointers.
73d4bd3d25SJens Wiklander *
74d4bd3d25SJens Wiklander * Returns a pointer page list on success or NULL on failure
75d4bd3d25SJens Wiklander */
optee_alloc_and_init_page_list(void * buf,ulong len,u64 * phys_buf_ptr)76d4bd3d25SJens Wiklander void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr)
77d4bd3d25SJens Wiklander {
78d4bd3d25SJens Wiklander const unsigned int page_size = OPTEE_MSG_NONCONTIG_PAGE_SIZE;
79d4bd3d25SJens Wiklander const phys_addr_t page_mask = page_size - 1;
80d4bd3d25SJens Wiklander u8 *buf_base;
81d4bd3d25SJens Wiklander unsigned int page_offset;
82d4bd3d25SJens Wiklander unsigned int num_pages;
83d4bd3d25SJens Wiklander unsigned int list_size;
84d4bd3d25SJens Wiklander unsigned int n;
85d4bd3d25SJens Wiklander void *page_list;
86d4bd3d25SJens Wiklander struct {
87d4bd3d25SJens Wiklander u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
88d4bd3d25SJens Wiklander u64 next_page_data;
89d4bd3d25SJens Wiklander } *pages_data;
90d4bd3d25SJens Wiklander
91d4bd3d25SJens Wiklander /*
92d4bd3d25SJens Wiklander * A Memory buffer is described in chunks of 4k. The list of
93d4bd3d25SJens Wiklander * physical addresses has to be represented by a physical pointer
94d4bd3d25SJens Wiklander * too and a single list has to start at a 4k page and fit into
95d4bd3d25SJens Wiklander * that page. In order to be able to describe large memory buffers
96d4bd3d25SJens Wiklander * these 4k pages carrying physical addresses are linked together
97d4bd3d25SJens Wiklander * in a list. See OPTEE_MSG_ATTR_NONCONTIG in
98d4bd3d25SJens Wiklander * drivers/tee/optee/optee_msg.h for more information.
99d4bd3d25SJens Wiklander */
100d4bd3d25SJens Wiklander
101d4bd3d25SJens Wiklander page_offset = (ulong)buf & page_mask;
102d4bd3d25SJens Wiklander num_pages = roundup(page_offset + len, page_size) / page_size;
103d4bd3d25SJens Wiklander list_size = DIV_ROUND_UP(num_pages, PAGELIST_ENTRIES_PER_PAGE) *
104d4bd3d25SJens Wiklander page_size;
105d4bd3d25SJens Wiklander page_list = memalign(page_size, list_size);
106d4bd3d25SJens Wiklander if (!page_list)
107d4bd3d25SJens Wiklander return NULL;
108d4bd3d25SJens Wiklander
109d4bd3d25SJens Wiklander pages_data = page_list;
110d4bd3d25SJens Wiklander buf_base = (u8 *)rounddown((ulong)buf, page_size);
111d4bd3d25SJens Wiklander n = 0;
112d4bd3d25SJens Wiklander while (num_pages) {
113d4bd3d25SJens Wiklander pages_data->pages_list[n] = virt_to_phys(buf_base);
114d4bd3d25SJens Wiklander n++;
115d4bd3d25SJens Wiklander buf_base += page_size;
116d4bd3d25SJens Wiklander num_pages--;
117d4bd3d25SJens Wiklander
118d4bd3d25SJens Wiklander if (n == PAGELIST_ENTRIES_PER_PAGE) {
119d4bd3d25SJens Wiklander pages_data->next_page_data =
120d4bd3d25SJens Wiklander virt_to_phys(pages_data + 1);
121d4bd3d25SJens Wiklander pages_data++;
122d4bd3d25SJens Wiklander n = 0;
123d4bd3d25SJens Wiklander }
124d4bd3d25SJens Wiklander }
125d4bd3d25SJens Wiklander
126d4bd3d25SJens Wiklander *phys_buf_ptr = virt_to_phys(page_list) | page_offset;
127d4bd3d25SJens Wiklander return page_list;
128d4bd3d25SJens Wiklander }
129d4bd3d25SJens Wiklander
optee_get_version(struct udevice * dev,struct tee_version_data * vers)130d4bd3d25SJens Wiklander static void optee_get_version(struct udevice *dev,
131d4bd3d25SJens Wiklander struct tee_version_data *vers)
132d4bd3d25SJens Wiklander {
133d4bd3d25SJens Wiklander struct tee_version_data v = {
134d4bd3d25SJens Wiklander .gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM,
135d4bd3d25SJens Wiklander };
136d4bd3d25SJens Wiklander
137d4bd3d25SJens Wiklander *vers = v;
138d4bd3d25SJens Wiklander }
139d4bd3d25SJens Wiklander
get_msg_arg(struct udevice * dev,uint num_params,struct tee_shm ** shmp,struct optee_msg_arg ** msg_arg)140d4bd3d25SJens Wiklander static int get_msg_arg(struct udevice *dev, uint num_params,
141d4bd3d25SJens Wiklander struct tee_shm **shmp, struct optee_msg_arg **msg_arg)
142d4bd3d25SJens Wiklander {
143d4bd3d25SJens Wiklander int rc;
144d4bd3d25SJens Wiklander struct optee_msg_arg *ma;
145d4bd3d25SJens Wiklander
146d4bd3d25SJens Wiklander rc = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
147d4bd3d25SJens Wiklander OPTEE_MSG_GET_ARG_SIZE(num_params), TEE_SHM_ALLOC,
148d4bd3d25SJens Wiklander shmp);
149d4bd3d25SJens Wiklander if (rc)
150d4bd3d25SJens Wiklander return rc;
151d4bd3d25SJens Wiklander
152d4bd3d25SJens Wiklander ma = (*shmp)->addr;
153d4bd3d25SJens Wiklander memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params));
154d4bd3d25SJens Wiklander ma->num_params = num_params;
155d4bd3d25SJens Wiklander *msg_arg = ma;
156d4bd3d25SJens Wiklander
157d4bd3d25SJens Wiklander return 0;
158d4bd3d25SJens Wiklander }
159d4bd3d25SJens Wiklander
to_msg_param(struct optee_msg_param * msg_params,uint num_params,const struct tee_param * params)160d4bd3d25SJens Wiklander static int to_msg_param(struct optee_msg_param *msg_params, uint num_params,
161d4bd3d25SJens Wiklander const struct tee_param *params)
162d4bd3d25SJens Wiklander {
163d4bd3d25SJens Wiklander uint n;
164d4bd3d25SJens Wiklander
165d4bd3d25SJens Wiklander for (n = 0; n < num_params; n++) {
166d4bd3d25SJens Wiklander const struct tee_param *p = params + n;
167d4bd3d25SJens Wiklander struct optee_msg_param *mp = msg_params + n;
168d4bd3d25SJens Wiklander
169d4bd3d25SJens Wiklander switch (p->attr) {
170d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_NONE:
171d4bd3d25SJens Wiklander mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
172d4bd3d25SJens Wiklander memset(&mp->u, 0, sizeof(mp->u));
173d4bd3d25SJens Wiklander break;
174d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_VALUE_INPUT:
175d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT:
176d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_VALUE_INOUT:
177d4bd3d25SJens Wiklander mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
178d4bd3d25SJens Wiklander TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
179d4bd3d25SJens Wiklander mp->u.value.a = p->u.value.a;
180d4bd3d25SJens Wiklander mp->u.value.b = p->u.value.b;
181d4bd3d25SJens Wiklander mp->u.value.c = p->u.value.c;
182d4bd3d25SJens Wiklander break;
183d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_MEMREF_INPUT:
184d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
185d4bd3d25SJens Wiklander case TEE_PARAM_ATTR_TYPE_MEMREF_INOUT:
186d4bd3d25SJens Wiklander mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
187d4bd3d25SJens Wiklander TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
188d4bd3d25SJens Wiklander mp->u.rmem.shm_ref = (ulong)p->u.memref.shm;
189d4bd3d25SJens Wiklander mp->u.rmem.size = p->u.memref.size;
190d4bd3d25SJens Wiklander mp->u.rmem.offs = p->u.memref.shm_offs;
191d4bd3d25SJens Wiklander break;
192d4bd3d25SJens Wiklander default:
193d4bd3d25SJens Wiklander return -EINVAL;
194d4bd3d25SJens Wiklander }
195d4bd3d25SJens Wiklander }
196d4bd3d25SJens Wiklander return 0;
197d4bd3d25SJens Wiklander }
198d4bd3d25SJens Wiklander
from_msg_param(struct tee_param * params,uint num_params,const struct optee_msg_param * msg_params)199d4bd3d25SJens Wiklander static int from_msg_param(struct tee_param *params, uint num_params,
200d4bd3d25SJens Wiklander const struct optee_msg_param *msg_params)
201d4bd3d25SJens Wiklander {
202d4bd3d25SJens Wiklander uint n;
203d4bd3d25SJens Wiklander struct tee_shm *shm;
204d4bd3d25SJens Wiklander
205d4bd3d25SJens Wiklander for (n = 0; n < num_params; n++) {
206d4bd3d25SJens Wiklander struct tee_param *p = params + n;
207d4bd3d25SJens Wiklander const struct optee_msg_param *mp = msg_params + n;
208d4bd3d25SJens Wiklander u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
209d4bd3d25SJens Wiklander
210d4bd3d25SJens Wiklander switch (attr) {
211d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_NONE:
212d4bd3d25SJens Wiklander p->attr = TEE_PARAM_ATTR_TYPE_NONE;
213d4bd3d25SJens Wiklander memset(&p->u, 0, sizeof(p->u));
214d4bd3d25SJens Wiklander break;
215d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
216d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
217d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
218d4bd3d25SJens Wiklander p->attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT + attr -
219d4bd3d25SJens Wiklander OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
220d4bd3d25SJens Wiklander p->u.value.a = mp->u.value.a;
221d4bd3d25SJens Wiklander p->u.value.b = mp->u.value.b;
222d4bd3d25SJens Wiklander p->u.value.c = mp->u.value.c;
223d4bd3d25SJens Wiklander break;
224d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
225d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
226d4bd3d25SJens Wiklander case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
227d4bd3d25SJens Wiklander p->attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT + attr -
228d4bd3d25SJens Wiklander OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
229d4bd3d25SJens Wiklander p->u.memref.size = mp->u.rmem.size;
230d4bd3d25SJens Wiklander shm = (struct tee_shm *)(ulong)mp->u.rmem.shm_ref;
231d4bd3d25SJens Wiklander
232d4bd3d25SJens Wiklander if (!shm) {
233d4bd3d25SJens Wiklander p->u.memref.shm_offs = 0;
234d4bd3d25SJens Wiklander p->u.memref.shm = NULL;
235d4bd3d25SJens Wiklander break;
236d4bd3d25SJens Wiklander }
237d4bd3d25SJens Wiklander p->u.memref.shm_offs = mp->u.rmem.offs;
238d4bd3d25SJens Wiklander p->u.memref.shm = shm;
239d4bd3d25SJens Wiklander break;
240d4bd3d25SJens Wiklander default:
241d4bd3d25SJens Wiklander return -EINVAL;
242d4bd3d25SJens Wiklander }
243d4bd3d25SJens Wiklander }
244d4bd3d25SJens Wiklander return 0;
245d4bd3d25SJens Wiklander }
246d4bd3d25SJens Wiklander
handle_rpc(struct udevice * dev,struct rpc_param * param,void * page_list)247d4bd3d25SJens Wiklander static void handle_rpc(struct udevice *dev, struct rpc_param *param,
248d4bd3d25SJens Wiklander void *page_list)
249d4bd3d25SJens Wiklander {
250d4bd3d25SJens Wiklander struct tee_shm *shm;
251d4bd3d25SJens Wiklander
252d4bd3d25SJens Wiklander switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
253d4bd3d25SJens Wiklander case OPTEE_SMC_RPC_FUNC_ALLOC:
254d4bd3d25SJens Wiklander if (!__tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
255d4bd3d25SJens Wiklander param->a1, TEE_SHM_ALLOC | TEE_SHM_REGISTER,
256d4bd3d25SJens Wiklander &shm)) {
257d4bd3d25SJens Wiklander reg_pair_from_64(¶m->a1, ¶m->a2,
258d4bd3d25SJens Wiklander virt_to_phys(shm->addr));
259d4bd3d25SJens Wiklander /* "cookie" */
260d4bd3d25SJens Wiklander reg_pair_from_64(¶m->a4, ¶m->a5, (ulong)shm);
261d4bd3d25SJens Wiklander } else {
262d4bd3d25SJens Wiklander param->a1 = 0;
263d4bd3d25SJens Wiklander param->a2 = 0;
264d4bd3d25SJens Wiklander param->a4 = 0;
265d4bd3d25SJens Wiklander param->a5 = 0;
266d4bd3d25SJens Wiklander }
267d4bd3d25SJens Wiklander break;
268d4bd3d25SJens Wiklander case OPTEE_SMC_RPC_FUNC_FREE:
269d4bd3d25SJens Wiklander shm = reg_pair_to_ptr(param->a1, param->a2);
270d4bd3d25SJens Wiklander tee_shm_free(shm);
271d4bd3d25SJens Wiklander break;
272d4bd3d25SJens Wiklander case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR:
273d4bd3d25SJens Wiklander break;
274d4bd3d25SJens Wiklander case OPTEE_SMC_RPC_FUNC_CMD:
275d4bd3d25SJens Wiklander shm = reg_pair_to_ptr(param->a1, param->a2);
276d4bd3d25SJens Wiklander optee_suppl_cmd(dev, shm, page_list);
277d4bd3d25SJens Wiklander break;
278d4bd3d25SJens Wiklander default:
279d4bd3d25SJens Wiklander break;
280d4bd3d25SJens Wiklander }
281d4bd3d25SJens Wiklander
282d4bd3d25SJens Wiklander param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
283d4bd3d25SJens Wiklander }
284d4bd3d25SJens Wiklander
call_err_to_res(u32 call_err)285d4bd3d25SJens Wiklander static u32 call_err_to_res(u32 call_err)
286d4bd3d25SJens Wiklander {
287d4bd3d25SJens Wiklander switch (call_err) {
288d4bd3d25SJens Wiklander case OPTEE_SMC_RETURN_OK:
289d4bd3d25SJens Wiklander return TEE_SUCCESS;
290d4bd3d25SJens Wiklander default:
291d4bd3d25SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
292d4bd3d25SJens Wiklander }
293d4bd3d25SJens Wiklander }
294d4bd3d25SJens Wiklander
do_call_with_arg(struct udevice * dev,struct optee_msg_arg * arg)295d4bd3d25SJens Wiklander static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg)
296d4bd3d25SJens Wiklander {
297d4bd3d25SJens Wiklander struct optee_pdata *pdata = dev_get_platdata(dev);
298d4bd3d25SJens Wiklander struct rpc_param param = { .a0 = OPTEE_SMC_CALL_WITH_ARG };
299d4bd3d25SJens Wiklander void *page_list = NULL;
300d4bd3d25SJens Wiklander
301d4bd3d25SJens Wiklander reg_pair_from_64(¶m.a1, ¶m.a2, virt_to_phys(arg));
302d4bd3d25SJens Wiklander while (true) {
303d4bd3d25SJens Wiklander struct arm_smccc_res res;
304d4bd3d25SJens Wiklander
305d4bd3d25SJens Wiklander pdata->invoke_fn(param.a0, param.a1, param.a2, param.a3,
306d4bd3d25SJens Wiklander param.a4, param.a5, param.a6, param.a7, &res);
307d4bd3d25SJens Wiklander
308d4bd3d25SJens Wiklander free(page_list);
309d4bd3d25SJens Wiklander page_list = NULL;
310d4bd3d25SJens Wiklander
311d4bd3d25SJens Wiklander if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
312d4bd3d25SJens Wiklander param.a0 = res.a0;
313d4bd3d25SJens Wiklander param.a1 = res.a1;
314d4bd3d25SJens Wiklander param.a2 = res.a2;
315d4bd3d25SJens Wiklander param.a3 = res.a3;
316d4bd3d25SJens Wiklander handle_rpc(dev, ¶m, &page_list);
317d4bd3d25SJens Wiklander } else {
318*232cfd6dSJens Wiklander /*
319*232cfd6dSJens Wiklander * In case we've accessed RPMB to serve an RPC
320*232cfd6dSJens Wiklander * request we need to restore the previously
321*232cfd6dSJens Wiklander * selected partition as the caller may expect it
322*232cfd6dSJens Wiklander * to remain unchanged.
323*232cfd6dSJens Wiklander */
324*232cfd6dSJens Wiklander optee_suppl_rpmb_release(dev);
325d4bd3d25SJens Wiklander return call_err_to_res(res.a0);
326d4bd3d25SJens Wiklander }
327d4bd3d25SJens Wiklander }
328d4bd3d25SJens Wiklander }
329d4bd3d25SJens Wiklander
optee_close_session(struct udevice * dev,u32 session)330d4bd3d25SJens Wiklander static int optee_close_session(struct udevice *dev, u32 session)
331d4bd3d25SJens Wiklander {
332d4bd3d25SJens Wiklander int rc;
333d4bd3d25SJens Wiklander struct tee_shm *shm;
334d4bd3d25SJens Wiklander struct optee_msg_arg *msg_arg;
335d4bd3d25SJens Wiklander
336d4bd3d25SJens Wiklander rc = get_msg_arg(dev, 0, &shm, &msg_arg);
337d4bd3d25SJens Wiklander if (rc)
338d4bd3d25SJens Wiklander return rc;
339d4bd3d25SJens Wiklander
340d4bd3d25SJens Wiklander msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
341d4bd3d25SJens Wiklander msg_arg->session = session;
342d4bd3d25SJens Wiklander do_call_with_arg(dev, msg_arg);
343d4bd3d25SJens Wiklander
344d4bd3d25SJens Wiklander tee_shm_free(shm);
345d4bd3d25SJens Wiklander
346d4bd3d25SJens Wiklander return 0;
347d4bd3d25SJens Wiklander }
348d4bd3d25SJens Wiklander
optee_open_session(struct udevice * dev,struct tee_open_session_arg * arg,uint num_params,struct tee_param * params)349d4bd3d25SJens Wiklander static int optee_open_session(struct udevice *dev,
350d4bd3d25SJens Wiklander struct tee_open_session_arg *arg,
351d4bd3d25SJens Wiklander uint num_params, struct tee_param *params)
352d4bd3d25SJens Wiklander {
353d4bd3d25SJens Wiklander int rc;
354d4bd3d25SJens Wiklander struct tee_shm *shm;
355d4bd3d25SJens Wiklander struct optee_msg_arg *msg_arg;
356d4bd3d25SJens Wiklander
357d4bd3d25SJens Wiklander rc = get_msg_arg(dev, num_params + 2, &shm, &msg_arg);
358d4bd3d25SJens Wiklander if (rc)
359d4bd3d25SJens Wiklander return rc;
360d4bd3d25SJens Wiklander
361d4bd3d25SJens Wiklander msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION;
362d4bd3d25SJens Wiklander /*
363d4bd3d25SJens Wiklander * Initialize and add the meta parameters needed when opening a
364d4bd3d25SJens Wiklander * session.
365d4bd3d25SJens Wiklander */
366d4bd3d25SJens Wiklander msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
367d4bd3d25SJens Wiklander OPTEE_MSG_ATTR_META;
368d4bd3d25SJens Wiklander msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
369d4bd3d25SJens Wiklander OPTEE_MSG_ATTR_META;
370d4bd3d25SJens Wiklander memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid));
371d4bd3d25SJens Wiklander memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid));
372d4bd3d25SJens Wiklander msg_arg->params[1].u.value.c = arg->clnt_login;
373d4bd3d25SJens Wiklander
374d4bd3d25SJens Wiklander rc = to_msg_param(msg_arg->params + 2, num_params, params);
375d4bd3d25SJens Wiklander if (rc)
376d4bd3d25SJens Wiklander goto out;
377d4bd3d25SJens Wiklander
378d4bd3d25SJens Wiklander arg->ret = do_call_with_arg(dev, msg_arg);
379d4bd3d25SJens Wiklander if (arg->ret) {
380d4bd3d25SJens Wiklander arg->ret_origin = TEE_ORIGIN_COMMS;
381d4bd3d25SJens Wiklander goto out;
382d4bd3d25SJens Wiklander }
383d4bd3d25SJens Wiklander
384d4bd3d25SJens Wiklander if (from_msg_param(params, num_params, msg_arg->params + 2)) {
385d4bd3d25SJens Wiklander arg->ret = TEE_ERROR_COMMUNICATION;
386d4bd3d25SJens Wiklander arg->ret_origin = TEE_ORIGIN_COMMS;
387d4bd3d25SJens Wiklander /* Close session again to avoid leakage */
388d4bd3d25SJens Wiklander optee_close_session(dev, msg_arg->session);
389d4bd3d25SJens Wiklander goto out;
390d4bd3d25SJens Wiklander }
391d4bd3d25SJens Wiklander
392d4bd3d25SJens Wiklander arg->session = msg_arg->session;
393d4bd3d25SJens Wiklander arg->ret = msg_arg->ret;
394d4bd3d25SJens Wiklander arg->ret_origin = msg_arg->ret_origin;
395d4bd3d25SJens Wiklander out:
396d4bd3d25SJens Wiklander tee_shm_free(shm);
397d4bd3d25SJens Wiklander
398d4bd3d25SJens Wiklander return rc;
399d4bd3d25SJens Wiklander }
400d4bd3d25SJens Wiklander
optee_invoke_func(struct udevice * dev,struct tee_invoke_arg * arg,uint num_params,struct tee_param * params)401d4bd3d25SJens Wiklander static int optee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
402d4bd3d25SJens Wiklander uint num_params, struct tee_param *params)
403d4bd3d25SJens Wiklander {
404d4bd3d25SJens Wiklander struct tee_shm *shm;
405d4bd3d25SJens Wiklander struct optee_msg_arg *msg_arg;
406d4bd3d25SJens Wiklander int rc;
407d4bd3d25SJens Wiklander
408d4bd3d25SJens Wiklander rc = get_msg_arg(dev, num_params, &shm, &msg_arg);
409d4bd3d25SJens Wiklander if (rc)
410d4bd3d25SJens Wiklander return rc;
411d4bd3d25SJens Wiklander msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND;
412d4bd3d25SJens Wiklander msg_arg->func = arg->func;
413d4bd3d25SJens Wiklander msg_arg->session = arg->session;
414d4bd3d25SJens Wiklander
415d4bd3d25SJens Wiklander rc = to_msg_param(msg_arg->params, num_params, params);
416d4bd3d25SJens Wiklander if (rc)
417d4bd3d25SJens Wiklander goto out;
418d4bd3d25SJens Wiklander
419d4bd3d25SJens Wiklander arg->ret = do_call_with_arg(dev, msg_arg);
420d4bd3d25SJens Wiklander if (arg->ret) {
421d4bd3d25SJens Wiklander arg->ret_origin = TEE_ORIGIN_COMMS;
422d4bd3d25SJens Wiklander goto out;
423d4bd3d25SJens Wiklander }
424d4bd3d25SJens Wiklander
425d4bd3d25SJens Wiklander if (from_msg_param(params, num_params, msg_arg->params)) {
426d4bd3d25SJens Wiklander arg->ret = TEE_ERROR_COMMUNICATION;
427d4bd3d25SJens Wiklander arg->ret_origin = TEE_ORIGIN_COMMS;
428d4bd3d25SJens Wiklander goto out;
429d4bd3d25SJens Wiklander }
430d4bd3d25SJens Wiklander
431d4bd3d25SJens Wiklander arg->ret = msg_arg->ret;
432d4bd3d25SJens Wiklander arg->ret_origin = msg_arg->ret_origin;
433d4bd3d25SJens Wiklander out:
434d4bd3d25SJens Wiklander tee_shm_free(shm);
435d4bd3d25SJens Wiklander return rc;
436d4bd3d25SJens Wiklander }
437d4bd3d25SJens Wiklander
optee_shm_register(struct udevice * dev,struct tee_shm * shm)438d4bd3d25SJens Wiklander static int optee_shm_register(struct udevice *dev, struct tee_shm *shm)
439d4bd3d25SJens Wiklander {
440d4bd3d25SJens Wiklander struct tee_shm *shm_arg;
441d4bd3d25SJens Wiklander struct optee_msg_arg *msg_arg;
442d4bd3d25SJens Wiklander void *pl;
443d4bd3d25SJens Wiklander u64 ph_ptr;
444d4bd3d25SJens Wiklander int rc;
445d4bd3d25SJens Wiklander
446d4bd3d25SJens Wiklander rc = get_msg_arg(dev, 1, &shm_arg, &msg_arg);
447d4bd3d25SJens Wiklander if (rc)
448d4bd3d25SJens Wiklander return rc;
449d4bd3d25SJens Wiklander
450d4bd3d25SJens Wiklander pl = optee_alloc_and_init_page_list(shm->addr, shm->size, &ph_ptr);
451d4bd3d25SJens Wiklander if (!pl) {
452d4bd3d25SJens Wiklander rc = -ENOMEM;
453d4bd3d25SJens Wiklander goto out;
454d4bd3d25SJens Wiklander }
455d4bd3d25SJens Wiklander
456d4bd3d25SJens Wiklander msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
457d4bd3d25SJens Wiklander msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
458d4bd3d25SJens Wiklander OPTEE_MSG_ATTR_NONCONTIG;
459d4bd3d25SJens Wiklander msg_arg->params->u.tmem.buf_ptr = ph_ptr;
460d4bd3d25SJens Wiklander msg_arg->params->u.tmem.shm_ref = (ulong)shm;
461d4bd3d25SJens Wiklander msg_arg->params->u.tmem.size = shm->size;
462d4bd3d25SJens Wiklander
463d4bd3d25SJens Wiklander if (do_call_with_arg(dev, msg_arg) || msg_arg->ret)
464d4bd3d25SJens Wiklander rc = -EINVAL;
465d4bd3d25SJens Wiklander
466d4bd3d25SJens Wiklander free(pl);
467d4bd3d25SJens Wiklander out:
468d4bd3d25SJens Wiklander tee_shm_free(shm_arg);
469d4bd3d25SJens Wiklander
470d4bd3d25SJens Wiklander return rc;
471d4bd3d25SJens Wiklander }
472d4bd3d25SJens Wiklander
optee_shm_unregister(struct udevice * dev,struct tee_shm * shm)473d4bd3d25SJens Wiklander static int optee_shm_unregister(struct udevice *dev, struct tee_shm *shm)
474d4bd3d25SJens Wiklander {
475d4bd3d25SJens Wiklander struct tee_shm *shm_arg;
476d4bd3d25SJens Wiklander struct optee_msg_arg *msg_arg;
477d4bd3d25SJens Wiklander int rc;
478d4bd3d25SJens Wiklander
479d4bd3d25SJens Wiklander rc = get_msg_arg(dev, 1, &shm_arg, &msg_arg);
480d4bd3d25SJens Wiklander if (rc)
481d4bd3d25SJens Wiklander return rc;
482d4bd3d25SJens Wiklander
483d4bd3d25SJens Wiklander msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
484d4bd3d25SJens Wiklander msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
485d4bd3d25SJens Wiklander msg_arg->params[0].u.rmem.shm_ref = (ulong)shm;
486d4bd3d25SJens Wiklander
487d4bd3d25SJens Wiklander if (do_call_with_arg(dev, msg_arg) || msg_arg->ret)
488d4bd3d25SJens Wiklander rc = -EINVAL;
489d4bd3d25SJens Wiklander tee_shm_free(shm_arg);
490d4bd3d25SJens Wiklander
491d4bd3d25SJens Wiklander return rc;
492d4bd3d25SJens Wiklander }
493d4bd3d25SJens Wiklander
494d4bd3d25SJens Wiklander static const struct tee_driver_ops optee_ops = {
495d4bd3d25SJens Wiklander .get_version = optee_get_version,
496d4bd3d25SJens Wiklander .open_session = optee_open_session,
497d4bd3d25SJens Wiklander .close_session = optee_close_session,
498d4bd3d25SJens Wiklander .invoke_func = optee_invoke_func,
499d4bd3d25SJens Wiklander .shm_register = optee_shm_register,
500d4bd3d25SJens Wiklander .shm_unregister = optee_shm_unregister,
501d4bd3d25SJens Wiklander };
502d4bd3d25SJens Wiklander
is_optee_api(optee_invoke_fn * invoke_fn)503d4bd3d25SJens Wiklander static bool is_optee_api(optee_invoke_fn *invoke_fn)
504d4bd3d25SJens Wiklander {
505d4bd3d25SJens Wiklander struct arm_smccc_res res;
506d4bd3d25SJens Wiklander
507d4bd3d25SJens Wiklander invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
508d4bd3d25SJens Wiklander
509d4bd3d25SJens Wiklander return res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 &&
510d4bd3d25SJens Wiklander res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3;
511d4bd3d25SJens Wiklander }
512d4bd3d25SJens Wiklander
print_os_revision(optee_invoke_fn * invoke_fn)513d4bd3d25SJens Wiklander static void print_os_revision(optee_invoke_fn *invoke_fn)
514d4bd3d25SJens Wiklander {
515d4bd3d25SJens Wiklander union {
516d4bd3d25SJens Wiklander struct arm_smccc_res smccc;
517d4bd3d25SJens Wiklander struct optee_smc_call_get_os_revision_result result;
518d4bd3d25SJens Wiklander } res = {
519d4bd3d25SJens Wiklander .result = {
520d4bd3d25SJens Wiklander .build_id = 0
521d4bd3d25SJens Wiklander }
522d4bd3d25SJens Wiklander };
523d4bd3d25SJens Wiklander
524d4bd3d25SJens Wiklander invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
525d4bd3d25SJens Wiklander &res.smccc);
526d4bd3d25SJens Wiklander
527d4bd3d25SJens Wiklander if (res.result.build_id)
528d4bd3d25SJens Wiklander debug("OP-TEE revision %lu.%lu (%08lx)\n", res.result.major,
529d4bd3d25SJens Wiklander res.result.minor, res.result.build_id);
530d4bd3d25SJens Wiklander else
531d4bd3d25SJens Wiklander debug("OP-TEE revision %lu.%lu\n", res.result.major,
532d4bd3d25SJens Wiklander res.result.minor);
533d4bd3d25SJens Wiklander }
534d4bd3d25SJens Wiklander
api_revision_is_compatible(optee_invoke_fn * invoke_fn)535d4bd3d25SJens Wiklander static bool api_revision_is_compatible(optee_invoke_fn *invoke_fn)
536d4bd3d25SJens Wiklander {
537d4bd3d25SJens Wiklander union {
538d4bd3d25SJens Wiklander struct arm_smccc_res smccc;
539d4bd3d25SJens Wiklander struct optee_smc_calls_revision_result result;
540d4bd3d25SJens Wiklander } res;
541d4bd3d25SJens Wiklander
542d4bd3d25SJens Wiklander invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
543d4bd3d25SJens Wiklander
544d4bd3d25SJens Wiklander return res.result.major == OPTEE_MSG_REVISION_MAJOR &&
545d4bd3d25SJens Wiklander (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR;
546d4bd3d25SJens Wiklander }
547d4bd3d25SJens Wiklander
exchange_capabilities(optee_invoke_fn * invoke_fn,u32 * sec_caps)548d4bd3d25SJens Wiklander static bool exchange_capabilities(optee_invoke_fn *invoke_fn, u32 *sec_caps)
549d4bd3d25SJens Wiklander {
550d4bd3d25SJens Wiklander union {
551d4bd3d25SJens Wiklander struct arm_smccc_res smccc;
552d4bd3d25SJens Wiklander struct optee_smc_exchange_capabilities_result result;
553d4bd3d25SJens Wiklander } res;
554d4bd3d25SJens Wiklander
555d4bd3d25SJens Wiklander invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES,
556d4bd3d25SJens Wiklander OPTEE_SMC_NSEC_CAP_UNIPROCESSOR, 0, 0, 0, 0, 0, 0,
557d4bd3d25SJens Wiklander &res.smccc);
558d4bd3d25SJens Wiklander
559d4bd3d25SJens Wiklander if (res.result.status != OPTEE_SMC_RETURN_OK)
560d4bd3d25SJens Wiklander return false;
561d4bd3d25SJens Wiklander
562d4bd3d25SJens Wiklander *sec_caps = res.result.capabilities;
563d4bd3d25SJens Wiklander
564d4bd3d25SJens Wiklander return true;
565d4bd3d25SJens Wiklander }
566d4bd3d25SJens Wiklander
567d4bd3d25SJens Wiklander /* Simple wrapper functions to be able to use a function pointer */
optee_smccc_smc(unsigned long a0,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long a4,unsigned long a5,unsigned long a6,unsigned long a7,struct arm_smccc_res * res)568d4bd3d25SJens Wiklander static void optee_smccc_smc(unsigned long a0, unsigned long a1,
569d4bd3d25SJens Wiklander unsigned long a2, unsigned long a3,
570d4bd3d25SJens Wiklander unsigned long a4, unsigned long a5,
571d4bd3d25SJens Wiklander unsigned long a6, unsigned long a7,
572d4bd3d25SJens Wiklander struct arm_smccc_res *res)
573d4bd3d25SJens Wiklander {
574d4bd3d25SJens Wiklander arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
575d4bd3d25SJens Wiklander }
576d4bd3d25SJens Wiklander
optee_smccc_hvc(unsigned long a0,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long a4,unsigned long a5,unsigned long a6,unsigned long a7,struct arm_smccc_res * res)577d4bd3d25SJens Wiklander static void optee_smccc_hvc(unsigned long a0, unsigned long a1,
578d4bd3d25SJens Wiklander unsigned long a2, unsigned long a3,
579d4bd3d25SJens Wiklander unsigned long a4, unsigned long a5,
580d4bd3d25SJens Wiklander unsigned long a6, unsigned long a7,
581d4bd3d25SJens Wiklander struct arm_smccc_res *res)
582d4bd3d25SJens Wiklander {
583d4bd3d25SJens Wiklander arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
584d4bd3d25SJens Wiklander }
585d4bd3d25SJens Wiklander
get_invoke_func(struct udevice * dev)586d4bd3d25SJens Wiklander static optee_invoke_fn *get_invoke_func(struct udevice *dev)
587d4bd3d25SJens Wiklander {
588d4bd3d25SJens Wiklander const char *method;
589d4bd3d25SJens Wiklander
590d4bd3d25SJens Wiklander debug("optee: looking for conduit method in DT.\n");
591d4bd3d25SJens Wiklander method = ofnode_get_property(dev->node, "method", NULL);
592d4bd3d25SJens Wiklander if (!method) {
593d4bd3d25SJens Wiklander debug("optee: missing \"method\" property\n");
594d4bd3d25SJens Wiklander return ERR_PTR(-ENXIO);
595d4bd3d25SJens Wiklander }
596d4bd3d25SJens Wiklander
597d4bd3d25SJens Wiklander if (!strcmp("hvc", method))
598d4bd3d25SJens Wiklander return optee_smccc_hvc;
599d4bd3d25SJens Wiklander else if (!strcmp("smc", method))
600d4bd3d25SJens Wiklander return optee_smccc_smc;
601d4bd3d25SJens Wiklander
602d4bd3d25SJens Wiklander debug("optee: invalid \"method\" property: %s\n", method);
603d4bd3d25SJens Wiklander return ERR_PTR(-EINVAL);
604d4bd3d25SJens Wiklander }
605d4bd3d25SJens Wiklander
optee_ofdata_to_platdata(struct udevice * dev)606d4bd3d25SJens Wiklander static int optee_ofdata_to_platdata(struct udevice *dev)
607d4bd3d25SJens Wiklander {
608d4bd3d25SJens Wiklander struct optee_pdata *pdata = dev_get_platdata(dev);
609d4bd3d25SJens Wiklander
610d4bd3d25SJens Wiklander pdata->invoke_fn = get_invoke_func(dev);
611d4bd3d25SJens Wiklander if (IS_ERR(pdata->invoke_fn))
612d4bd3d25SJens Wiklander return PTR_ERR(pdata->invoke_fn);
613d4bd3d25SJens Wiklander
614d4bd3d25SJens Wiklander return 0;
615d4bd3d25SJens Wiklander }
616d4bd3d25SJens Wiklander
optee_probe(struct udevice * dev)617d4bd3d25SJens Wiklander static int optee_probe(struct udevice *dev)
618d4bd3d25SJens Wiklander {
619d4bd3d25SJens Wiklander struct optee_pdata *pdata = dev_get_platdata(dev);
620d4bd3d25SJens Wiklander u32 sec_caps;
621d4bd3d25SJens Wiklander
622d4bd3d25SJens Wiklander if (!is_optee_api(pdata->invoke_fn)) {
623d4bd3d25SJens Wiklander debug("%s: OP-TEE api uid mismatch\n", __func__);
624d4bd3d25SJens Wiklander return -ENOENT;
625d4bd3d25SJens Wiklander }
626d4bd3d25SJens Wiklander
627d4bd3d25SJens Wiklander print_os_revision(pdata->invoke_fn);
628d4bd3d25SJens Wiklander
629d4bd3d25SJens Wiklander if (!api_revision_is_compatible(pdata->invoke_fn)) {
630d4bd3d25SJens Wiklander debug("%s: OP-TEE api revision mismatch\n", __func__);
631d4bd3d25SJens Wiklander return -ENOENT;
632d4bd3d25SJens Wiklander }
633d4bd3d25SJens Wiklander
634d4bd3d25SJens Wiklander /*
635d4bd3d25SJens Wiklander * OP-TEE can use both shared memory via predefined pool or as
636d4bd3d25SJens Wiklander * dynamic shared memory provided by normal world. To keep things
637d4bd3d25SJens Wiklander * simple we're only using dynamic shared memory in this driver.
638d4bd3d25SJens Wiklander */
639d4bd3d25SJens Wiklander if (!exchange_capabilities(pdata->invoke_fn, &sec_caps) ||
640d4bd3d25SJens Wiklander !(sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)) {
641d4bd3d25SJens Wiklander debug("%s: OP-TEE capabilities mismatch\n", __func__);
642d4bd3d25SJens Wiklander return -ENOENT;
643d4bd3d25SJens Wiklander }
644d4bd3d25SJens Wiklander
645d4bd3d25SJens Wiklander return 0;
646d4bd3d25SJens Wiklander }
647d4bd3d25SJens Wiklander
648d4bd3d25SJens Wiklander static const struct udevice_id optee_match[] = {
649d4bd3d25SJens Wiklander { .compatible = "linaro,optee-tz" },
650d4bd3d25SJens Wiklander {},
651d4bd3d25SJens Wiklander };
652d4bd3d25SJens Wiklander
653d4bd3d25SJens Wiklander U_BOOT_DRIVER(optee) = {
654d4bd3d25SJens Wiklander .name = "optee",
655d4bd3d25SJens Wiklander .id = UCLASS_TEE,
656d4bd3d25SJens Wiklander .of_match = optee_match,
657d4bd3d25SJens Wiklander .ofdata_to_platdata = optee_ofdata_to_platdata,
658d4bd3d25SJens Wiklander .probe = optee_probe,
659d4bd3d25SJens Wiklander .ops = &optee_ops,
660d4bd3d25SJens Wiklander .platdata_auto_alloc_size = sizeof(struct optee_pdata),
661*232cfd6dSJens Wiklander .priv_auto_alloc_size = sizeof(struct optee_private),
662d4bd3d25SJens Wiklander };
663