| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- // SPDX-License-Identifier: MIT
- /*
- * Copyright 2019 Advanced Micro Devices, Inc.
- */
- #include <linux/errno.h>
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include <linux/device.h>
- #include <linux/tee_core.h>
- #include <linux/types.h>
- #include <linux/mm.h>
- #include <linux/uaccess.h>
- #include <linux/firmware.h>
- #include "amdtee_private.h"
- #include <linux/psp-tee.h>
- static struct amdtee_driver_data *drv_data;
- static DEFINE_MUTEX(session_list_mutex);
- static void amdtee_get_version(struct tee_device *teedev,
- struct tee_ioctl_version_data *vers)
- {
- struct tee_ioctl_version_data v = {
- .impl_id = TEE_IMPL_ID_AMDTEE,
- .impl_caps = 0,
- .gen_caps = TEE_GEN_CAP_GP,
- };
- *vers = v;
- }
- static int amdtee_open(struct tee_context *ctx)
- {
- struct amdtee_context_data *ctxdata;
- ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
- if (!ctxdata)
- return -ENOMEM;
- INIT_LIST_HEAD(&ctxdata->sess_list);
- INIT_LIST_HEAD(&ctxdata->shm_list);
- mutex_init(&ctxdata->shm_mutex);
- ctx->data = ctxdata;
- return 0;
- }
- static void release_session(struct amdtee_session *sess)
- {
- int i;
- /* Close any open session */
- for (i = 0; i < TEE_NUM_SESSIONS; ++i) {
- /* Check if session entry 'i' is valid */
- if (!test_bit(i, sess->sess_mask))
- continue;
- handle_close_session(sess->ta_handle, sess->session_info[i]);
- handle_unload_ta(sess->ta_handle);
- }
- kfree(sess);
- }
- static void amdtee_release(struct tee_context *ctx)
- {
- struct amdtee_context_data *ctxdata = ctx->data;
- if (!ctxdata)
- return;
- while (true) {
- struct amdtee_session *sess;
- sess = list_first_entry_or_null(&ctxdata->sess_list,
- struct amdtee_session,
- list_node);
- if (!sess)
- break;
- list_del(&sess->list_node);
- release_session(sess);
- }
- mutex_destroy(&ctxdata->shm_mutex);
- kfree(ctxdata);
- ctx->data = NULL;
- }
- /**
- * alloc_session() - Allocate a session structure
- * @ctxdata: TEE Context data structure
- * @session: Session ID for which 'struct amdtee_session' structure is to be
- * allocated.
- *
- * Scans the TEE context's session list to check if TA is already loaded in to
- * TEE. If yes, returns the 'session' structure for that TA. Else allocates,
- * initializes a new 'session' structure and adds it to context's session list.
- *
- * The caller must hold a mutex.
- *
- * Returns:
- * 'struct amdtee_session *' on success and NULL on failure.
- */
- static struct amdtee_session *alloc_session(struct amdtee_context_data *ctxdata,
- u32 session)
- {
- struct amdtee_session *sess;
- u32 ta_handle = get_ta_handle(session);
- /* Scan session list to check if TA is already loaded in to TEE */
- list_for_each_entry(sess, &ctxdata->sess_list, list_node)
- if (sess->ta_handle == ta_handle) {
- kref_get(&sess->refcount);
- return sess;
- }
- /* Allocate a new session and add to list */
- sess = kzalloc(sizeof(*sess), GFP_KERNEL);
- if (sess) {
- sess->ta_handle = ta_handle;
- kref_init(&sess->refcount);
- spin_lock_init(&sess->lock);
- list_add(&sess->list_node, &ctxdata->sess_list);
- }
- return sess;
- }
- /* Requires mutex to be held */
- static struct amdtee_session *find_session(struct amdtee_context_data *ctxdata,
- u32 session)
- {
- u32 ta_handle = get_ta_handle(session);
- u32 index = get_session_index(session);
- struct amdtee_session *sess;
- if (index >= TEE_NUM_SESSIONS)
- return NULL;
- list_for_each_entry(sess, &ctxdata->sess_list, list_node)
- if (ta_handle == sess->ta_handle &&
- test_bit(index, sess->sess_mask))
- return sess;
- return NULL;
- }
- u32 get_buffer_id(struct tee_shm *shm)
- {
- struct amdtee_context_data *ctxdata = shm->ctx->data;
- struct amdtee_shm_data *shmdata;
- u32 buf_id = 0;
- mutex_lock(&ctxdata->shm_mutex);
- list_for_each_entry(shmdata, &ctxdata->shm_list, shm_node)
- if (shmdata->kaddr == shm->kaddr) {
- buf_id = shmdata->buf_id;
- break;
- }
- mutex_unlock(&ctxdata->shm_mutex);
- return buf_id;
- }
- static DEFINE_MUTEX(drv_mutex);
- static int copy_ta_binary(struct tee_context *ctx, void *ptr, void **ta,
- size_t *ta_size)
- {
- const struct firmware *fw;
- char fw_name[TA_PATH_MAX];
- struct {
- u32 lo;
- u16 mid;
- u16 hi_ver;
- u8 seq_n[8];
- } *uuid = ptr;
- int n, rc = 0;
- n = snprintf(fw_name, TA_PATH_MAX,
- "%s/%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x.bin",
- TA_LOAD_PATH, uuid->lo, uuid->mid, uuid->hi_ver,
- uuid->seq_n[0], uuid->seq_n[1],
- uuid->seq_n[2], uuid->seq_n[3],
- uuid->seq_n[4], uuid->seq_n[5],
- uuid->seq_n[6], uuid->seq_n[7]);
- if (n < 0 || n >= TA_PATH_MAX) {
- pr_err("failed to get firmware name\n");
- return -EINVAL;
- }
- mutex_lock(&drv_mutex);
- n = request_firmware(&fw, fw_name, &ctx->teedev->dev);
- if (n) {
- pr_err("failed to load firmware %s\n", fw_name);
- rc = -ENOMEM;
- goto unlock;
- }
- *ta_size = roundup(fw->size, PAGE_SIZE);
- *ta = (void *)__get_free_pages(GFP_KERNEL, get_order(*ta_size));
- if (!*ta) {
- pr_err("%s: get_free_pages failed\n", __func__);
- rc = -ENOMEM;
- goto rel_fw;
- }
- memcpy(*ta, fw->data, fw->size);
- rel_fw:
- release_firmware(fw);
- unlock:
- mutex_unlock(&drv_mutex);
- return rc;
- }
- /* mutex must be held by caller */
- static void destroy_session(struct kref *ref)
- {
- struct amdtee_session *sess = container_of(ref, struct amdtee_session,
- refcount);
- list_del(&sess->list_node);
- mutex_unlock(&session_list_mutex);
- kfree(sess);
- }
- int amdtee_open_session(struct tee_context *ctx,
- struct tee_ioctl_open_session_arg *arg,
- struct tee_param *param)
- {
- struct amdtee_context_data *ctxdata = ctx->data;
- struct amdtee_session *sess = NULL;
- u32 session_info, ta_handle;
- size_t ta_size;
- int rc, i;
- void *ta;
- if (arg->clnt_login != TEE_IOCTL_LOGIN_PUBLIC) {
- pr_err("unsupported client login method\n");
- return -EINVAL;
- }
- rc = copy_ta_binary(ctx, &arg->uuid[0], &ta, &ta_size);
- if (rc) {
- pr_err("failed to copy TA binary\n");
- return rc;
- }
- /* Load the TA binary into TEE environment */
- handle_load_ta(ta, ta_size, arg);
- if (arg->ret != TEEC_SUCCESS)
- goto out;
- ta_handle = get_ta_handle(arg->session);
- mutex_lock(&session_list_mutex);
- sess = alloc_session(ctxdata, arg->session);
- mutex_unlock(&session_list_mutex);
- if (!sess) {
- handle_unload_ta(ta_handle);
- rc = -ENOMEM;
- goto out;
- }
- /* Open session with loaded TA */
- handle_open_session(arg, &session_info, param);
- if (arg->ret != TEEC_SUCCESS) {
- pr_err("open_session failed %d\n", arg->ret);
- handle_unload_ta(ta_handle);
- kref_put_mutex(&sess->refcount, destroy_session,
- &session_list_mutex);
- goto out;
- }
- /* Find an empty session index for the given TA */
- spin_lock(&sess->lock);
- i = find_first_zero_bit(sess->sess_mask, TEE_NUM_SESSIONS);
- if (i < TEE_NUM_SESSIONS) {
- sess->session_info[i] = session_info;
- set_session_id(ta_handle, i, &arg->session);
- set_bit(i, sess->sess_mask);
- }
- spin_unlock(&sess->lock);
- if (i >= TEE_NUM_SESSIONS) {
- pr_err("reached maximum session count %d\n", TEE_NUM_SESSIONS);
- handle_close_session(ta_handle, session_info);
- handle_unload_ta(ta_handle);
- kref_put_mutex(&sess->refcount, destroy_session,
- &session_list_mutex);
- rc = -ENOMEM;
- goto out;
- }
- out:
- free_pages((u64)ta, get_order(ta_size));
- return rc;
- }
- int amdtee_close_session(struct tee_context *ctx, u32 session)
- {
- struct amdtee_context_data *ctxdata = ctx->data;
- u32 i, ta_handle, session_info;
- struct amdtee_session *sess;
- pr_debug("%s: sid = 0x%x\n", __func__, session);
- /*
- * Check that the session is valid and clear the session
- * usage bit
- */
- mutex_lock(&session_list_mutex);
- sess = find_session(ctxdata, session);
- if (sess) {
- ta_handle = get_ta_handle(session);
- i = get_session_index(session);
- session_info = sess->session_info[i];
- spin_lock(&sess->lock);
- clear_bit(i, sess->sess_mask);
- spin_unlock(&sess->lock);
- }
- mutex_unlock(&session_list_mutex);
- if (!sess)
- return -EINVAL;
- /* Close the session */
- handle_close_session(ta_handle, session_info);
- handle_unload_ta(ta_handle);
- kref_put_mutex(&sess->refcount, destroy_session, &session_list_mutex);
- return 0;
- }
- int amdtee_map_shmem(struct tee_shm *shm)
- {
- struct amdtee_context_data *ctxdata;
- struct amdtee_shm_data *shmnode;
- struct shmem_desc shmem;
- int rc, count;
- u32 buf_id;
- if (!shm)
- return -EINVAL;
- shmnode = kmalloc(sizeof(*shmnode), GFP_KERNEL);
- if (!shmnode)
- return -ENOMEM;
- count = 1;
- shmem.kaddr = shm->kaddr;
- shmem.size = shm->size;
- /*
- * Send a MAP command to TEE and get the corresponding
- * buffer Id
- */
- rc = handle_map_shmem(count, &shmem, &buf_id);
- if (rc) {
- pr_err("map_shmem failed: ret = %d\n", rc);
- kfree(shmnode);
- return rc;
- }
- shmnode->kaddr = shm->kaddr;
- shmnode->buf_id = buf_id;
- ctxdata = shm->ctx->data;
- mutex_lock(&ctxdata->shm_mutex);
- list_add(&shmnode->shm_node, &ctxdata->shm_list);
- mutex_unlock(&ctxdata->shm_mutex);
- pr_debug("buf_id :[%x] kaddr[%p]\n", shmnode->buf_id, shmnode->kaddr);
- return 0;
- }
- void amdtee_unmap_shmem(struct tee_shm *shm)
- {
- struct amdtee_context_data *ctxdata;
- struct amdtee_shm_data *shmnode;
- u32 buf_id;
- if (!shm)
- return;
- buf_id = get_buffer_id(shm);
- /* Unmap the shared memory from TEE */
- handle_unmap_shmem(buf_id);
- ctxdata = shm->ctx->data;
- mutex_lock(&ctxdata->shm_mutex);
- list_for_each_entry(shmnode, &ctxdata->shm_list, shm_node)
- if (buf_id == shmnode->buf_id) {
- list_del(&shmnode->shm_node);
- kfree(shmnode);
- break;
- }
- mutex_unlock(&ctxdata->shm_mutex);
- }
- int amdtee_invoke_func(struct tee_context *ctx,
- struct tee_ioctl_invoke_arg *arg,
- struct tee_param *param)
- {
- struct amdtee_context_data *ctxdata = ctx->data;
- struct amdtee_session *sess;
- u32 i, session_info;
- /* Check that the session is valid */
- mutex_lock(&session_list_mutex);
- sess = find_session(ctxdata, arg->session);
- if (sess) {
- i = get_session_index(arg->session);
- session_info = sess->session_info[i];
- }
- mutex_unlock(&session_list_mutex);
- if (!sess)
- return -EINVAL;
- handle_invoke_cmd(arg, session_info, param);
- return 0;
- }
- int amdtee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session)
- {
- return -EINVAL;
- }
- static const struct tee_driver_ops amdtee_ops = {
- .get_version = amdtee_get_version,
- .open = amdtee_open,
- .release = amdtee_release,
- .open_session = amdtee_open_session,
- .close_session = amdtee_close_session,
- .invoke_func = amdtee_invoke_func,
- .cancel_req = amdtee_cancel_req,
- };
- static const struct tee_desc amdtee_desc = {
- .name = DRIVER_NAME "-clnt",
- .ops = &amdtee_ops,
- .owner = THIS_MODULE,
- };
- static int __init amdtee_driver_init(void)
- {
- struct tee_device *teedev;
- struct tee_shm_pool *pool;
- struct amdtee *amdtee;
- int rc;
- rc = psp_check_tee_status();
- if (rc) {
- pr_err("amd-tee driver: tee not present\n");
- return rc;
- }
- drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
- if (!drv_data)
- return -ENOMEM;
- amdtee = kzalloc(sizeof(*amdtee), GFP_KERNEL);
- if (!amdtee) {
- rc = -ENOMEM;
- goto err_kfree_drv_data;
- }
- pool = amdtee_config_shm();
- if (IS_ERR(pool)) {
- pr_err("shared pool configuration error\n");
- rc = PTR_ERR(pool);
- goto err_kfree_amdtee;
- }
- teedev = tee_device_alloc(&amdtee_desc, NULL, pool, amdtee);
- if (IS_ERR(teedev)) {
- rc = PTR_ERR(teedev);
- goto err_free_pool;
- }
- amdtee->teedev = teedev;
- rc = tee_device_register(amdtee->teedev);
- if (rc)
- goto err_device_unregister;
- amdtee->pool = pool;
- drv_data->amdtee = amdtee;
- pr_info("amd-tee driver initialization successful\n");
- return 0;
- err_device_unregister:
- tee_device_unregister(amdtee->teedev);
- err_free_pool:
- tee_shm_pool_free(pool);
- err_kfree_amdtee:
- kfree(amdtee);
- err_kfree_drv_data:
- kfree(drv_data);
- drv_data = NULL;
- pr_err("amd-tee driver initialization failed\n");
- return rc;
- }
- module_init(amdtee_driver_init);
- static void __exit amdtee_driver_exit(void)
- {
- struct amdtee *amdtee;
- if (!drv_data || !drv_data->amdtee)
- return;
- amdtee = drv_data->amdtee;
- tee_device_unregister(amdtee->teedev);
- tee_shm_pool_free(amdtee->pool);
- }
- module_exit(amdtee_driver_exit);
- MODULE_AUTHOR(DRIVER_AUTHOR);
- MODULE_DESCRIPTION("AMD-TEE driver");
- MODULE_VERSION("1.0");
- MODULE_LICENSE("Dual MIT/GPL");
|