Commit 9f015b37 authored by Rijo Thomas's avatar Rijo Thomas Committed by Jens Wiklander

tee: amdtee: unload TA only when its refcount becomes 0

Same Trusted Application (TA) can be loaded in multiple TEE contexts.

If it is a single instance TA, the TA should not get unloaded from AMD
Secure Processor, while it is still in use in another TEE context.

Therefore reference count TA and unload it when the count becomes zero.

Fixes: 757cc3e9 ("tee: add AMD-TEE driver")
Reviewed-by: default avatarDevaraj Rangasamy <Devaraj.Rangasamy@amd.com>
Signed-off-by: default avatarRijo Thomas <Rijo-john.Thomas@amd.com>
Acked-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: default avatarJens Wiklander <jens.wiklander@linaro.org>
parent 9f4ad9e4
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define TEEC_SUCCESS 0x00000000 #define TEEC_SUCCESS 0x00000000
#define TEEC_ERROR_GENERIC 0xFFFF0000 #define TEEC_ERROR_GENERIC 0xFFFF0000
#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_COMMUNICATION 0xFFFF000E
#define TEEC_ORIGIN_COMMS 0x00000002 #define TEEC_ORIGIN_COMMS 0x00000002
...@@ -93,6 +94,18 @@ struct amdtee_shm_data { ...@@ -93,6 +94,18 @@ struct amdtee_shm_data {
u32 buf_id; u32 buf_id;
}; };
/**
* struct amdtee_ta_data - Keeps track of all TAs loaded in AMD Secure
* Processor
* @ta_handle: Handle to TA loaded in TEE
* @refcount: Reference count for the loaded TA
*/
struct amdtee_ta_data {
struct list_head list_node;
u32 ta_handle;
u32 refcount;
};
#define LOWER_TWO_BYTE_MASK 0x0000FFFF #define LOWER_TWO_BYTE_MASK 0x0000FFFF
/** /**
......
...@@ -121,15 +121,69 @@ static int amd_params_to_tee_params(struct tee_param *tee, u32 count, ...@@ -121,15 +121,69 @@ static int amd_params_to_tee_params(struct tee_param *tee, u32 count,
return ret; return ret;
} }
static DEFINE_MUTEX(ta_refcount_mutex);
static struct list_head ta_list = LIST_HEAD_INIT(ta_list);
static u32 get_ta_refcount(u32 ta_handle)
{
struct amdtee_ta_data *ta_data;
u32 count = 0;
/* Caller must hold a mutex */
list_for_each_entry(ta_data, &ta_list, list_node)
if (ta_data->ta_handle == ta_handle)
return ++ta_data->refcount;
ta_data = kzalloc(sizeof(*ta_data), GFP_KERNEL);
if (ta_data) {
ta_data->ta_handle = ta_handle;
ta_data->refcount = 1;
count = ta_data->refcount;
list_add(&ta_data->list_node, &ta_list);
}
return count;
}
static u32 put_ta_refcount(u32 ta_handle)
{
struct amdtee_ta_data *ta_data;
u32 count = 0;
/* Caller must hold a mutex */
list_for_each_entry(ta_data, &ta_list, list_node)
if (ta_data->ta_handle == ta_handle) {
count = --ta_data->refcount;
if (count == 0) {
list_del(&ta_data->list_node);
kfree(ta_data);
break;
}
}
return count;
}
int handle_unload_ta(u32 ta_handle) int handle_unload_ta(u32 ta_handle)
{ {
struct tee_cmd_unload_ta cmd = {0}; struct tee_cmd_unload_ta cmd = {0};
u32 status; u32 status, count;
int ret; int ret;
if (!ta_handle) if (!ta_handle)
return -EINVAL; return -EINVAL;
mutex_lock(&ta_refcount_mutex);
count = put_ta_refcount(ta_handle);
if (count) {
pr_debug("unload ta: not unloading %u count %u\n",
ta_handle, count);
ret = -EBUSY;
goto unlock;
}
cmd.ta_handle = ta_handle; cmd.ta_handle = ta_handle;
ret = psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, (void *)&cmd, ret = psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, (void *)&cmd,
...@@ -137,8 +191,12 @@ int handle_unload_ta(u32 ta_handle) ...@@ -137,8 +191,12 @@ int handle_unload_ta(u32 ta_handle)
if (!ret && status != 0) { if (!ret && status != 0) {
pr_err("unload ta: status = 0x%x\n", status); pr_err("unload ta: status = 0x%x\n", status);
ret = -EBUSY; ret = -EBUSY;
} else {
pr_debug("unloaded ta handle %u\n", ta_handle);
} }
unlock:
mutex_unlock(&ta_refcount_mutex);
return ret; return ret;
} }
...@@ -340,7 +398,8 @@ int handle_open_session(struct tee_ioctl_open_session_arg *arg, u32 *info, ...@@ -340,7 +398,8 @@ int handle_open_session(struct tee_ioctl_open_session_arg *arg, u32 *info,
int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg) int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg)
{ {
struct tee_cmd_load_ta cmd = {0}; struct tee_cmd_unload_ta unload_cmd = {};
struct tee_cmd_load_ta load_cmd = {};
phys_addr_t blob; phys_addr_t blob;
int ret; int ret;
...@@ -353,21 +412,36 @@ int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg) ...@@ -353,21 +412,36 @@ int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg)
return -EINVAL; return -EINVAL;
} }
cmd.hi_addr = upper_32_bits(blob); load_cmd.hi_addr = upper_32_bits(blob);
cmd.low_addr = lower_32_bits(blob); load_cmd.low_addr = lower_32_bits(blob);
cmd.size = size; load_cmd.size = size;
ret = psp_tee_process_cmd(TEE_CMD_ID_LOAD_TA, (void *)&cmd, mutex_lock(&ta_refcount_mutex);
sizeof(cmd), &arg->ret);
ret = psp_tee_process_cmd(TEE_CMD_ID_LOAD_TA, (void *)&load_cmd,
sizeof(load_cmd), &arg->ret);
if (ret) { if (ret) {
arg->ret_origin = TEEC_ORIGIN_COMMS; arg->ret_origin = TEEC_ORIGIN_COMMS;
arg->ret = TEEC_ERROR_COMMUNICATION; arg->ret = TEEC_ERROR_COMMUNICATION;
} else { } else if (arg->ret == TEEC_SUCCESS) {
set_session_id(cmd.ta_handle, 0, &arg->session); ret = get_ta_refcount(load_cmd.ta_handle);
if (!ret) {
arg->ret_origin = TEEC_ORIGIN_COMMS;
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
/* Unload the TA on error */
unload_cmd.ta_handle = load_cmd.ta_handle;
psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA,
(void *)&unload_cmd,
sizeof(unload_cmd), &ret);
} else {
set_session_id(load_cmd.ta_handle, 0, &arg->session);
}
} }
mutex_unlock(&ta_refcount_mutex);
pr_debug("load TA: TA handle = 0x%x, RO = 0x%x, ret = 0x%x\n", pr_debug("load TA: TA handle = 0x%x, RO = 0x%x, ret = 0x%x\n",
cmd.ta_handle, arg->ret_origin, arg->ret); load_cmd.ta_handle, arg->ret_origin, arg->ret);
return 0; return 0;
} }
...@@ -59,10 +59,9 @@ static void release_session(struct amdtee_session *sess) ...@@ -59,10 +59,9 @@ static void release_session(struct amdtee_session *sess)
continue; continue;
handle_close_session(sess->ta_handle, sess->session_info[i]); handle_close_session(sess->ta_handle, sess->session_info[i]);
handle_unload_ta(sess->ta_handle);
} }
/* Unload Trusted Application once all sessions are closed */
handle_unload_ta(sess->ta_handle);
kfree(sess); kfree(sess);
} }
...@@ -224,8 +223,6 @@ static void destroy_session(struct kref *ref) ...@@ -224,8 +223,6 @@ static void destroy_session(struct kref *ref)
struct amdtee_session *sess = container_of(ref, struct amdtee_session, struct amdtee_session *sess = container_of(ref, struct amdtee_session,
refcount); refcount);
/* Unload the TA from TEE */
handle_unload_ta(sess->ta_handle);
mutex_lock(&session_list_mutex); mutex_lock(&session_list_mutex);
list_del(&sess->list_node); list_del(&sess->list_node);
mutex_unlock(&session_list_mutex); mutex_unlock(&session_list_mutex);
...@@ -238,7 +235,7 @@ int amdtee_open_session(struct tee_context *ctx, ...@@ -238,7 +235,7 @@ int amdtee_open_session(struct tee_context *ctx,
{ {
struct amdtee_context_data *ctxdata = ctx->data; struct amdtee_context_data *ctxdata = ctx->data;
struct amdtee_session *sess = NULL; struct amdtee_session *sess = NULL;
u32 session_info; u32 session_info, ta_handle;
size_t ta_size; size_t ta_size;
int rc, i; int rc, i;
void *ta; void *ta;
...@@ -259,11 +256,14 @@ int amdtee_open_session(struct tee_context *ctx, ...@@ -259,11 +256,14 @@ int amdtee_open_session(struct tee_context *ctx,
if (arg->ret != TEEC_SUCCESS) if (arg->ret != TEEC_SUCCESS)
goto out; goto out;
ta_handle = get_ta_handle(arg->session);
mutex_lock(&session_list_mutex); mutex_lock(&session_list_mutex);
sess = alloc_session(ctxdata, arg->session); sess = alloc_session(ctxdata, arg->session);
mutex_unlock(&session_list_mutex); mutex_unlock(&session_list_mutex);
if (!sess) { if (!sess) {
handle_unload_ta(ta_handle);
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
} }
...@@ -277,6 +277,7 @@ int amdtee_open_session(struct tee_context *ctx, ...@@ -277,6 +277,7 @@ int amdtee_open_session(struct tee_context *ctx,
if (i >= TEE_NUM_SESSIONS) { if (i >= TEE_NUM_SESSIONS) {
pr_err("reached maximum session count %d\n", TEE_NUM_SESSIONS); pr_err("reached maximum session count %d\n", TEE_NUM_SESSIONS);
handle_unload_ta(ta_handle);
kref_put(&sess->refcount, destroy_session); kref_put(&sess->refcount, destroy_session);
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
...@@ -289,12 +290,13 @@ int amdtee_open_session(struct tee_context *ctx, ...@@ -289,12 +290,13 @@ int amdtee_open_session(struct tee_context *ctx,
spin_lock(&sess->lock); spin_lock(&sess->lock);
clear_bit(i, sess->sess_mask); clear_bit(i, sess->sess_mask);
spin_unlock(&sess->lock); spin_unlock(&sess->lock);
handle_unload_ta(ta_handle);
kref_put(&sess->refcount, destroy_session); kref_put(&sess->refcount, destroy_session);
goto out; goto out;
} }
sess->session_info[i] = session_info; sess->session_info[i] = session_info;
set_session_id(sess->ta_handle, i, &arg->session); set_session_id(ta_handle, i, &arg->session);
out: out:
free_pages((u64)ta, get_order(ta_size)); free_pages((u64)ta, get_order(ta_size));
return rc; return rc;
...@@ -329,6 +331,7 @@ int amdtee_close_session(struct tee_context *ctx, u32 session) ...@@ -329,6 +331,7 @@ int amdtee_close_session(struct tee_context *ctx, u32 session)
/* Close the session */ /* Close the session */
handle_close_session(ta_handle, session_info); handle_close_session(ta_handle, session_info);
handle_unload_ta(ta_handle);
kref_put(&sess->refcount, destroy_session); kref_put(&sess->refcount, destroy_session);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment