// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ /* * GenieZone (hypervisor-based seucrity platform) enables hardware protected * and isolated security execution environment, includes * 1. GZ hypervisor * 2. Hypervisor-TEE OS (built-in Trusty OS) * 3. Drivers (ex: debug, communication and interrupt) for GZ and * hypervisor-TEE OS * 4. GZ and hypervisor-TEE and GZ framework (supporting multiple TEE * ecosystem, ex: M-TEE, Trusty, GlobalPlatform, ...) */ #include #include #include #include #include #include /* Linux kernel 4.14 */ #include /*** Trusty MT test device attributes ***/ #define GZ_CONCURRENT_TEST_ENABLE (0) #if GZ_CONCURRENT_TEST_ENABLE static DEFINE_MUTEX(gz_concurrent_lock); static struct task_struct *trusty_task; static struct task_struct *nebula_task; static struct device *mtee_dev[TEE_ID_END]; static int cpu[TEE_ID_END] = { 0 }; static uint64_t s_cnt[TEE_ID_END] = { 0 }, f_cnt[TEE_ID_END] = { 0 }; static int stress_trusty_thread(void *data) { s32 a, b, ret; struct device *dev = mtee_dev[TEE_ID_TRUSTY]; s_cnt[0] = f_cnt[0] = 0; allow_signal(SIGKILL); while (!kthread_should_stop()) { get_random_bytes(&a, sizeof(s32)); a &= 0xDFFFFFFF; get_random_bytes(&b, sizeof(s32)); b &= 0xDFFFFFF; ret = trusty_std_call32(dev, MTEE_SMCNR(MT_SMCF_SC_ADD, dev), a, b, 0); if ((a + b) == ret) s_cnt[0]++; else f_cnt[0]++; pr_info_ratelimited("[%lld/%lld] %u + %u = %u, %s\n", s_cnt[0], f_cnt[0], a, b, ret, (a + b) == ret ? "PASS" : "FAIL"); if (signal_pending(trusty_task)) break; } pr_debug("[%s] End of test, succeed %lld, failed %lld\n", __func__, s_cnt[0], f_cnt[0]); s_cnt[0] = f_cnt[0] = 0; return 0; } static int stress_nebula_thread(void *data) { s32 a, b, c, ret; struct device *dev = mtee_dev[TEE_ID_NEBULA]; s_cnt[1] = f_cnt[1] = 0; allow_signal(SIGKILL); while (!kthread_should_stop()) { get_random_bytes(&a, sizeof(s32)); a &= 0xFF; get_random_bytes(&b, sizeof(s32)); b &= 0xFF; get_random_bytes(&c, sizeof(s32)); c &= 0xFF; ret = trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_TEST_MULTIPLY, dev), a, b, c); if ((a * b * c) == ret) s_cnt[1]++; else f_cnt[1]++; pr_info_ratelimited("[%lld/%lld] %d * %d * %d= %d, %s\n", s_cnt[1], f_cnt[1], a, b, c, ret, (a * b * c) == ret ? "PASS" : "FAIL"); if (signal_pending(nebula_task)) break; } pr_info("[%s] End of test, succeed %lld, failed %lld\n", __func__, s_cnt[1], f_cnt[1]); s_cnt[1] = f_cnt[1] = 0; return 0; } static ssize_t gz_concurrent_show(struct device *dev, struct device_attribute *attr, char *buf) { char str[256], tmp[256]; int i = 0; size_t ret = 0; str[0] = '\0'; mutex_lock(&gz_concurrent_lock); if (trusty_task) { i = snprintf(tmp, 256, "stress_trusty on CPU %d, succeed %lld, failed %lld\n", cpu[0], s_cnt[0], f_cnt[0]); tmp[i] = '\0'; strncat(str, tmp, 255); strncat(str, "MTEE 1.0 :stress_trusty_thread Running\n", 255); pr_info("stress_trusty on CPU %d, succeed %lld, failed %lld\n", cpu[0], s_cnt[0], f_cnt[0]); } if (nebula_task) { i = snprintf(tmp, 256, "stress_nebula on CPU %d, succeed %lld, failed %lld\n", cpu[1], s_cnt[1], f_cnt[1]); tmp[i] = '\0'; strncat(str, tmp, 255); strncat(str, "MTEE 2.0 :stress_nebula_thread Running\n", 255); pr_info("stress_nebula on CPU %d, succeed %lld, failed %lld\n", cpu[1], s_cnt[1], f_cnt[1]); } if (!trusty_task && !nebula_task) { strncat(str, "Usage:\techo 1 > start default smc stress for gz33\n", 255); strncat(str, "\techo 55 > both thread on cpu 5\n", 255); strncat(str, "\techo 56 > one thread on cpu 5, another on cpu 6\n", 255); strncat(str, "\techo 0 > to stop\n", 255); } ret = scnprintf(buf, 256, "%s", str); mutex_unlock(&gz_concurrent_lock); return ret; } static ssize_t gz_concurrent_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { unsigned long tmp = 0; if (!buf) return -EINVAL; if (kstrtoul(buf, 10, &tmp)) { pr_info("[%s] convert to number failed\n", __func__); return -1; } tmp %= 100; pr_info("[%s] get number %lu\n", __func__, tmp); mutex_lock(&gz_concurrent_lock); if (tmp == 0) { if (trusty_task) { pr_info("[%s] Stop stress_trusty_thread", __func__); kthread_stop(trusty_task); } if (nebula_task) { pr_info("[%s] Stop stress_nebula_thread", __func__); kthread_stop(nebula_task); } trusty_task = nebula_task = NULL; mutex_unlock(&gz_concurrent_lock); return n; } if (trusty_task || nebula_task) { pr_info("[%s] Start already!\n", __func__); mutex_unlock(&gz_concurrent_lock); return n; } trusty_task = kthread_create(stress_trusty_thread, dev, "stress_trusty_thread"); nebula_task = kthread_create(stress_nebula_thread, dev, "stress_nebula_thread"); cpu[0] = cpu[1] = 0; if (tmp == 1) { cpu[0] = 5; cpu[1] = 6; } else { cpu[0] = (tmp / 10) & (num_possible_cpus() - 1); cpu[1] = (tmp % 10) & (num_possible_cpus() - 1); } if (IS_ERR(trusty_task)) pr_info("[%s] Unable to start on cpu %d: stress_trusty_thread", __func__, cpu[0]); else { kthread_bind(trusty_task, cpu[0]); pr_info("[%s] Start stress_trusty_thread on cpu %d", __func__, cpu[0]); wake_up_process(trusty_task); } if (IS_ERR(nebula_task)) pr_info("[%s] Unable to start at cpu %d: stress_nebula_thread", __func__, cpu[1]); else { kthread_bind(nebula_task, cpu[1]); pr_info("[%s] Start stress_nebula_thread on cpu %d", __func__, cpu[1]); wake_up_process(nebula_task); } mutex_unlock(&gz_concurrent_lock); return n; } #else static ssize_t gz_concurrent_show(struct device *dev, struct device_attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "Not support Geniezone concurrent test\n"); } static ssize_t gz_concurrent_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } #endif // GZ_CONCURRENT_TEST_ENABLE DEVICE_ATTR_RW(gz_concurrent); static ssize_t trusty_add_show(struct device *dev, struct device_attribute *attr, char *buf) { s32 a, b, ret; get_random_bytes(&a, sizeof(s32)); a &= 0xFF; get_random_bytes(&b, sizeof(s32)); b &= 0xFF; ret = trusty_std_call32(dev, MTEE_SMCNR(MT_SMCF_SC_ADD, dev), a, b, 0); return scnprintf(buf, PAGE_SIZE, "%d + %d = %d, %s\n", a, b, ret, (a + b) == ret ? "PASS" : "FAIL"); } static ssize_t trusty_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_add); static ssize_t trusty_threads_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Dump Trusty threads info to memlog */ trusty_fast_call32(dev, MTEE_SMCNR(MT_SMCF_FC_THREADS, dev), 0, 0, 0); /* Dump threads info from memlog to kmsg */ trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_NOP, dev), 0, 0, 0); return 0; } static ssize_t trusty_threads_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_threads); static ssize_t trusty_threadstats_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Dump Trusty threads info to memlog */ trusty_fast_call32(dev, MTEE_SMCNR(MT_SMCF_FC_THREADSTATS, dev), 0, 0, 0); /* Dump threads info from memlog to kmsg */ trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_NOP, dev), 0, 0, 0); return 0; } static ssize_t trusty_threadstats_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_threadstats); static ssize_t trusty_threadload_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Dump Trusty threads info to memlog */ trusty_fast_call32(dev, MTEE_SMCNR(MT_SMCF_FC_THREADLOAD, dev), 0, 0, 0); /* Dump threads info from memlog to kmsg */ trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_NOP, dev), 0, 0, 0); return 0; } static ssize_t trusty_threadload_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_threadload); static ssize_t trusty_heap_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Dump Trusty threads info to memlog */ trusty_fast_call32(dev, MTEE_SMCNR(MT_SMCF_FC_HEAP_DUMP, dev), 0, 0, 0); /* Dump threads info from memlog to kmsg */ trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_NOP, dev), 0, 0, 0); return 0; } static ssize_t trusty_heap_dump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_heap_dump); static ssize_t trusty_apps_show(struct device *dev, struct device_attribute *attr, char *buf) { /* Dump Trusty threads info to memlog */ trusty_fast_call32(dev, MTEE_SMCNR(MT_SMCF_FC_APPS, dev), 0, 0, 0); /* Dump threads info from memlog to kmsg */ trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_NOP, dev), 0, 0, 0); return 0; } static ssize_t trusty_apps_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_apps); static ssize_t trusty_vdev_reset_show(struct device *dev, struct device_attribute *attr, char *buf) { trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_VDEV_RESET, dev), 0, 0, 0); return 0; } static ssize_t trusty_vdev_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(trusty_vdev_reset); static void trusty_create_debugfs(struct trusty_state *s, struct device *dev) { int ret; pr_info("%s-%s\n", __func__, get_tee_name(s->tee_id)); if (!is_trusty_tee(s->tee_id)) return; #if GZ_CONCURRENT_TEST_ENABLE mtee_dev[s->tee_id] = dev; #endif ret = device_create_file(dev, &dev_attr_gz_concurrent); if (ret) goto err_create_gz_concurrent; ret = device_create_file(dev, &dev_attr_trusty_add); if (ret) goto err_create_trusty_add; ret = device_create_file(dev, &dev_attr_trusty_threads); if (ret) goto err_create_trusty_threads; ret = device_create_file(dev, &dev_attr_trusty_threadstats); if (ret) goto err_create_trusty_threadstats; ret = device_create_file(dev, &dev_attr_trusty_threadload); if (ret) goto err_create_trusty_threadload; ret = device_create_file(dev, &dev_attr_trusty_heap_dump); if (ret) goto err_create_trusty_heap_dump; ret = device_create_file(dev, &dev_attr_trusty_apps); if (ret) goto err_create_trusty_apps; ret = device_create_file(dev, &dev_attr_trusty_vdev_reset); if (ret) goto err_create_trusty_vdev_reset; return; err_create_trusty_vdev_reset: device_remove_file(dev, &dev_attr_trusty_vdev_reset); err_create_trusty_apps: device_remove_file(dev, &dev_attr_trusty_apps); err_create_trusty_heap_dump: device_remove_file(dev, &dev_attr_trusty_heap_dump); err_create_trusty_threadload: device_remove_file(dev, &dev_attr_trusty_threadload); err_create_trusty_threadstats: device_remove_file(dev, &dev_attr_trusty_threadstats); err_create_trusty_threads: device_remove_file(dev, &dev_attr_trusty_threads); err_create_trusty_add: device_remove_file(dev, &dev_attr_trusty_add); err_create_gz_concurrent: device_remove_file(dev, &dev_attr_gz_concurrent); } /*** Nebula MT test device attributes ***/ static ssize_t vmm_fast_add_show(struct device *dev, struct device_attribute *attr, char *buf) { s32 a, b, c, ret; get_random_bytes(&a, sizeof(s32)); a &= 0xFF; get_random_bytes(&b, sizeof(s32)); b &= 0xFF; get_random_bytes(&c, sizeof(s32)); c &= 0xFF; ret = trusty_fast_call32(dev, MTEE_SMCNR(SMCF_FC_TEST_ADD, dev), a, b, c); return scnprintf(buf, PAGE_SIZE, "%d + %d + %d = %d, %s\n", a, b, c, ret, (a + b + c) == ret ? "PASS" : "FAIL"); } static ssize_t vmm_fast_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(vmm_fast_add); ssize_t vmm_fast_multiply_show(struct device *dev, struct device_attribute *attr, char *buf) { s32 a, b, c, ret; get_random_bytes(&a, sizeof(s32)); a &= 0xFF; get_random_bytes(&b, sizeof(s32)); b &= 0xFF; get_random_bytes(&c, sizeof(s32)); c &= 0xFF; ret = trusty_fast_call32(dev, MTEE_SMCNR(SMCF_FC_TEST_MULTIPLY, dev), a, b, c); return scnprintf(buf, PAGE_SIZE, "%d * %d * %d = %d, %s\n", a, b, c, ret, (a * b * c) == ret ? "PASS" : "FAIL"); } static ssize_t vmm_fast_multiply_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(vmm_fast_multiply); static ssize_t vmm_std_add_show(struct device *dev, struct device_attribute *attr, char *buf) { s32 a, b, c, ret; get_random_bytes(&a, sizeof(s32)); a &= 0xFF; get_random_bytes(&b, sizeof(s32)); b &= 0xFF; get_random_bytes(&c, sizeof(s32)); c &= 0xFF; ret = trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_TEST_ADD, dev), a, b, c); return scnprintf(buf, PAGE_SIZE, "%d + %d + %d = %d, %s\n", a, b, c, ret, (a + b + c) == ret ? "PASS" : "FAIL"); } static ssize_t vmm_std_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(vmm_std_add); static ssize_t vmm_std_multiply_show(struct device *dev, struct device_attribute *attr, char *buf) { s32 a, b, c, ret; get_random_bytes(&a, sizeof(s32)); a &= 0xFF; get_random_bytes(&b, sizeof(s32)); b &= 0xFF; get_random_bytes(&c, sizeof(s32)); c &= 0xFF; ret = trusty_std_call32(dev, MTEE_SMCNR(SMCF_SC_TEST_MULTIPLY, dev), a, b, c); return scnprintf(buf, PAGE_SIZE, "%d * %d * %d = %d, %s\n", a, b, c, ret, (a * b * c) == ret ? "PASS" : "FAIL"); } static ssize_t vmm_std_multiply_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { return n; } DEVICE_ATTR_RW(vmm_std_multiply); static void nebula_create_debugfs(struct trusty_state *s, struct device *dev) { int ret; pr_info("%s-%s\n", __func__, get_tee_name(s->tee_id)); if (!is_nebula_tee(s->tee_id)) return; #if GZ_CONCURRENT_TEST_ENABLE mtee_dev[s->tee_id] = dev; #endif ret = device_create_file(dev, &dev_attr_vmm_fast_add); if (ret) goto err_create_vmm_fast_add; ret = device_create_file(dev, &dev_attr_vmm_fast_multiply); if (ret) goto err_create_vmm_fast_multiply; ret = device_create_file(dev, &dev_attr_vmm_std_add); if (ret) goto err_create_vmm_std_add; ret = device_create_file(dev, &dev_attr_vmm_std_multiply); if (ret) goto err_create_vmm_std_multiply; return; err_create_vmm_std_multiply: device_remove_file(dev, &dev_attr_vmm_std_multiply); err_create_vmm_std_add: device_remove_file(dev, &dev_attr_vmm_std_add); err_create_vmm_fast_multiply: device_remove_file(dev, &dev_attr_vmm_fast_multiply); err_create_vmm_fast_add: device_remove_file(dev, &dev_attr_vmm_fast_add); } void mtee_create_debugfs(struct trusty_state *s, struct device *dev) { if (!s || !dev) { pr_info("[%s] Invalid input\n", __func__); return; } if (is_trusty_tee(s->tee_id)) trusty_create_debugfs(s, dev); else if (is_nebula_tee(s->tee_id)) nebula_create_debugfs(s, dev); }