/* * Copyright (c) 2023 EPAM Systems * * SPDX-License-Identifier: Apache-2.0 */ /** * @addtogroup t_optee_driver * @{ * @defgroup t_optee_driver test_optee_driver * @} */ #include #include #include #include #include #include #include #include /* * TODO: Test shm_register/shm_register API to work with huge * buffers (more than 512K) to ensure that optee_construct_page_list * builds correct page list. */ #define TEE_OPTEE_CAP_TZ BIT(0) typedef void (*smc_cb_t)(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res); static struct test_call { int num; int pending; smc_cb_t smc_cb; uint32_t a0; uint32_t a1; uint32_t a2; uint32_t a3; uint32_t a4; uint32_t a5; uint32_t a6; uint32_t a7; k_tid_t th_id; } t_call; static struct test_call wait_call; static struct test_call send_call; void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { if (a0 == OPTEE_SMC_CALLS_UID) { res->a0 = OPTEE_MSG_UID_0; res->a1 = OPTEE_MSG_UID_1; res->a2 = OPTEE_MSG_UID_2; res->a3 = OPTEE_MSG_UID_3; return; } if (a0 == OPTEE_SMC_EXCHANGE_CAPABILITIES) { res->a1 = OPTEE_SMC_SEC_CAP_DYNAMIC_SHM; return; } if (a0 == OPTEE_SMC_GET_THREAD_COUNT) { res->a1 = 5; return; } if (t_call.pending && t_call.smc_cb) { t_call.smc_cb(a0, a1, a2, a3, a4, a5, a6, a7, res); } if (wait_call.pending && wait_call.smc_cb && (k_current_get() == wait_call.th_id)) { wait_call.smc_cb(a0, a1, a2, a3, a4, a5, a6, a7, res); } if (send_call.pending && send_call.smc_cb) { send_call.smc_cb(a0, a1, a2, a3, a4, a5, a6, a7, res); } } /* Allocate dummy arm_smccc_hvc function for the tests */ void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { } ZTEST(optee_test_suite, test_get_version) { int ret; struct tee_version_info info; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); ret = tee_get_version(dev, NULL); zassert_equal(ret, -EINVAL, "tee_get_version failed with code %d", ret); ret = tee_get_version(dev, &info); zassert_ok(ret, "tee_get_version failed with code %d", ret); zassert_equal(info.impl_id, 1, "Wrong impl_id"); zassert_equal(info.impl_caps, TEE_OPTEE_CAP_TZ, "Wrong impl_caps"); zassert_equal(info.gen_caps, TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM, "Wrong gen_caps"); ret = tee_get_version(dev, NULL); zassert_equal(ret, -EINVAL, "tee_get_version failed with code %d", ret); } void fast_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { t_call.a0 = a0; t_call.a1 = a1; t_call.a2 = a2; t_call.a3 = a3; t_call.a4 = a4; t_call.a5 = a5; t_call.a6 = a6; t_call.a7 = a7; res->a0 = OPTEE_SMC_RETURN_OK; } void fail_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { res->a0 = OPTEE_SMC_RETURN_EBUSY; } ZTEST(optee_test_suite, test_fast_calls) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; /* Fail pass */ ret = tee_open_session(dev, NULL, 0, NULL, &session_id); zassert_equal(ret, -EINVAL, "tee_open_session failed with code %d", ret); ret = tee_open_session(dev, NULL, 0, NULL, NULL); zassert_equal(ret, -EINVAL, "tee_open_session failed with code %d", ret); /* Happy pass */ arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } ZTEST(optee_test_suite, test_invoke_fn) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; /* Fail pass */ ret = tee_invoke_func(dev, NULL, 0, NULL); zassert_equal(ret, -EINVAL, "tee_invoke_fn failed with code %d", ret); t_call.smc_cb = fail_call; invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 0, NULL); zassert_equal(ret, -EINVAL, "tee_invoke_fn failed with code %d", ret); t_call.smc_cb = fast_call; /* Happy pass */ arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 1, ¶m); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } ZTEST(optee_test_suite, test_cancel) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); ret = tee_cancel(dev, 1, 25); zassert_ok(ret, "tee_cancel failed with code %d", ret); ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } void normal_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { t_call.a0 = a0; t_call.a1 = a1; t_call.a2 = a2; t_call.a3 = a3; t_call.a4 = a4; t_call.a5 = a5; t_call.a6 = a6; t_call.a7 = a7; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = a4; res->a2 = a5; res->a3 = a3; res->a4 = 0; res->a5 = 0; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_FREE; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; break; case 2: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_FOREIGN_INTR; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } ZTEST(optee_test_suite, test_normal_calls) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = normal_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } ZTEST(optee_test_suite, test_reg_unreg) { int ret; int addr; struct tee_shm *shm = NULL; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = normal_call; zassert_not_null(dev, "Unable to get dev"); /* Fail pass */ ret = tee_shm_register(dev, &addr, 1, 0, NULL); zassert_equal(ret, -EINVAL, "tee_shm_register failed with code %d", ret); t_call.num = 0; ret = tee_shm_register(dev, NULL, 1, 0, &shm); zassert_equal(ret, -ENOMEM, "tee_shm_register failed with code %d", ret); t_call.num = 0; ret = tee_shm_register(dev, &addr, 1, 0, NULL); zassert_equal(ret, -EINVAL, "tee_shm_register failed with code %d", ret); t_call.num = 0; ret = tee_shm_register(dev, &addr, 0, 0, &shm); zassert_equal(ret, 0, "tee_shm_register failed with code %d", ret); t_call.num = 0; ret = tee_shm_unregister(dev, NULL); zassert_equal(ret, -EINVAL, "tee_shm_unregister failed with code %d", ret); /* Happy pass */ t_call.num = 0; ret = tee_shm_register(dev, &addr, 1, 0, &shm); zassert_ok(ret, "tee_shm_register failed with code %d", ret); t_call.num = 0; ret = tee_shm_unregister(dev, shm); zassert_ok(ret, "tee_shm_unregister failed with code %d", ret); t_call.num = 0; ret = tee_shm_alloc(dev, 1, 0, &shm); zassert_ok(ret, "tee_shm_alloc failed with code %d", ret); t_call.num = 0; ret = tee_shm_free(dev, shm); zassert_ok(ret, "tee_shm_free failed with code %d", ret); t_call.pending = 0; } static uint64_t regs_to_u64(uint32_t reg0, uint32_t reg1) { return (uint64_t)(((uint64_t)reg0 << 32) | reg1); } static void u64_to_regs(uint64_t val, unsigned long *reg0, unsigned long *reg1) { *reg0 = val >> 32; *reg1 = val; } uint64_t g_shm_ref; uint64_t g_func_shm_ref; void cmd_alloc_free_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; t_call.a0 = a0; t_call.a1 = a1; t_call.a2 = a2; t_call.a3 = a3; t_call.a4 = a4; t_call.a5 = a5; t_call.a6 = a6; t_call.a7 = a7; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = 1; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; arg = (struct optee_msg_arg *)regs_to_u64(a1, a2); arg->cmd = OPTEE_RPC_CMD_SHM_ALLOC; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.b = 4096; arg->params[0].u.value.a = OPTEE_RPC_SHM_TYPE_KERNEL; res->a1 = a4; res->a2 = a5; g_shm_ref = regs_to_u64(a4, a5); break; case 2: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; printk("a1 %lx, a2 %lx a4 %lx a5 %lx\n", a1, a2); shm = (struct tee_shm *)regs_to_u64(a1, a2); arg = (struct optee_msg_arg *)shm->addr; arg->cmd = OPTEE_RPC_CMD_SHM_FREE; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.a = OPTEE_RPC_SHM_TYPE_KERNEL; arg->params[0].u.value.b = g_func_shm_ref; u64_to_regs(g_shm_ref, &res->a1, &res->a2); break; case 3: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); u64_to_regs(g_shm_ref, &res->a1, &res->a2); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_FREE; break; case 4: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } ZTEST(optee_test_suite, test_func_shm_alloc) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; t_call.smc_cb = cmd_alloc_free_call; invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 1, ¶m); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); t_call.num = 0; t_call.smc_cb = fast_call; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } K_KERNEL_STACK_DEFINE(supp_stack, 8192); #define TEE_NUM_PARAMS 5 static struct k_thread supp_thread_data; volatile int supp_thread_ok; void cmd_rpc_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; t_call.a0 = a0; t_call.a1 = a1; t_call.a2 = a2; t_call.a3 = a3; t_call.a4 = a4; t_call.a5 = a5; t_call.a6 = a6; t_call.a7 = a7; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = 1; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; arg = (struct optee_msg_arg *)regs_to_u64(a1, a2); /* Fall back to supplicant branch */ arg->cmd = 555; arg->num_params = 2; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.a = 1111; arg->params[0].u.value.b = 3; arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT; res->a1 = a4; res->a2 = a5; break; case 2: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); shm = (struct tee_shm *)regs_to_u64(a1, a2); arg = (struct optee_msg_arg *)shm->addr; zassert_equal(arg->params[1].attr, OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT, "%s failed wrong attr %lx", __func__, arg->params[0].attr); zassert_equal(arg->params[1].u.value.a, 0x1234, "%s failed wrong a %lx", __func__, arg->params[0].u.value.a); zassert_equal(arg->params[1].u.value.b, 0x5678, "%s failed wrong b %lx", __func__, arg->params[0].u.value.b); res->a0 = OPTEE_SMC_RETURN_OK; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } static void supp_thread_comm(void) { const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); struct tee_param params[TEE_NUM_PARAMS]; unsigned int num_params = TEE_NUM_PARAMS; uint32_t func; int ret; supp_thread_ok = true; ret = tee_suppl_recv(dev, &func, &num_params, params); if (ret != 0) { printk("tee_suppl_recv failed with %d\n", ret); supp_thread_ok = false; } if (func != 555 || num_params != 2) { printk("Unexpected func & num_params %d %d\n", func, num_params); supp_thread_ok = false; } if (params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT || params[1].attr != OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT || params[0].a != 1111 || params[0].b != 3) { printk("Unexpected params %d %d %d\n", params[0].attr, params[0].a, params[0].b); supp_thread_ok = false; } params[1].a = 0x1234; params[1].b = 0x5678; tee_suppl_send(dev, 0, 2, params); } ZTEST(optee_test_suite, test_func_rpc_supp_cmd) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); k_tid_t tid; tid = k_thread_create(&supp_thread_data, supp_stack, K_KERNEL_STACK_SIZEOF(supp_stack), (k_thread_entry_t)supp_thread_comm, NULL, NULL, NULL, K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1), 0, K_NO_WAIT); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; t_call.smc_cb = cmd_rpc_call; invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 1, ¶m); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); t_call.num = 0; t_call.smc_cb = fast_call; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); zassert_true(supp_thread_ok, "supp_thread_comm failed"); t_call.pending = 0; k_thread_abort(tid); } void cmd_shm_alloc_appl(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; t_call.a0 = a0; t_call.a1 = a1; t_call.a2 = a2; t_call.a3 = a3; t_call.a4 = a4; t_call.a5 = a5; t_call.a6 = a6; t_call.a7 = a7; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = 1; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; arg = (struct optee_msg_arg *)regs_to_u64(a1, a2); arg->cmd = OPTEE_RPC_CMD_SHM_ALLOC; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.b = 4096; arg->params[0].u.value.a = OPTEE_RPC_SHM_TYPE_APPL; res->a1 = a4; res->a2 = a5; g_shm_ref = regs_to_u64(a4, a5); break; case 2: res->a0 = OPTEE_SMC_RETURN_OK; break; case 3: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; shm = (struct tee_shm *)regs_to_u64(a1, a2); arg = (struct optee_msg_arg *)shm->addr; arg->cmd = OPTEE_RPC_CMD_SHM_FREE; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.a = OPTEE_RPC_SHM_TYPE_APPL; arg->params[0].u.value.b = g_shm_ref; u64_to_regs(g_shm_ref, &res->a1, &res->a2); break; case 4: u64_to_regs(g_shm_ref, &res->a1, &res->a2); res->a0 = OPTEE_SMC_RETURN_OK; break; case 5: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } static void supp_thread_alloc(void) { const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); struct tee_param params[TEE_NUM_PARAMS]; void *mem; unsigned int num_params = TEE_NUM_PARAMS; uint32_t func; int ret; supp_thread_ok = true; ret = tee_suppl_recv(dev, &func, &num_params, params); if (ret != 0) { printk("tee_suppl_recv failed with %d\n", ret); supp_thread_ok = false; goto fail1; } if (func != OPTEE_RPC_CMD_SHM_ALLOC || num_params != 1) { printk("Unexpected func & num_params %d %d\n", func, num_params); supp_thread_ok = false; goto fail1; } if (params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INOUT || params[0].a != OPTEE_RPC_SHM_TYPE_APPL) { printk("Unexpected params %d %d\n", params[0].attr, params[0].a); supp_thread_ok = false; goto fail1; } mem = k_malloc(params[0].b); if (!mem) { printk("k_malloc failed\n"); supp_thread_ok = false; goto fail1; } params[0].c = (uint64_t)mem; tee_suppl_send(dev, 0, 1, params); ret = tee_suppl_recv(dev, &func, &num_params, params); if (func != OPTEE_RPC_CMD_SHM_FREE || num_params != 1) { printk("Unexpected func & num_params %d %d\n", func, num_params); supp_thread_ok = false; goto fail2; } tee_suppl_send(dev, 0, 1, params); k_free(mem); return; fail1: tee_suppl_send(dev, -1, 1, params); fail2: tee_suppl_send(dev, -1, 1, params); } ZTEST(optee_test_suite, test_func_rpc_shm_alloc_appl) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); k_tid_t tid; tid = k_thread_create(&supp_thread_data, supp_stack, K_KERNEL_STACK_SIZEOF(supp_stack), (k_thread_entry_t)supp_thread_alloc, NULL, NULL, NULL, K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1), 0, K_NO_WAIT); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; t_call.smc_cb = cmd_shm_alloc_appl; invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 1, ¶m); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); zassert_true(supp_thread_ok, "supp_thread failed"); t_call.num = 0; t_call.smc_cb = fast_call; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; k_thread_abort(tid); } void cmd_gettime_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = 1; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; arg = (struct optee_msg_arg *)regs_to_u64(a1, a2); arg->cmd = OPTEE_RPC_CMD_GET_TIME; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT; res->a1 = a4; res->a2 = a5; g_shm_ref = regs_to_u64(a4, a5); break; case 2: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_FREE; shm = (struct tee_shm *)regs_to_u64(a1, a2); arg = (struct optee_msg_arg *)shm->addr; t_call.a6 = arg->params[0].u.value.a; t_call.a7 = arg->params[0].u.value.b; u64_to_regs(g_shm_ref, &res->a1, &res->a2); break; case 3: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } #define TICKS 0xDEADBEEF #define TEST_SEC 37359285 #define TEST_NSEC 590000000 extern void z_vrfy_sys_clock_tick_set(uint64_t tick); ZTEST(optee_test_suite, test_gettime) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; t_call.smc_cb = cmd_gettime_call; z_vrfy_sys_clock_tick_set(TICKS); invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 1, ¶m); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); zassert_equal(t_call.a6, TEST_SEC, "Unexpected secs"); zassert_equal(t_call.a7, TEST_NSEC, "Unexpected nsecs"); t_call.num = 0; t_call.smc_cb = fast_call; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } void cmd_suspend_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = 1; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; arg = (struct optee_msg_arg *)regs_to_u64(a1, a2); arg->cmd = OPTEE_RPC_CMD_SUSPEND; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.a = t_call.a0; res->a1 = a4; res->a2 = a5; g_shm_ref = regs_to_u64(a4, a5); break; case 2: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_FREE; shm = (struct tee_shm *)regs_to_u64(a1, a2); arg = (struct optee_msg_arg *)shm->addr; t_call.a6 = arg->params[0].u.value.a; t_call.a7 = arg->params[0].u.value.b; u64_to_regs(g_shm_ref, &res->a1, &res->a2); break; case 3: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } ZTEST(optee_test_suite, test_suspend) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; t_call.a0 = 4000; /* Set timeout 4000 ms */ t_call.smc_cb = cmd_suspend_call; invoke_arg.func = 12; invoke_arg.session = 1; ret = tee_invoke_func(dev, &invoke_arg, 1, ¶m); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); t_call.num = 0; t_call.smc_cb = fast_call; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } void cmd_notify_alloc_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_ALLOC; res->a1 = 1; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; /* res->a1 = a4; */ /* res->a2 = a5; */ g_shm_ref = regs_to_u64(a4, a5); break; default: zassert_equal(a0, 0x32000004, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } void cmd_notify_free_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (t_call.num) { case 0: zassert_equal(a0, 0x32000004, "%s failed with ret %lx", __func__, a0); u64_to_regs(g_shm_ref, &res->a1, &res->a2); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_FREE; break; case 1: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; res->a1 = a4; res->a2 = a5; break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; } t_call.num++; } static struct test_call wait_call; static struct test_call send_call; void cmd_notify_wait_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (wait_call.num) { case 0: zassert_equal(a0, 0x32000004, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; shm = (struct tee_shm *)g_shm_ref; arg = (struct optee_msg_arg *)shm->addr; arg->cmd = OPTEE_RPC_CMD_NOTIFICATION; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.a = OPTEE_RPC_NOTIFICATION_WAIT; arg->params[0].u.value.b = wait_call.a0; /* Set notification key */ u64_to_regs(g_shm_ref, &res->a1, &res->a2); break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; wait_call.a6 = 1; } wait_call.num++; } void cmd_notify_send_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res) { struct optee_msg_arg *arg; struct tee_shm *shm; res->a1 = a1; res->a2 = a2; res->a3 = a3; res->a4 = a4; res->a5 = a5; switch (send_call.num) { case 0: zassert_equal(a0, 0x32000004, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_RPC_PREFIX | OPTEE_SMC_RPC_FUNC_CMD; shm = (struct tee_shm *)g_shm_ref; arg = (struct optee_msg_arg *)shm->addr; arg->cmd = OPTEE_RPC_CMD_NOTIFICATION; arg->num_params = 1; arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; arg->params[0].u.value.a = OPTEE_RPC_NOTIFICATION_SEND; arg->params[0].u.value.b = send_call.a0; /* Set notification key */ u64_to_regs(g_shm_ref, &res->a1, &res->a2); break; default: zassert_equal(a0, 0x32000003, "%s failed with ret %lx", __func__, a0); res->a0 = OPTEE_SMC_RETURN_OK; send_call.a6 = 1; } send_call.num++; } static int test_invoke_fn(const struct device *const dev, uint32_t session_id) { struct tee_invoke_func_arg invoke_arg = {}; struct tee_param param = {}; invoke_arg.func = 12; invoke_arg.session = session_id; return tee_invoke_func(dev, &invoke_arg, 1, ¶m); } static void wait_handler(void *arg0, void *arg1, void *arg2) { int ret; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); ARG_UNUSED(arg0); ARG_UNUSED(arg1); ARG_UNUSED(arg2); /* This expects wait_call.a0 to be set as key and wait_call.a4 as session_id */ wait_call.pending = 1; wait_call.th_id = k_current_get(); wait_call.num = 0; wait_call.a6 = 0; /* result */ wait_call.smc_cb = cmd_notify_wait_call; ret = test_invoke_fn(dev, t_call.a4); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); wait_call.a6 = 1; wait_call.pending = 0; } #define WAIT_STACKSIZE 512 #define WAIT_PRIORITY 4 static K_THREAD_STACK_DEFINE(wait_stack, WAIT_STACKSIZE); static struct k_thread wait_thread; static void do_wait(int key, uint32_t session_id) { wait_call.a0 = key; wait_call.a4 = session_id; k_thread_create(&wait_thread, wait_stack, WAIT_STACKSIZE, wait_handler, INT_TO_POINTER(1) /* key */, NULL, NULL, K_PRIO_COOP(WAIT_PRIORITY), 0, K_NO_WAIT); } ZTEST(optee_test_suite, test_notify) { int ret; uint32_t session_id; struct tee_open_session_arg arg = {}; struct tee_param param = {}; const struct device *const dev = DEVICE_DT_GET_ONE(linaro_optee_tz); zassert_not_null(dev, "Unable to get dev"); t_call.pending = 1; t_call.num = 0; t_call.smc_cb = fast_call; arg.uuid[0] = 111; arg.clnt_uuid[0] = 222; arg.clnt_login = TEEC_LOGIN_PUBLIC; param.attr = TEE_PARAM_ATTR_TYPE_NONE; param.a = 3333; ret = tee_open_session(dev, &arg, 1, ¶m, &session_id); zassert_ok(ret, "tee_open_session failed with code %d", ret); t_call.num = 0; t_call.smc_cb = cmd_notify_alloc_call; ret = test_invoke_fn(dev, session_id); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); t_call.pending = 0; /* Wait then send */ do_wait(1, session_id); k_sleep(K_MSEC(100)); send_call.pending = 1; send_call.num = 0; send_call.a0 = 1; /* key */ send_call.smc_cb = cmd_notify_send_call; ret = test_invoke_fn(dev, session_id); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); send_call.pending = 0; k_sleep(K_MSEC(100)); zassert_equal(wait_call.a6, 1, "Notify wait is still in progress"); /* Test send then wait */ send_call.pending = 1; send_call.num = 0; send_call.a0 = 2; /* key */ send_call.smc_cb = cmd_notify_send_call; ret = test_invoke_fn(dev, session_id); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); send_call.pending = 0; wait_call.pending = 1; wait_call.th_id = k_current_get(); wait_call.num = 0; wait_call.a0 = 2; /* key */ wait_call.smc_cb = cmd_notify_wait_call; ret = test_invoke_fn(dev, session_id); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); wait_call.pending = 0; /* End of test section */ t_call.pending = 1; t_call.num = 0; t_call.smc_cb = cmd_notify_free_call; ret = test_invoke_fn(dev, session_id); zassert_ok(ret, "tee_invoke_fn failed with code %d", ret); t_call.num = 0; t_call.smc_cb = fast_call; ret = tee_close_session(dev, session_id); zassert_ok(ret, "close_session failed with code %d", ret); t_call.pending = 0; } ZTEST_SUITE(optee_test_suite, NULL, NULL, NULL, NULL, NULL);