xref: /linux/drivers/tee/amdtee/call.c (revision 0526b56cbc3c489642bd6a5fe4b718dea7ef0ee8)
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.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 static DEFINE_MUTEX(ta_refcount_mutex);
125 static LIST_HEAD(ta_list);
126 
127 static u32 get_ta_refcount(u32 ta_handle)
128 {
129 	struct amdtee_ta_data *ta_data;
130 	u32 count = 0;
131 
132 	/* Caller must hold a mutex */
133 	list_for_each_entry(ta_data, &ta_list, list_node)
134 		if (ta_data->ta_handle == ta_handle)
135 			return ++ta_data->refcount;
136 
137 	ta_data = kzalloc(sizeof(*ta_data), GFP_KERNEL);
138 	if (ta_data) {
139 		ta_data->ta_handle = ta_handle;
140 		ta_data->refcount = 1;
141 		count = ta_data->refcount;
142 		list_add(&ta_data->list_node, &ta_list);
143 	}
144 
145 	return count;
146 }
147 
148 static u32 put_ta_refcount(u32 ta_handle)
149 {
150 	struct amdtee_ta_data *ta_data;
151 	u32 count = 0;
152 
153 	/* Caller must hold a mutex */
154 	list_for_each_entry(ta_data, &ta_list, list_node)
155 		if (ta_data->ta_handle == ta_handle) {
156 			count = --ta_data->refcount;
157 			if (count == 0) {
158 				list_del(&ta_data->list_node);
159 				kfree(ta_data);
160 				break;
161 			}
162 		}
163 
164 	return count;
165 }
166 
167 int handle_unload_ta(u32 ta_handle)
168 {
169 	struct tee_cmd_unload_ta cmd = {0};
170 	u32 status, count;
171 	int ret;
172 
173 	if (!ta_handle)
174 		return -EINVAL;
175 
176 	mutex_lock(&ta_refcount_mutex);
177 
178 	count = put_ta_refcount(ta_handle);
179 
180 	if (count) {
181 		pr_debug("unload ta: not unloading %u count %u\n",
182 			 ta_handle, count);
183 		ret = -EBUSY;
184 		goto unlock;
185 	}
186 
187 	cmd.ta_handle = ta_handle;
188 
189 	ret = psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, (void *)&cmd,
190 				  sizeof(cmd), &status);
191 	if (!ret && status != 0) {
192 		pr_err("unload ta: status = 0x%x\n", status);
193 		ret = -EBUSY;
194 	} else {
195 		pr_debug("unloaded ta handle %u\n", ta_handle);
196 	}
197 
198 unlock:
199 	mutex_unlock(&ta_refcount_mutex);
200 	return ret;
201 }
202 
203 int handle_close_session(u32 ta_handle, u32 info)
204 {
205 	struct tee_cmd_close_session cmd = {0};
206 	u32 status;
207 	int ret;
208 
209 	if (ta_handle == 0)
210 		return -EINVAL;
211 
212 	cmd.ta_handle = ta_handle;
213 	cmd.session_info = info;
214 
215 	ret = psp_tee_process_cmd(TEE_CMD_ID_CLOSE_SESSION, (void *)&cmd,
216 				  sizeof(cmd), &status);
217 	if (!ret && status != 0) {
218 		pr_err("close session: status = 0x%x\n", status);
219 		ret = -EBUSY;
220 	}
221 
222 	return ret;
223 }
224 
225 void handle_unmap_shmem(u32 buf_id)
226 {
227 	struct tee_cmd_unmap_shared_mem cmd = {0};
228 	u32 status;
229 	int ret;
230 
231 	cmd.buf_id = buf_id;
232 
233 	ret = psp_tee_process_cmd(TEE_CMD_ID_UNMAP_SHARED_MEM, (void *)&cmd,
234 				  sizeof(cmd), &status);
235 	if (!ret)
236 		pr_debug("unmap shared memory: buf_id %u status = 0x%x\n",
237 			 buf_id, status);
238 }
239 
240 int handle_invoke_cmd(struct tee_ioctl_invoke_arg *arg, u32 sinfo,
241 		      struct tee_param *p)
242 {
243 	struct tee_cmd_invoke_cmd cmd = {0};
244 	int ret;
245 
246 	if (!arg || (!p && arg->num_params))
247 		return -EINVAL;
248 
249 	arg->ret_origin = TEEC_ORIGIN_COMMS;
250 
251 	if (arg->session == 0) {
252 		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
253 		return -EINVAL;
254 	}
255 
256 	ret = tee_params_to_amd_params(p, arg->num_params, &cmd.op);
257 	if (ret) {
258 		pr_err("invalid Params. Abort invoke command\n");
259 		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
260 		return ret;
261 	}
262 
263 	cmd.ta_handle = get_ta_handle(arg->session);
264 	cmd.cmd_id = arg->func;
265 	cmd.session_info = sinfo;
266 
267 	ret = psp_tee_process_cmd(TEE_CMD_ID_INVOKE_CMD, (void *)&cmd,
268 				  sizeof(cmd), &arg->ret);
269 	if (ret) {
270 		arg->ret = TEEC_ERROR_COMMUNICATION;
271 	} else {
272 		ret = amd_params_to_tee_params(p, arg->num_params, &cmd.op);
273 		if (unlikely(ret)) {
274 			pr_err("invoke command: failed to copy output\n");
275 			arg->ret = TEEC_ERROR_GENERIC;
276 			return ret;
277 		}
278 		arg->ret_origin = cmd.return_origin;
279 		pr_debug("invoke command: RO = 0x%x ret = 0x%x\n",
280 			 arg->ret_origin, arg->ret);
281 	}
282 
283 	return ret;
284 }
285 
286 int handle_map_shmem(u32 count, struct shmem_desc *start, u32 *buf_id)
287 {
288 	struct tee_cmd_map_shared_mem *cmd;
289 	phys_addr_t paddr;
290 	int ret, i;
291 	u32 status;
292 
293 	if (!count || !start || !buf_id)
294 		return -EINVAL;
295 
296 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
297 	if (!cmd)
298 		return -ENOMEM;
299 
300 	/* Size must be page aligned */
301 	for (i = 0; i < count ; i++) {
302 		if (!start[i].kaddr || (start[i].size & (PAGE_SIZE - 1))) {
303 			ret = -EINVAL;
304 			goto free_cmd;
305 		}
306 
307 		if ((u64)start[i].kaddr & (PAGE_SIZE - 1)) {
308 			pr_err("map shared memory: page unaligned. addr 0x%llx",
309 			       (u64)start[i].kaddr);
310 			ret = -EINVAL;
311 			goto free_cmd;
312 		}
313 	}
314 
315 	cmd->sg_list.count = count;
316 
317 	/* Create buffer list */
318 	for (i = 0; i < count ; i++) {
319 		paddr = __psp_pa(start[i].kaddr);
320 		cmd->sg_list.buf[i].hi_addr = upper_32_bits(paddr);
321 		cmd->sg_list.buf[i].low_addr = lower_32_bits(paddr);
322 		cmd->sg_list.buf[i].size = start[i].size;
323 		cmd->sg_list.size += cmd->sg_list.buf[i].size;
324 
325 		pr_debug("buf[%d]:hi addr = 0x%x\n", i,
326 			 cmd->sg_list.buf[i].hi_addr);
327 		pr_debug("buf[%d]:low addr = 0x%x\n", i,
328 			 cmd->sg_list.buf[i].low_addr);
329 		pr_debug("buf[%d]:size = 0x%x\n", i, cmd->sg_list.buf[i].size);
330 		pr_debug("list size = 0x%x\n", cmd->sg_list.size);
331 	}
332 
333 	*buf_id = 0;
334 
335 	ret = psp_tee_process_cmd(TEE_CMD_ID_MAP_SHARED_MEM, (void *)cmd,
336 				  sizeof(*cmd), &status);
337 	if (!ret && !status) {
338 		*buf_id = cmd->buf_id;
339 		pr_debug("mapped buffer ID = 0x%x\n", *buf_id);
340 	} else {
341 		pr_err("map shared memory: status = 0x%x\n", status);
342 		ret = -ENOMEM;
343 	}
344 
345 free_cmd:
346 	kfree(cmd);
347 
348 	return ret;
349 }
350 
351 int handle_open_session(struct tee_ioctl_open_session_arg *arg, u32 *info,
352 			struct tee_param *p)
353 {
354 	struct tee_cmd_open_session cmd = {0};
355 	int ret;
356 
357 	if (!arg || !info || (!p && arg->num_params))
358 		return -EINVAL;
359 
360 	arg->ret_origin = TEEC_ORIGIN_COMMS;
361 
362 	if (arg->session == 0) {
363 		arg->ret = TEEC_ERROR_GENERIC;
364 		return -EINVAL;
365 	}
366 
367 	ret = tee_params_to_amd_params(p, arg->num_params, &cmd.op);
368 	if (ret) {
369 		pr_err("invalid Params. Abort open session\n");
370 		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
371 		return ret;
372 	}
373 
374 	cmd.ta_handle = get_ta_handle(arg->session);
375 	*info = 0;
376 
377 	ret = psp_tee_process_cmd(TEE_CMD_ID_OPEN_SESSION, (void *)&cmd,
378 				  sizeof(cmd), &arg->ret);
379 	if (ret) {
380 		arg->ret = TEEC_ERROR_COMMUNICATION;
381 	} else {
382 		ret = amd_params_to_tee_params(p, arg->num_params, &cmd.op);
383 		if (unlikely(ret)) {
384 			pr_err("open session: failed to copy output\n");
385 			arg->ret = TEEC_ERROR_GENERIC;
386 			return ret;
387 		}
388 		arg->ret_origin = cmd.return_origin;
389 		*info = cmd.session_info;
390 		pr_debug("open session: session info = 0x%x\n", *info);
391 	}
392 
393 	pr_debug("open session: ret = 0x%x RO = 0x%x\n", arg->ret,
394 		 arg->ret_origin);
395 
396 	return ret;
397 }
398 
399 int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg)
400 {
401 	struct tee_cmd_unload_ta unload_cmd = {};
402 	struct tee_cmd_load_ta load_cmd = {};
403 	phys_addr_t blob;
404 	int ret;
405 
406 	if (size == 0 || !data || !arg)
407 		return -EINVAL;
408 
409 	blob = __psp_pa(data);
410 	if (blob & (PAGE_SIZE - 1)) {
411 		pr_err("load TA: page unaligned. blob 0x%llx", blob);
412 		return -EINVAL;
413 	}
414 
415 	load_cmd.hi_addr = upper_32_bits(blob);
416 	load_cmd.low_addr = lower_32_bits(blob);
417 	load_cmd.size = size;
418 
419 	mutex_lock(&ta_refcount_mutex);
420 
421 	ret = psp_tee_process_cmd(TEE_CMD_ID_LOAD_TA, (void *)&load_cmd,
422 				  sizeof(load_cmd), &arg->ret);
423 	if (ret) {
424 		arg->ret_origin = TEEC_ORIGIN_COMMS;
425 		arg->ret = TEEC_ERROR_COMMUNICATION;
426 	} else if (arg->ret == TEEC_SUCCESS) {
427 		ret = get_ta_refcount(load_cmd.ta_handle);
428 		if (!ret) {
429 			arg->ret_origin = TEEC_ORIGIN_COMMS;
430 			arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
431 
432 			/* Unload the TA on error */
433 			unload_cmd.ta_handle = load_cmd.ta_handle;
434 			psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA,
435 					    (void *)&unload_cmd,
436 					    sizeof(unload_cmd), &ret);
437 		} else {
438 			set_session_id(load_cmd.ta_handle, 0, &arg->session);
439 		}
440 	}
441 	mutex_unlock(&ta_refcount_mutex);
442 
443 	pr_debug("load TA: TA handle = 0x%x, RO = 0x%x, ret = 0x%x\n",
444 		 load_cmd.ta_handle, arg->ret_origin, arg->ret);
445 
446 	return 0;
447 }
448