Merge "securemsm-kernel: Post process shmbridge delete and object release"

This commit is contained in:
qctecmdr
2022-07-06 17:51:50 -07:00
gecommit door Gerrit - the friendly Code Review server
bovenliggende cb8f5bd80b e2231d0fe2
commit a3079924a2

Bestand weergeven

@@ -149,6 +149,17 @@ static DEFINE_MUTEX(g_smcinvoke_lock);
#define TAKE_LOCK 1
#define MUTEX_LOCK(x) { if (x) mutex_lock(&g_smcinvoke_lock); }
#define MUTEX_UNLOCK(x) { if (x) mutex_unlock(&g_smcinvoke_lock); }
#define POST_KT_SLEEP 0
#define POST_KT_WAKEUP 1
#define MAX_CHAR_NAME 50
enum worker_thread_type {
SHMB_WORKER_THREAD = 0,
OBJECT_WORKER_THREAD,
MAX_THREAD_NUMBER
};
static DEFINE_HASHTABLE(g_cb_servers, 8);
static LIST_HEAD(g_mem_objs);
static uint16_t g_last_cb_server_id = CBOBJ_SERVER_ID_START;
@@ -268,6 +279,55 @@ struct smcinvoke_mem_obj {
uint64_t shmbridge_handle;
};
static LIST_HEAD(g_bridge_postprocess);
DEFINE_MUTEX(bridge_postprocess_lock);
static LIST_HEAD(g_object_postprocess);
DEFINE_MUTEX(object_postprocess_lock);
struct bridge_deregister {
uint64_t shmbridge_handle;
struct dma_buf *dmabuf_to_free;
};
struct object_release {
uint32_t tzhandle;
uint32_t context_type;
};
struct smcinvoke_shmbridge_deregister_pending_list {
struct list_head list;
struct bridge_deregister data;
};
struct smcinvoke_object_release_pending_list {
struct list_head list;
struct object_release data;
};
struct smcinvoke_worker_thread {
enum worker_thread_type type;
atomic_t postprocess_kthread_state;
wait_queue_head_t postprocess_kthread_wq;
struct task_struct *postprocess_kthread_task;
};
struct smcinvoke_worker_thread smcinvoke[MAX_THREAD_NUMBER];
const char thread_name[MAX_THREAD_NUMBER][MAX_CHAR_NAME] = {
"smcinvoke_shmbridge_postprocess", "smcinvoke_object_postprocess"};
static int prepare_send_scm_msg(const uint8_t *in_buf, phys_addr_t in_paddr,
size_t in_buf_len,
uint8_t *out_buf, phys_addr_t out_paddr,
size_t out_buf_len,
struct smcinvoke_cmd_req *req,
union smcinvoke_arg *args_buf,
bool *tz_acked, uint32_t context_type,
struct qtee_shm *in_shm, struct qtee_shm *out_shm);
static void process_piggyback_data(void *buf, size_t buf_size);
static void destroy_cb_server(struct kref *kref)
{
struct smcinvoke_server_info *server = container_of(kref,
@@ -370,27 +430,236 @@ static uint32_t next_mem_map_obj_id_locked(void)
return g_last_mem_map_obj_id;
}
static void smcinvoke_shmbridge_post_process(void)
{
struct smcinvoke_shmbridge_deregister_pending_list *entry = NULL;
struct list_head *pos;
int ret = 0;
uint64_t handle = 0;
struct dma_buf *dmabuf_to_free = NULL;
do {
mutex_lock(&bridge_postprocess_lock);
if (list_empty(&g_bridge_postprocess)) {
mutex_unlock(&bridge_postprocess_lock);
break;
}
pos = g_bridge_postprocess.next;
entry = list_entry(pos,
struct smcinvoke_shmbridge_deregister_pending_list,
list);
if (entry) {
handle = entry->data.shmbridge_handle;
dmabuf_to_free = entry->data.dmabuf_to_free;
} else {
pr_err("entry is NULL, pos:%#llx\n", (uint64_t)pos);
}
list_del(pos);
kfree_sensitive(entry);
mutex_unlock(&bridge_postprocess_lock);
if (entry) {
do {
ret = qtee_shmbridge_deregister(handle);
if (unlikely(ret)) {
pr_err("SHM failed: ret:%d ptr:0x%x h:%#llx\n",
ret,
dmabuf_to_free,
handle);
} else {
pr_debug("SHM deletion: Handle:%#llx\n",
handle);
dma_buf_put(dmabuf_to_free);
}
} while (-EBUSY == ret);
}
} while (1);
}
static int smcinvoke_object_post_process(void)
{
struct smcinvoke_object_release_pending_list *entry = NULL;
struct list_head *pos;
int ret = 0;
bool release_handles;
uint32_t context_type;
uint8_t *in_buf = NULL;
uint8_t *out_buf = NULL;
struct smcinvoke_cmd_req req = {0};
struct smcinvoke_msg_hdr hdr = {0};
struct qtee_shm in_shm = {0}, out_shm = {0};
ret = qtee_shmbridge_allocate_shm(SMCINVOKE_TZ_MIN_BUF_SIZE, &in_shm);
if (ret) {
ret = -ENOMEM;
pr_err("shmbridge alloc failed for in msg in object release\n");
goto out;
}
ret = qtee_shmbridge_allocate_shm(SMCINVOKE_TZ_MIN_BUF_SIZE, &out_shm);
if (ret) {
ret = -ENOMEM;
pr_err("shmbridge alloc failed for out msg in object release\n");
goto out;
}
do {
mutex_lock(&object_postprocess_lock);
if (list_empty(&g_object_postprocess)) {
mutex_unlock(&object_postprocess_lock);
break;
}
pos = g_object_postprocess.next;
entry = list_entry(pos, struct smcinvoke_object_release_pending_list, list);
if (entry) {
in_buf = in_shm.vaddr;
out_buf = out_shm.vaddr;
hdr.tzhandle = entry->data.tzhandle;
hdr.op = OBJECT_OP_RELEASE;
hdr.counts = 0;
*(struct smcinvoke_msg_hdr *)in_buf = hdr;
context_type = entry->data.context_type;
} else {
pr_err("entry is NULL, pos:%#llx\n", (uint64_t)pos);
}
list_del(pos);
kfree_sensitive(entry);
mutex_unlock(&object_postprocess_lock);
if (entry) {
do {
ret = prepare_send_scm_msg(in_buf, in_shm.paddr,
SMCINVOKE_TZ_MIN_BUF_SIZE, out_buf, out_shm.paddr,
SMCINVOKE_TZ_MIN_BUF_SIZE, &req, NULL,
&release_handles, context_type, &in_shm, &out_shm);
process_piggyback_data(out_buf, SMCINVOKE_TZ_MIN_BUF_SIZE);
if (ret) {
pr_err("Failed to release object(0x%x), ret:%d\n",
hdr.tzhandle, ret);
} else {
pr_debug("Released object(0x%x) successfully.\n",
hdr.tzhandle);
}
} while (-EBUSY == ret);
}
} while (1);
out:
qtee_shmbridge_free_shm(&in_shm);
qtee_shmbridge_free_shm(&out_shm);
return ret;
}
static void __wakeup_postprocess_kthread(struct smcinvoke_worker_thread *smcinvoke)
{
if (smcinvoke) {
atomic_set(&smcinvoke->postprocess_kthread_state,
POST_KT_WAKEUP);
wake_up_interruptible(&smcinvoke->postprocess_kthread_wq);
} else {
pr_err("Invalid smcinvoke pointer.\n");
}
}
static int smcinvoke_postprocess_kthread_func(void *data)
{
struct smcinvoke_worker_thread *smcinvoke_wrk_trd = data;
const char *tag;
if (!smcinvoke_wrk_trd) {
pr_err("Bad input.\n");
return -EINVAL;
}
tag = smcinvoke_wrk_trd->type == SHMB_WORKER_THREAD ? "shmbridge":"object";
while (!kthread_should_stop()) {
wait_event_interruptible(
smcinvoke_wrk_trd->postprocess_kthread_wq,
kthread_should_stop() ||
(atomic_read(&smcinvoke_wrk_trd->postprocess_kthread_state)
== POST_KT_WAKEUP));
pr_debug("kthread to %s postprocess is called %d\n",
tag,
atomic_read(&smcinvoke_wrk_trd->postprocess_kthread_state));
switch (smcinvoke_wrk_trd->type) {
case SHMB_WORKER_THREAD:
smcinvoke_shmbridge_post_process();
break;
case OBJECT_WORKER_THREAD:
smcinvoke_object_post_process();
break;
default:
pr_err("Invalid thread type(%d), do nothing.\n",
(int)smcinvoke_wrk_trd->type);
break;
}
atomic_set(&smcinvoke_wrk_trd->postprocess_kthread_state,
POST_KT_SLEEP);
}
pr_warn("kthread to %s postprocess stopped\n", tag);
return 0;
}
static int smcinvoke_create_kthreads(void)
{
int i, rc = 0;
const enum worker_thread_type thread_type[MAX_THREAD_NUMBER] = {
SHMB_WORKER_THREAD, OBJECT_WORKER_THREAD};
for (i = 0; i < MAX_THREAD_NUMBER; i++) {
init_waitqueue_head(&smcinvoke[i].postprocess_kthread_wq);
smcinvoke[i].type = thread_type[i];
smcinvoke[i].postprocess_kthread_task = kthread_run(
smcinvoke_postprocess_kthread_func,
&smcinvoke[i], thread_name[i]);
if (IS_ERR(smcinvoke[i].postprocess_kthread_task)) {
rc = PTR_ERR(smcinvoke[i].postprocess_kthread_task);
pr_err("fail to create kthread to postprocess, rc = %x\n",
rc);
return rc;
}
atomic_set(&smcinvoke[i].postprocess_kthread_state,
POST_KT_SLEEP);
}
return rc;
}
static void smcinvoke_destroy_kthreads(void)
{
int i;
for (i = 0; i < MAX_THREAD_NUMBER; i++)
kthread_stop(smcinvoke[i].postprocess_kthread_task);
}
static inline void free_mem_obj_locked(struct smcinvoke_mem_obj *mem_obj)
{
int ret = 0;
bool is_bridge_created = mem_obj->is_smcinvoke_created_shmbridge;
struct dma_buf *dmabuf_to_free = mem_obj->dma_buf;
uint64_t shmbridge_handle = mem_obj->shmbridge_handle;
struct smcinvoke_shmbridge_deregister_pending_list *entry = NULL;
if (!mem_obj->is_smcinvoke_created_shmbridge) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return;
entry->data.shmbridge_handle = mem_obj->shmbridge_handle;
entry->data.dmabuf_to_free = mem_obj->dma_buf;
mutex_lock(&bridge_postprocess_lock);
list_add_tail(&entry->list, &g_bridge_postprocess);
mutex_unlock(&bridge_postprocess_lock);
pr_debug("SHMBridge list: added a Handle:%#llx\n",
mem_obj->shmbridge_handle);
__wakeup_postprocess_kthread(&smcinvoke[SHMB_WORKER_THREAD]);
} else {
dma_buf_put(mem_obj->dma_buf);
}
list_del(&mem_obj->list);
kfree(mem_obj);
mem_obj = NULL;
mutex_unlock(&g_smcinvoke_lock);
if (is_bridge_created)
ret = qtee_shmbridge_deregister(shmbridge_handle);
if (ret)
pr_err("Error:%d delete bridge failed leaking memory 0x%x\n",
ret, dmabuf_to_free);
else
dma_buf_put(dmabuf_to_free);
mutex_lock(&g_smcinvoke_lock);
}
static void del_mem_regn_obj_locked(struct kref *kref)
@@ -2299,14 +2568,9 @@ static int release_cb_server(uint16_t server_id)
int smcinvoke_release_filp(struct file *filp)
{
int ret = 0;
bool release_handles;
uint8_t *in_buf = NULL;
uint8_t *out_buf = NULL;
struct smcinvoke_msg_hdr hdr = {0};
struct smcinvoke_file_data *file_data = filp->private_data;
struct smcinvoke_cmd_req req = {0};
uint32_t tzhandle = 0;
struct qtee_shm in_shm = {0}, out_shm = {0};
struct smcinvoke_object_release_pending_list *entry = NULL;
trace_smcinvoke_release_filp(current->files, filp,
file_count(filp), file_data->context_type);
@@ -2321,38 +2585,23 @@ int smcinvoke_release_filp(struct file *filp)
if (!tzhandle || tzhandle == SMCINVOKE_TZ_ROOT_OBJ)
goto out;
ret = qtee_shmbridge_allocate_shm(SMCINVOKE_TZ_MIN_BUF_SIZE, &in_shm);
if (ret) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
ret = -ENOMEM;
pr_err("shmbridge alloc failed for in msg in release\n");
goto out;
}
ret = qtee_shmbridge_allocate_shm(SMCINVOKE_TZ_MIN_BUF_SIZE, &out_shm);
if (ret) {
ret = -ENOMEM;
pr_err("shmbridge alloc failed for out msg in release\n");
goto out;
}
entry->data.tzhandle = tzhandle;
entry->data.context_type = file_data->context_type;
mutex_lock(&object_postprocess_lock);
list_add_tail(&entry->list, &g_object_postprocess);
mutex_unlock(&object_postprocess_lock);
pr_debug("Object release list: added a handle:0x%lx\n", tzhandle);
__wakeup_postprocess_kthread(&smcinvoke[OBJECT_WORKER_THREAD]);
in_buf = in_shm.vaddr;
out_buf = out_shm.vaddr;
hdr.tzhandle = tzhandle;
hdr.op = OBJECT_OP_RELEASE;
hdr.counts = 0;
*(struct smcinvoke_msg_hdr *)in_buf = hdr;
ret = prepare_send_scm_msg(in_buf, in_shm.paddr,
SMCINVOKE_TZ_MIN_BUF_SIZE, out_buf, out_shm.paddr,
SMCINVOKE_TZ_MIN_BUF_SIZE, &req, NULL, &release_handles,
file_data->context_type, &in_shm, &out_shm);
process_piggyback_data(out_buf, SMCINVOKE_TZ_MIN_BUF_SIZE);
out:
kfree(filp->private_data);
filp->private_data = NULL;
qtee_shmbridge_free_shm(&in_shm);
qtee_shmbridge_free_shm(&out_shm);
return ret;
@@ -2428,14 +2677,22 @@ static int smcinvoke_probe(struct platform_device *pdev)
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc) {
pr_err("dma_set_mask_and_coherent failed %d\n", rc);
goto exit_destroy_device;
goto exit_cv_del;
}
legacy_smc_call = of_property_read_bool((&pdev->dev)->of_node,
"qcom,support-legacy_smc");
invoke_cmd = legacy_smc_call ? SMCINVOKE_INVOKE_CMD_LEGACY : SMCINVOKE_INVOKE_CMD;
rc = smcinvoke_create_kthreads();
if (rc) {
pr_err("smcinvoke_create_kthreads failed %d\n", rc);
goto exit_cv_del;
}
return 0;
exit_cv_del:
cdev_del(&smcinvoke_cdev);
exit_destroy_device:
device_destroy(driver_class, smcinvoke_device_no);
exit_destroy_class:
@@ -2449,6 +2706,7 @@ static int smcinvoke_remove(struct platform_device *pdev)
{
int count = 1;
smcinvoke_destroy_kthreads();
cdev_del(&smcinvoke_cdev);
device_destroy(driver_class, smcinvoke_device_no);
class_destroy(driver_class);