xref: /openbmc/linux/drivers/tee/amdtee/call.c (revision 3a83e4e6)
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 }
374