Merge tag 'amd-drm-fixes-5.11-2020-12-23' of git://people.freedesktop.org/~agd5f...
[linux-2.6-microblaze.git] / drivers / tee / amdtee / call.c
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright 2019 Advanced Micro Devices, Inc.
4  */
5
6 #include <linux/device.h>
7 #include <linux/tee.h>
8 #include <linux/tee_drv.h>
9 #include <linux/psp-tee.h>
10 #include <linux/slab.h>
11 #include <linux/psp-sev.h>
12 #include "amdtee_if.h"
13 #include "amdtee_private.h"
14
15 static int tee_params_to_amd_params(struct tee_param *tee, u32 count,
16                                     struct tee_operation *amd)
17 {
18         int i, ret = 0;
19         u32 type;
20
21         if (!count)
22                 return 0;
23
24         if (!tee || !amd || count > TEE_MAX_PARAMS)
25                 return -EINVAL;
26
27         amd->param_types = 0;
28         for (i = 0; i < count; i++) {
29                 /* AMD TEE does not support meta parameter */
30                 if (tee[i].attr > TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT)
31                         return -EINVAL;
32
33                 amd->param_types |= ((tee[i].attr & 0xF) << i * 4);
34         }
35
36         for (i = 0; i < count; i++) {
37                 type = TEE_PARAM_TYPE_GET(amd->param_types, i);
38                 pr_debug("%s: type[%d] = 0x%x\n", __func__, i, type);
39
40                 if (type == TEE_OP_PARAM_TYPE_INVALID)
41                         return -EINVAL;
42
43                 if (type == TEE_OP_PARAM_TYPE_NONE)
44                         continue;
45
46                 /* It is assumed that all values are within 2^32-1 */
47                 if (type > TEE_OP_PARAM_TYPE_VALUE_INOUT) {
48                         u32 buf_id = get_buffer_id(tee[i].u.memref.shm);
49
50                         amd->params[i].mref.buf_id = buf_id;
51                         amd->params[i].mref.offset = tee[i].u.memref.shm_offs;
52                         amd->params[i].mref.size = tee[i].u.memref.size;
53                         pr_debug("%s: bufid[%d] = 0x%x, offset[%d] = 0x%x, size[%d] = 0x%x\n",
54                                  __func__,
55                                  i, amd->params[i].mref.buf_id,
56                                  i, amd->params[i].mref.offset,
57                                  i, amd->params[i].mref.size);
58                 } else {
59                         if (tee[i].u.value.c)
60                                 pr_warn("%s: Discarding value c", __func__);
61
62                         amd->params[i].val.a = tee[i].u.value.a;
63                         amd->params[i].val.b = tee[i].u.value.b;
64                         pr_debug("%s: a[%d] = 0x%x, b[%d] = 0x%x\n", __func__,
65                                  i, amd->params[i].val.a,
66                                  i, amd->params[i].val.b);
67                 }
68         }
69         return ret;
70 }
71
72 static int amd_params_to_tee_params(struct tee_param *tee, u32 count,
73                                     struct tee_operation *amd)
74 {
75         int i, ret = 0;
76         u32 type;
77
78         if (!count)
79                 return 0;
80
81         if (!tee || !amd || count > TEE_MAX_PARAMS)
82                 return -EINVAL;
83
84         /* Assumes amd->param_types is valid */
85         for (i = 0; i < count; i++) {
86                 type = TEE_PARAM_TYPE_GET(amd->param_types, i);
87                 pr_debug("%s: type[%d] = 0x%x\n", __func__, i, type);
88
89                 if (type == TEE_OP_PARAM_TYPE_INVALID ||
90                     type > TEE_OP_PARAM_TYPE_MEMREF_INOUT)
91                         return -EINVAL;
92
93                 if (type == TEE_OP_PARAM_TYPE_NONE ||
94                     type == TEE_OP_PARAM_TYPE_VALUE_INPUT ||
95                     type == TEE_OP_PARAM_TYPE_MEMREF_INPUT)
96                         continue;
97
98                 /*
99                  * It is assumed that buf_id remains unchanged for
100                  * both open_session and invoke_cmd call
101                  */
102                 if (type > TEE_OP_PARAM_TYPE_MEMREF_INPUT) {
103                         tee[i].u.memref.shm_offs = amd->params[i].mref.offset;
104                         tee[i].u.memref.size = amd->params[i].mref.size;
105                         pr_debug("%s: bufid[%d] = 0x%x, offset[%d] = 0x%x, size[%d] = 0x%x\n",
106                                  __func__,
107                                  i, amd->params[i].mref.buf_id,
108                                  i, amd->params[i].mref.offset,
109                                  i, amd->params[i].mref.size);
110                 } else {
111                         /* field 'c' not supported by AMD TEE */
112                         tee[i].u.value.a = amd->params[i].val.a;
113                         tee[i].u.value.b = amd->params[i].val.b;
114                         tee[i].u.value.c = 0;
115                         pr_debug("%s: a[%d] = 0x%x, b[%d] = 0x%x\n",
116                                  __func__,
117                                  i, amd->params[i].val.a,
118                                  i, amd->params[i].val.b);
119                 }
120         }
121         return ret;
122 }
123
124 int handle_unload_ta(u32 ta_handle)
125 {
126         struct tee_cmd_unload_ta cmd = {0};
127         u32 status;
128         int ret;
129
130         if (!ta_handle)
131                 return -EINVAL;
132
133         cmd.ta_handle = ta_handle;
134
135         ret = psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, (void *)&cmd,
136                                   sizeof(cmd), &status);
137         if (!ret && status != 0) {
138                 pr_err("unload ta: status = 0x%x\n", status);
139                 ret = -EBUSY;
140         }
141
142         return ret;
143 }
144
145 int handle_close_session(u32 ta_handle, u32 info)
146 {
147         struct tee_cmd_close_session cmd = {0};
148         u32 status;
149         int ret;
150
151         if (ta_handle == 0)
152                 return -EINVAL;
153
154         cmd.ta_handle = ta_handle;
155         cmd.session_info = info;
156
157         ret = psp_tee_process_cmd(TEE_CMD_ID_CLOSE_SESSION, (void *)&cmd,
158                                   sizeof(cmd), &status);
159         if (!ret && status != 0) {
160                 pr_err("close session: status = 0x%x\n", status);
161                 ret = -EBUSY;
162         }
163
164         return ret;
165 }
166
167 void handle_unmap_shmem(u32 buf_id)
168 {
169         struct tee_cmd_unmap_shared_mem cmd = {0};
170         u32 status;
171         int ret;
172
173         cmd.buf_id = buf_id;
174
175         ret = psp_tee_process_cmd(TEE_CMD_ID_UNMAP_SHARED_MEM, (void *)&cmd,
176                                   sizeof(cmd), &status);
177         if (!ret)
178                 pr_debug("unmap shared memory: buf_id %u status = 0x%x\n",
179                          buf_id, status);
180 }
181
182 int handle_invoke_cmd(struct tee_ioctl_invoke_arg *arg, u32 sinfo,
183                       struct tee_param *p)
184 {
185         struct tee_cmd_invoke_cmd cmd = {0};
186         int ret;
187
188         if (!arg || (!p && arg->num_params))
189                 return -EINVAL;
190
191         arg->ret_origin = TEEC_ORIGIN_COMMS;
192
193         if (arg->session == 0) {
194                 arg->ret = TEEC_ERROR_BAD_PARAMETERS;
195                 return -EINVAL;
196         }
197
198         ret = tee_params_to_amd_params(p, arg->num_params, &cmd.op);
199         if (ret) {
200                 pr_err("invalid Params. Abort invoke command\n");
201                 arg->ret = TEEC_ERROR_BAD_PARAMETERS;
202                 return ret;
203         }
204
205         cmd.ta_handle = get_ta_handle(arg->session);
206         cmd.cmd_id = arg->func;
207         cmd.session_info = sinfo;
208
209         ret = psp_tee_process_cmd(TEE_CMD_ID_INVOKE_CMD, (void *)&cmd,
210                                   sizeof(cmd), &arg->ret);
211         if (ret) {
212                 arg->ret = TEEC_ERROR_COMMUNICATION;
213         } else {
214                 ret = amd_params_to_tee_params(p, arg->num_params, &cmd.op);
215                 if (unlikely(ret)) {
216                         pr_err("invoke command: failed to copy output\n");
217                         arg->ret = TEEC_ERROR_GENERIC;
218                         return ret;
219                 }
220                 arg->ret_origin = cmd.return_origin;
221                 pr_debug("invoke command: RO = 0x%x ret = 0x%x\n",
222                          arg->ret_origin, arg->ret);
223         }
224
225         return ret;
226 }
227
228 int handle_map_shmem(u32 count, struct shmem_desc *start, u32 *buf_id)
229 {
230         struct tee_cmd_map_shared_mem *cmd;
231         phys_addr_t paddr;
232         int ret, i;
233         u32 status;
234
235         if (!count || !start || !buf_id)
236                 return -EINVAL;
237
238         cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
239         if (!cmd)
240                 return -ENOMEM;
241
242         /* Size must be page aligned */
243         for (i = 0; i < count ; i++) {
244                 if (!start[i].kaddr || (start[i].size & (PAGE_SIZE - 1))) {
245                         ret = -EINVAL;
246                         goto free_cmd;
247                 }
248
249                 if ((u64)start[i].kaddr & (PAGE_SIZE - 1)) {
250                         pr_err("map shared memory: page unaligned. addr 0x%llx",
251                                (u64)start[i].kaddr);
252                         ret = -EINVAL;
253                         goto free_cmd;
254                 }
255         }
256
257         cmd->sg_list.count = count;
258
259         /* Create buffer list */
260         for (i = 0; i < count ; i++) {
261                 paddr = __psp_pa(start[i].kaddr);
262                 cmd->sg_list.buf[i].hi_addr = upper_32_bits(paddr);
263                 cmd->sg_list.buf[i].low_addr = lower_32_bits(paddr);
264                 cmd->sg_list.buf[i].size = start[i].size;
265                 cmd->sg_list.size += cmd->sg_list.buf[i].size;
266
267                 pr_debug("buf[%d]:hi addr = 0x%x\n", i,
268                          cmd->sg_list.buf[i].hi_addr);
269                 pr_debug("buf[%d]:low addr = 0x%x\n", i,
270                          cmd->sg_list.buf[i].low_addr);
271                 pr_debug("buf[%d]:size = 0x%x\n", i, cmd->sg_list.buf[i].size);
272                 pr_debug("list size = 0x%x\n", cmd->sg_list.size);
273         }
274
275         *buf_id = 0;
276
277         ret = psp_tee_process_cmd(TEE_CMD_ID_MAP_SHARED_MEM, (void *)cmd,
278                                   sizeof(*cmd), &status);
279         if (!ret && !status) {
280                 *buf_id = cmd->buf_id;
281                 pr_debug("mapped buffer ID = 0x%x\n", *buf_id);
282         } else {
283                 pr_err("map shared memory: status = 0x%x\n", status);
284                 ret = -ENOMEM;
285         }
286
287 free_cmd:
288         kfree(cmd);
289
290         return ret;
291 }
292
293 int handle_open_session(struct tee_ioctl_open_session_arg *arg, u32 *info,
294                         struct tee_param *p)
295 {
296         struct tee_cmd_open_session cmd = {0};
297         int ret;
298
299         if (!arg || !info || (!p && arg->num_params))
300                 return -EINVAL;
301
302         arg->ret_origin = TEEC_ORIGIN_COMMS;
303
304         if (arg->session == 0) {
305                 arg->ret = TEEC_ERROR_GENERIC;
306                 return -EINVAL;
307         }
308
309         ret = tee_params_to_amd_params(p, arg->num_params, &cmd.op);
310         if (ret) {
311                 pr_err("invalid Params. Abort open session\n");
312                 arg->ret = TEEC_ERROR_BAD_PARAMETERS;
313                 return ret;
314         }
315
316         cmd.ta_handle = get_ta_handle(arg->session);
317         *info = 0;
318
319         ret = psp_tee_process_cmd(TEE_CMD_ID_OPEN_SESSION, (void *)&cmd,
320                                   sizeof(cmd), &arg->ret);
321         if (ret) {
322                 arg->ret = TEEC_ERROR_COMMUNICATION;
323         } else {
324                 ret = amd_params_to_tee_params(p, arg->num_params, &cmd.op);
325                 if (unlikely(ret)) {
326                         pr_err("open session: failed to copy output\n");
327                         arg->ret = TEEC_ERROR_GENERIC;
328                         return ret;
329                 }
330                 arg->ret_origin = cmd.return_origin;
331                 *info = cmd.session_info;
332                 pr_debug("open session: session info = 0x%x\n", *info);
333         }
334
335         pr_debug("open session: ret = 0x%x RO = 0x%x\n", arg->ret,
336                  arg->ret_origin);
337
338         return ret;
339 }
340
341 int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg)
342 {
343         struct tee_cmd_load_ta cmd = {0};
344         phys_addr_t blob;
345         int ret;
346
347         if (size == 0 || !data || !arg)
348                 return -EINVAL;
349
350         blob = __psp_pa(data);
351         if (blob & (PAGE_SIZE - 1)) {
352                 pr_err("load TA: page unaligned. blob 0x%llx", blob);
353                 return -EINVAL;
354         }
355
356         cmd.hi_addr = upper_32_bits(blob);
357         cmd.low_addr = lower_32_bits(blob);
358         cmd.size = size;
359
360         ret = psp_tee_process_cmd(TEE_CMD_ID_LOAD_TA, (void *)&cmd,
361                                   sizeof(cmd), &arg->ret);
362         if (ret) {
363                 arg->ret_origin = TEEC_ORIGIN_COMMS;
364                 arg->ret = TEEC_ERROR_COMMUNICATION;
365         } else {
366                 set_session_id(cmd.ta_handle, 0, &arg->session);
367         }
368
369         pr_debug("load TA: TA handle = 0x%x, RO = 0x%x, ret = 0x%x\n",
370                  cmd.ta_handle, arg->ret_origin, arg->ret);
371
372         return 0;
373 }