1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright 2019 Advanced Micro Devices, Inc. |
4 | */ |
5 | |
6 | #include <linux/errno.h> |
7 | #include <linux/io.h> |
8 | #include <linux/module.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/string.h> |
11 | #include <linux/device.h> |
12 | #include <linux/tee_drv.h> |
13 | #include <linux/types.h> |
14 | #include <linux/mm.h> |
15 | #include <linux/uaccess.h> |
16 | #include <linux/firmware.h> |
17 | #include "amdtee_private.h" |
18 | #include "../tee_private.h" |
19 | #include <linux/psp-tee.h> |
20 | |
21 | static struct amdtee_driver_data *drv_data; |
22 | static DEFINE_MUTEX(session_list_mutex); |
23 | |
24 | static void amdtee_get_version(struct tee_device *teedev, |
25 | struct tee_ioctl_version_data *vers) |
26 | { |
27 | struct tee_ioctl_version_data v = { |
28 | .impl_id = TEE_IMPL_ID_AMDTEE, |
29 | .impl_caps = 0, |
30 | .gen_caps = TEE_GEN_CAP_GP, |
31 | }; |
32 | *vers = v; |
33 | } |
34 | |
35 | static int amdtee_open(struct tee_context *ctx) |
36 | { |
37 | struct amdtee_context_data *ctxdata; |
38 | |
39 | ctxdata = kzalloc(size: sizeof(*ctxdata), GFP_KERNEL); |
40 | if (!ctxdata) |
41 | return -ENOMEM; |
42 | |
43 | INIT_LIST_HEAD(list: &ctxdata->sess_list); |
44 | INIT_LIST_HEAD(list: &ctxdata->shm_list); |
45 | mutex_init(&ctxdata->shm_mutex); |
46 | |
47 | ctx->data = ctxdata; |
48 | return 0; |
49 | } |
50 | |
51 | static void release_session(struct amdtee_session *sess) |
52 | { |
53 | int i; |
54 | |
55 | /* Close any open session */ |
56 | for (i = 0; i < TEE_NUM_SESSIONS; ++i) { |
57 | /* Check if session entry 'i' is valid */ |
58 | if (!test_bit(i, sess->sess_mask)) |
59 | continue; |
60 | |
61 | handle_close_session(ta_handle: sess->ta_handle, info: sess->session_info[i]); |
62 | handle_unload_ta(ta_handle: sess->ta_handle); |
63 | } |
64 | |
65 | kfree(objp: sess); |
66 | } |
67 | |
68 | static void amdtee_release(struct tee_context *ctx) |
69 | { |
70 | struct amdtee_context_data *ctxdata = ctx->data; |
71 | |
72 | if (!ctxdata) |
73 | return; |
74 | |
75 | while (true) { |
76 | struct amdtee_session *sess; |
77 | |
78 | sess = list_first_entry_or_null(&ctxdata->sess_list, |
79 | struct amdtee_session, |
80 | list_node); |
81 | |
82 | if (!sess) |
83 | break; |
84 | |
85 | list_del(entry: &sess->list_node); |
86 | release_session(sess); |
87 | } |
88 | mutex_destroy(lock: &ctxdata->shm_mutex); |
89 | kfree(objp: ctxdata); |
90 | |
91 | ctx->data = NULL; |
92 | } |
93 | |
94 | /** |
95 | * alloc_session() - Allocate a session structure |
96 | * @ctxdata: TEE Context data structure |
97 | * @session: Session ID for which 'struct amdtee_session' structure is to be |
98 | * allocated. |
99 | * |
100 | * Scans the TEE context's session list to check if TA is already loaded in to |
101 | * TEE. If yes, returns the 'session' structure for that TA. Else allocates, |
102 | * initializes a new 'session' structure and adds it to context's session list. |
103 | * |
104 | * The caller must hold a mutex. |
105 | * |
106 | * Returns: |
107 | * 'struct amdtee_session *' on success and NULL on failure. |
108 | */ |
109 | static struct amdtee_session *alloc_session(struct amdtee_context_data *ctxdata, |
110 | u32 session) |
111 | { |
112 | struct amdtee_session *sess; |
113 | u32 ta_handle = get_ta_handle(session); |
114 | |
115 | /* Scan session list to check if TA is already loaded in to TEE */ |
116 | list_for_each_entry(sess, &ctxdata->sess_list, list_node) |
117 | if (sess->ta_handle == ta_handle) { |
118 | kref_get(kref: &sess->refcount); |
119 | return sess; |
120 | } |
121 | |
122 | /* Allocate a new session and add to list */ |
123 | sess = kzalloc(size: sizeof(*sess), GFP_KERNEL); |
124 | if (sess) { |
125 | sess->ta_handle = ta_handle; |
126 | kref_init(kref: &sess->refcount); |
127 | spin_lock_init(&sess->lock); |
128 | list_add(new: &sess->list_node, head: &ctxdata->sess_list); |
129 | } |
130 | |
131 | return sess; |
132 | } |
133 | |
134 | /* Requires mutex to be held */ |
135 | static struct amdtee_session *find_session(struct amdtee_context_data *ctxdata, |
136 | u32 session) |
137 | { |
138 | u32 ta_handle = get_ta_handle(session); |
139 | u32 index = get_session_index(session); |
140 | struct amdtee_session *sess; |
141 | |
142 | if (index >= TEE_NUM_SESSIONS) |
143 | return NULL; |
144 | |
145 | list_for_each_entry(sess, &ctxdata->sess_list, list_node) |
146 | if (ta_handle == sess->ta_handle && |
147 | test_bit(index, sess->sess_mask)) |
148 | return sess; |
149 | |
150 | return NULL; |
151 | } |
152 | |
153 | u32 get_buffer_id(struct tee_shm *shm) |
154 | { |
155 | struct amdtee_context_data *ctxdata = shm->ctx->data; |
156 | struct amdtee_shm_data *shmdata; |
157 | u32 buf_id = 0; |
158 | |
159 | mutex_lock(&ctxdata->shm_mutex); |
160 | list_for_each_entry(shmdata, &ctxdata->shm_list, shm_node) |
161 | if (shmdata->kaddr == shm->kaddr) { |
162 | buf_id = shmdata->buf_id; |
163 | break; |
164 | } |
165 | mutex_unlock(lock: &ctxdata->shm_mutex); |
166 | |
167 | return buf_id; |
168 | } |
169 | |
170 | static DEFINE_MUTEX(drv_mutex); |
171 | static int copy_ta_binary(struct tee_context *ctx, void *ptr, void **ta, |
172 | size_t *ta_size) |
173 | { |
174 | const struct firmware *fw; |
175 | char fw_name[TA_PATH_MAX]; |
176 | struct { |
177 | u32 lo; |
178 | u16 mid; |
179 | u16 hi_ver; |
180 | u8 seq_n[8]; |
181 | } *uuid = ptr; |
182 | int n, rc = 0; |
183 | |
184 | n = snprintf(buf: fw_name, TA_PATH_MAX, |
185 | fmt: "%s/%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x.bin" , |
186 | TA_LOAD_PATH, uuid->lo, uuid->mid, uuid->hi_ver, |
187 | uuid->seq_n[0], uuid->seq_n[1], |
188 | uuid->seq_n[2], uuid->seq_n[3], |
189 | uuid->seq_n[4], uuid->seq_n[5], |
190 | uuid->seq_n[6], uuid->seq_n[7]); |
191 | if (n < 0 || n >= TA_PATH_MAX) { |
192 | pr_err("failed to get firmware name\n" ); |
193 | return -EINVAL; |
194 | } |
195 | |
196 | mutex_lock(&drv_mutex); |
197 | n = request_firmware(fw: &fw, name: fw_name, device: &ctx->teedev->dev); |
198 | if (n) { |
199 | pr_err("failed to load firmware %s\n" , fw_name); |
200 | rc = -ENOMEM; |
201 | goto unlock; |
202 | } |
203 | |
204 | *ta_size = roundup(fw->size, PAGE_SIZE); |
205 | *ta = (void *)__get_free_pages(GFP_KERNEL, order: get_order(size: *ta_size)); |
206 | if (!*ta) { |
207 | pr_err("%s: get_free_pages failed\n" , __func__); |
208 | rc = -ENOMEM; |
209 | goto rel_fw; |
210 | } |
211 | |
212 | memcpy(*ta, fw->data, fw->size); |
213 | rel_fw: |
214 | release_firmware(fw); |
215 | unlock: |
216 | mutex_unlock(lock: &drv_mutex); |
217 | return rc; |
218 | } |
219 | |
220 | /* mutex must be held by caller */ |
221 | static void destroy_session(struct kref *ref) |
222 | { |
223 | struct amdtee_session *sess = container_of(ref, struct amdtee_session, |
224 | refcount); |
225 | |
226 | list_del(entry: &sess->list_node); |
227 | mutex_unlock(lock: &session_list_mutex); |
228 | kfree(objp: sess); |
229 | } |
230 | |
231 | int amdtee_open_session(struct tee_context *ctx, |
232 | struct tee_ioctl_open_session_arg *arg, |
233 | struct tee_param *param) |
234 | { |
235 | struct amdtee_context_data *ctxdata = ctx->data; |
236 | struct amdtee_session *sess = NULL; |
237 | u32 session_info, ta_handle; |
238 | size_t ta_size; |
239 | int rc, i; |
240 | void *ta; |
241 | |
242 | if (arg->clnt_login != TEE_IOCTL_LOGIN_PUBLIC) { |
243 | pr_err("unsupported client login method\n" ); |
244 | return -EINVAL; |
245 | } |
246 | |
247 | rc = copy_ta_binary(ctx, ptr: &arg->uuid[0], ta: &ta, ta_size: &ta_size); |
248 | if (rc) { |
249 | pr_err("failed to copy TA binary\n" ); |
250 | return rc; |
251 | } |
252 | |
253 | /* Load the TA binary into TEE environment */ |
254 | handle_load_ta(data: ta, size: ta_size, arg); |
255 | if (arg->ret != TEEC_SUCCESS) |
256 | goto out; |
257 | |
258 | ta_handle = get_ta_handle(session: arg->session); |
259 | |
260 | mutex_lock(&session_list_mutex); |
261 | sess = alloc_session(ctxdata, session: arg->session); |
262 | mutex_unlock(lock: &session_list_mutex); |
263 | |
264 | if (!sess) { |
265 | handle_unload_ta(ta_handle); |
266 | rc = -ENOMEM; |
267 | goto out; |
268 | } |
269 | |
270 | /* Open session with loaded TA */ |
271 | handle_open_session(arg, info: &session_info, p: param); |
272 | if (arg->ret != TEEC_SUCCESS) { |
273 | pr_err("open_session failed %d\n" , arg->ret); |
274 | handle_unload_ta(ta_handle); |
275 | kref_put_mutex(kref: &sess->refcount, release: destroy_session, |
276 | lock: &session_list_mutex); |
277 | goto out; |
278 | } |
279 | |
280 | /* Find an empty session index for the given TA */ |
281 | spin_lock(lock: &sess->lock); |
282 | i = find_first_zero_bit(addr: sess->sess_mask, TEE_NUM_SESSIONS); |
283 | if (i < TEE_NUM_SESSIONS) { |
284 | sess->session_info[i] = session_info; |
285 | set_session_id(ta_handle, session_index: i, session: &arg->session); |
286 | set_bit(nr: i, addr: sess->sess_mask); |
287 | } |
288 | spin_unlock(lock: &sess->lock); |
289 | |
290 | if (i >= TEE_NUM_SESSIONS) { |
291 | pr_err("reached maximum session count %d\n" , TEE_NUM_SESSIONS); |
292 | handle_close_session(ta_handle, info: session_info); |
293 | handle_unload_ta(ta_handle); |
294 | kref_put_mutex(kref: &sess->refcount, release: destroy_session, |
295 | lock: &session_list_mutex); |
296 | rc = -ENOMEM; |
297 | goto out; |
298 | } |
299 | |
300 | out: |
301 | free_pages(addr: (u64)ta, order: get_order(size: ta_size)); |
302 | return rc; |
303 | } |
304 | |
305 | int amdtee_close_session(struct tee_context *ctx, u32 session) |
306 | { |
307 | struct amdtee_context_data *ctxdata = ctx->data; |
308 | u32 i, ta_handle, session_info; |
309 | struct amdtee_session *sess; |
310 | |
311 | pr_debug("%s: sid = 0x%x\n" , __func__, session); |
312 | |
313 | /* |
314 | * Check that the session is valid and clear the session |
315 | * usage bit |
316 | */ |
317 | mutex_lock(&session_list_mutex); |
318 | sess = find_session(ctxdata, session); |
319 | if (sess) { |
320 | ta_handle = get_ta_handle(session); |
321 | i = get_session_index(session); |
322 | session_info = sess->session_info[i]; |
323 | spin_lock(lock: &sess->lock); |
324 | clear_bit(nr: i, addr: sess->sess_mask); |
325 | spin_unlock(lock: &sess->lock); |
326 | } |
327 | mutex_unlock(lock: &session_list_mutex); |
328 | |
329 | if (!sess) |
330 | return -EINVAL; |
331 | |
332 | /* Close the session */ |
333 | handle_close_session(ta_handle, info: session_info); |
334 | handle_unload_ta(ta_handle); |
335 | |
336 | kref_put_mutex(kref: &sess->refcount, release: destroy_session, lock: &session_list_mutex); |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | int amdtee_map_shmem(struct tee_shm *shm) |
342 | { |
343 | struct amdtee_context_data *ctxdata; |
344 | struct amdtee_shm_data *shmnode; |
345 | struct shmem_desc shmem; |
346 | int rc, count; |
347 | u32 buf_id; |
348 | |
349 | if (!shm) |
350 | return -EINVAL; |
351 | |
352 | shmnode = kmalloc(size: sizeof(*shmnode), GFP_KERNEL); |
353 | if (!shmnode) |
354 | return -ENOMEM; |
355 | |
356 | count = 1; |
357 | shmem.kaddr = shm->kaddr; |
358 | shmem.size = shm->size; |
359 | |
360 | /* |
361 | * Send a MAP command to TEE and get the corresponding |
362 | * buffer Id |
363 | */ |
364 | rc = handle_map_shmem(count, start: &shmem, buf_id: &buf_id); |
365 | if (rc) { |
366 | pr_err("map_shmem failed: ret = %d\n" , rc); |
367 | kfree(objp: shmnode); |
368 | return rc; |
369 | } |
370 | |
371 | shmnode->kaddr = shm->kaddr; |
372 | shmnode->buf_id = buf_id; |
373 | ctxdata = shm->ctx->data; |
374 | mutex_lock(&ctxdata->shm_mutex); |
375 | list_add(new: &shmnode->shm_node, head: &ctxdata->shm_list); |
376 | mutex_unlock(lock: &ctxdata->shm_mutex); |
377 | |
378 | pr_debug("buf_id :[%x] kaddr[%p]\n" , shmnode->buf_id, shmnode->kaddr); |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | void amdtee_unmap_shmem(struct tee_shm *shm) |
384 | { |
385 | struct amdtee_context_data *ctxdata; |
386 | struct amdtee_shm_data *shmnode; |
387 | u32 buf_id; |
388 | |
389 | if (!shm) |
390 | return; |
391 | |
392 | buf_id = get_buffer_id(shm); |
393 | /* Unmap the shared memory from TEE */ |
394 | handle_unmap_shmem(buf_id); |
395 | |
396 | ctxdata = shm->ctx->data; |
397 | mutex_lock(&ctxdata->shm_mutex); |
398 | list_for_each_entry(shmnode, &ctxdata->shm_list, shm_node) |
399 | if (buf_id == shmnode->buf_id) { |
400 | list_del(entry: &shmnode->shm_node); |
401 | kfree(objp: shmnode); |
402 | break; |
403 | } |
404 | mutex_unlock(lock: &ctxdata->shm_mutex); |
405 | } |
406 | |
407 | int amdtee_invoke_func(struct tee_context *ctx, |
408 | struct tee_ioctl_invoke_arg *arg, |
409 | struct tee_param *param) |
410 | { |
411 | struct amdtee_context_data *ctxdata = ctx->data; |
412 | struct amdtee_session *sess; |
413 | u32 i, session_info; |
414 | |
415 | /* Check that the session is valid */ |
416 | mutex_lock(&session_list_mutex); |
417 | sess = find_session(ctxdata, session: arg->session); |
418 | if (sess) { |
419 | i = get_session_index(session: arg->session); |
420 | session_info = sess->session_info[i]; |
421 | } |
422 | mutex_unlock(lock: &session_list_mutex); |
423 | |
424 | if (!sess) |
425 | return -EINVAL; |
426 | |
427 | handle_invoke_cmd(arg, sinfo: session_info, p: param); |
428 | |
429 | return 0; |
430 | } |
431 | |
432 | int amdtee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) |
433 | { |
434 | return -EINVAL; |
435 | } |
436 | |
437 | static const struct tee_driver_ops amdtee_ops = { |
438 | .get_version = amdtee_get_version, |
439 | .open = amdtee_open, |
440 | .release = amdtee_release, |
441 | .open_session = amdtee_open_session, |
442 | .close_session = amdtee_close_session, |
443 | .invoke_func = amdtee_invoke_func, |
444 | .cancel_req = amdtee_cancel_req, |
445 | }; |
446 | |
447 | static const struct tee_desc amdtee_desc = { |
448 | .name = DRIVER_NAME "-clnt" , |
449 | .ops = &amdtee_ops, |
450 | .owner = THIS_MODULE, |
451 | }; |
452 | |
453 | static int __init amdtee_driver_init(void) |
454 | { |
455 | struct tee_device *teedev; |
456 | struct tee_shm_pool *pool; |
457 | struct amdtee *amdtee; |
458 | int rc; |
459 | |
460 | rc = psp_check_tee_status(); |
461 | if (rc) { |
462 | pr_err("amd-tee driver: tee not present\n" ); |
463 | return rc; |
464 | } |
465 | |
466 | drv_data = kzalloc(size: sizeof(*drv_data), GFP_KERNEL); |
467 | if (!drv_data) |
468 | return -ENOMEM; |
469 | |
470 | amdtee = kzalloc(size: sizeof(*amdtee), GFP_KERNEL); |
471 | if (!amdtee) { |
472 | rc = -ENOMEM; |
473 | goto err_kfree_drv_data; |
474 | } |
475 | |
476 | pool = amdtee_config_shm(); |
477 | if (IS_ERR(ptr: pool)) { |
478 | pr_err("shared pool configuration error\n" ); |
479 | rc = PTR_ERR(ptr: pool); |
480 | goto err_kfree_amdtee; |
481 | } |
482 | |
483 | teedev = tee_device_alloc(teedesc: &amdtee_desc, NULL, pool, driver_data: amdtee); |
484 | if (IS_ERR(ptr: teedev)) { |
485 | rc = PTR_ERR(ptr: teedev); |
486 | goto err_free_pool; |
487 | } |
488 | amdtee->teedev = teedev; |
489 | |
490 | rc = tee_device_register(teedev: amdtee->teedev); |
491 | if (rc) |
492 | goto err_device_unregister; |
493 | |
494 | amdtee->pool = pool; |
495 | |
496 | drv_data->amdtee = amdtee; |
497 | |
498 | pr_info("amd-tee driver initialization successful\n" ); |
499 | return 0; |
500 | |
501 | err_device_unregister: |
502 | tee_device_unregister(teedev: amdtee->teedev); |
503 | |
504 | err_free_pool: |
505 | tee_shm_pool_free(pool); |
506 | |
507 | err_kfree_amdtee: |
508 | kfree(objp: amdtee); |
509 | |
510 | err_kfree_drv_data: |
511 | kfree(objp: drv_data); |
512 | drv_data = NULL; |
513 | |
514 | pr_err("amd-tee driver initialization failed\n" ); |
515 | return rc; |
516 | } |
517 | module_init(amdtee_driver_init); |
518 | |
519 | static void __exit amdtee_driver_exit(void) |
520 | { |
521 | struct amdtee *amdtee; |
522 | |
523 | if (!drv_data || !drv_data->amdtee) |
524 | return; |
525 | |
526 | amdtee = drv_data->amdtee; |
527 | |
528 | tee_device_unregister(teedev: amdtee->teedev); |
529 | tee_shm_pool_free(pool: amdtee->pool); |
530 | } |
531 | module_exit(amdtee_driver_exit); |
532 | |
533 | MODULE_AUTHOR(DRIVER_AUTHOR); |
534 | MODULE_DESCRIPTION("AMD-TEE driver" ); |
535 | MODULE_VERSION("1.0" ); |
536 | MODULE_LICENSE("Dual MIT/GPL" ); |
537 | |