// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015-2019, MICROTRUST Incorporated * All Rights Reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tz_dcih.h" #include "tz_dcih_test.h" #define IMSG_TAG "[tz_dcih_test]" #include #include static struct task_struct *test_notify_thread; bool start_notify_test; static struct task_struct *test_wait_notify_thread; bool start_wait_notify_test; static DEFINE_MUTEX(test_status_mutex); static DECLARE_COMPLETION(thread_start); static DECLARE_COMPLETION(thread_finish); static int dci_greeting_test(uint32_t driver_id) { struct dci_message *msg; uint32_t tmp_value; int ret; int num_item = 0; msg = (struct dci_message *)tz_get_share_buffer(driver_id); memset(msg, 0, sizeof(struct dci_message)); msg->cmd = GREETING; ret = snprintf(msg->req_data, sizeof(msg->req_data), "Say Hello to 0x%x", driver_id); if (ret <= 0) { IMSG_ERROR("snprintf failed, ret %d\n", ret); return ret; } ret = tz_notify_driver(driver_id); if (ret < 0) { IMSG_ERROR("failed to notify secure driver, ret %d\n", ret); goto exit; } num_item = sscanf(msg->res_data, "Hello 0x%x", &tmp_value); if (num_item != 1 || tmp_value != driver_id) { IMSG_ERROR("return unexpected data, res_data [%s]\n", msg->res_data); ret = -EINVAL; } exit: return ret; } static int dci_bit_op_not_test(uint32_t driver_id) { struct dci_message *msg; int ret; int i; msg = (struct dci_message *)tz_get_share_buffer(driver_id); memset(msg, 0, sizeof(struct dci_message)); msg->cmd = BIT_OP_NOT; for (i = 0; i < MAX_BUF_SIZE; i++) msg->req_data[i] = (uint8_t)(i & 0xff); ret = tz_notify_driver(driver_id); if (ret < 0) { IMSG_ERROR("failed to notify secure driver, ret %d\n", ret); goto exit; } for (i = 0; i < MAX_BUF_SIZE; i++) { if (msg->res_data[i] != (uint8_t)(~msg->req_data[i])) { IMSG_ERROR("return unexpected data:\n"); IMSG_ERROR("res_data[%d] expect 0x%02x actual 0x%02x\n", i, (uint8_t)(~msg->req_data[i]), msg->res_data[i]); ret = -EINVAL; break; } } exit: return ret; } static int notify_driver_test(void *data) { uint32_t driver_id = (uintptr_t)data; int ret; IMSG_DEBUG("starting notify test thread, driver_id 0x%x\n", driver_id); allow_signal(SIGTERM); ret = tz_create_share_buffer(driver_id, MAX_DCIH_BUF_SIZE); if (ret < 0) { IMSG_ERROR("failed to create dci share buffer\n"); goto exit; } ret = dci_greeting_test(driver_id); if (ret) goto exit; ret = dci_bit_op_not_test(driver_id); exit: tz_free_share_buffer(driver_id); complete(&thread_finish); IMSG_DEBUG("notify test is finished, wait for terminate signal...\n"); while (!kthread_should_stop()) msleep(50); IMSG_DEBUG("Got terminate signal, going to stop thread\n"); return ret; } static int process_dci_msg(uint32_t driver_id) { struct dci_message *msg = NULL; uint32_t tmp_value; int i; int num_item; int ret = 0; msg = (struct dci_message *)tz_get_share_buffer(driver_id); IMSG_DEBUG("cmd %d\n", msg->cmd); switch (msg->cmd) { case GREETING: num_item = sscanf(msg->req_data, "Say Hello to 0x%x", &tmp_value); if (num_item != 1) { IMSG_ERROR("no item found in sscanf\n"); ret = -EINVAL; break; } ret = snprintf(msg->res_data, sizeof(msg->res_data), "Hello 0x%x", tmp_value); if (ret <= 0) IMSG_ERROR("snprintf failed\n"); break; case BIT_OP_NOT: for (i = 0; i < MAX_BUF_SIZE; i++) msg->res_data[i] = (uint8_t)(~msg->req_data[i]); break; default: ret = -EINVAL; break; } return ret; } static int wait_driver_notify_test(void *data) { uint32_t driver_id = (uintptr_t)data; int ret; IMSG_DEBUG("starting wait notify test thread, driver_id 0x%x\n", driver_id); allow_signal(SIGTERM); ret = tz_create_share_buffer(driver_id, MAX_DCIH_BUF_SIZE); if (ret < 0) { IMSG_ERROR("failed to create dci share buffer\n"); complete(&thread_start); goto exit; } complete(&thread_start); while (!kthread_should_stop()) { ret = tz_wait_for_notification(driver_id); if (ret < 0) { if (ret == -ERESTARTSYS) { IMSG_INFO("waiting was interrupted\n"); ret = 0; } else IMSG_ERROR("failed to waiting, ret %d\n", ret); break; } ret = process_dci_msg(driver_id); if (ret < 0) IMSG_ERROR("failed to process dci message, %d\n", ret); ret = tz_notify_driver(driver_id); if (ret < 0) { IMSG_ERROR("failed to notify secure driver, %d\n", ret); break; } } exit: tz_free_share_buffer(driver_id); IMSG_DEBUG("Got terminate signal, going to stop thread\n"); return ret; } void start_dcih_notify_test(uint32_t driver_id) { uintptr_t data = driver_id; mutex_lock(&test_status_mutex); if (start_notify_test) { IMSG_ERROR("test thread is still running\n"); goto exit; } start_notify_test = true; test_notify_thread = kthread_run(notify_driver_test, (void *)data, "dcih_notify_test_thread"); if (IS_ERR(test_notify_thread)) { IMSG_ERROR("failed to create dcih test notify thread\n"); test_notify_thread = NULL; start_notify_test = false; goto exit; } wait_for_completion(&thread_finish); exit: mutex_unlock(&test_status_mutex); } int get_dcih_notify_test_result(void) { int ret = -EINVAL; mutex_lock(&test_status_mutex); start_notify_test = false; if (test_notify_thread) { send_sig(SIGTERM, test_notify_thread, 0); ret = kthread_stop(test_notify_thread); test_notify_thread = NULL; } mutex_unlock(&test_status_mutex); return ret; } void start_dcih_wait_notify_test(uint32_t driver_id) { uintptr_t data = driver_id; mutex_lock(&test_status_mutex); if (start_wait_notify_test) { IMSG_ERROR("test thread is still running\n"); goto exit; } start_wait_notify_test = true; test_wait_notify_thread = kthread_run(wait_driver_notify_test, (void *)data, "dcih_wait_notify_test_thread"); if (IS_ERR(test_wait_notify_thread)) { IMSG_ERROR("create dcih test wait notify thread failed\n"); test_wait_notify_thread = NULL; start_wait_notify_test = false; goto exit; } wait_for_completion(&thread_start); exit: mutex_unlock(&test_status_mutex); } int get_dcih_wait_notify_test_result(void) { int ret = -EINVAL; mutex_lock(&test_status_mutex); start_wait_notify_test = false; if (test_wait_notify_thread) { send_sig(SIGTERM, test_wait_notify_thread, 0); ret = kthread_stop(test_wait_notify_thread); test_wait_notify_thread = NULL; } mutex_unlock(&test_status_mutex); return ret; }