mirror of
https://github.com/openharmony/kernel_linux_common_modules.git
synced 2026-08-27 01:31:36 -04:00
b52fba7668
Signed-off-by: minjie.yu <minjie.yu@unionman.com.cn>
463 lines
11 KiB
C
463 lines
11 KiB
C
/*
|
|
* Copyright (C) 2022 Huawei Technologies Co., Ltd.
|
|
* Decription: for tzdriver debug.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
#include "tzdebug.h"
|
|
#include <linux/workqueue.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/list.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/version.h>
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
|
#include <stdarg.h>
|
|
#else
|
|
#include <linux/stdarg.h>
|
|
#endif
|
|
#include <linux/mm.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <securec.h>
|
|
#include <asm/io.h>
|
|
#include "tc_ns_log.h"
|
|
#include "tc_ns_client.h"
|
|
#include "tc_client_driver.h"
|
|
#include "teek_ns_client.h"
|
|
#include "smc_smp.h"
|
|
#include "teek_client_constants.h"
|
|
#include "mailbox_mempool.h"
|
|
#include "tlogger.h"
|
|
#include "cmdmonitor.h"
|
|
#include "session_manager.h"
|
|
#include "internal_functions.h"
|
|
|
|
#define DEBUG_OPT_LEN 128
|
|
|
|
#ifdef CONFIG_TA_MEM_INUSE_ONLY
|
|
#define TA_MEMSTAT_ALL 0
|
|
#else
|
|
#define TA_MEMSTAT_ALL 1
|
|
#endif
|
|
|
|
static struct dentry *g_tz_dbg_dentry;
|
|
|
|
typedef void (*tzdebug_opt_func)(const char *param);
|
|
|
|
struct opt_ops {
|
|
char *name;
|
|
tzdebug_opt_func func;
|
|
};
|
|
|
|
static DEFINE_MUTEX(g_meminfo_lock);
|
|
static struct tee_mem g_tee_meminfo;
|
|
static void tzmemdump(const char *param);
|
|
static int send_dump_mem(int flag, int history, const struct tee_mem *statmem)
|
|
{
|
|
struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
|
|
struct mb_cmd_pack *mb_pack = NULL;
|
|
int ret = 0;
|
|
|
|
if (!statmem) {
|
|
tloge("statmem is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mb_pack = mailbox_alloc_cmd_pack();
|
|
if (!mb_pack)
|
|
return -ENOMEM;
|
|
|
|
smc_cmd.cmd_id = GLOBAL_CMD_ID_DUMP_MEMINFO;
|
|
smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
|
|
mb_pack->operation.paramtypes = teec_param_types(
|
|
TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_VALUE_INPUT,
|
|
TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE);
|
|
mb_pack->operation.params[0].memref.buffer = (unsigned int)mailbox_virt_to_phys((uintptr_t)statmem);
|
|
mb_pack->operation.params[0].memref.size = sizeof(*statmem);
|
|
mb_pack->operation.buffer_h_addr[0] =
|
|
(unsigned int)((uint64_t)mailbox_virt_to_phys((uintptr_t)statmem) >> ADDR_TRANS_NUM);
|
|
mb_pack->operation.params[1].value.a = (unsigned int)flag;
|
|
mb_pack->operation.params[1].value.b = (unsigned int)history;
|
|
smc_cmd.operation_phys =
|
|
(unsigned int)mailbox_virt_to_phys((uintptr_t)&mb_pack->operation);
|
|
smc_cmd.operation_h_phys =
|
|
(unsigned int)((uint64_t)mailbox_virt_to_phys((uintptr_t)&mb_pack->operation) >> ADDR_TRANS_NUM);
|
|
|
|
livepatch_down_read_sem();
|
|
if (tc_ns_smc(&smc_cmd) != 0) {
|
|
ret = -EPERM;
|
|
tloge("send dump mem failed\n");
|
|
}
|
|
livepatch_up_read_sem();
|
|
|
|
tz_log_write();
|
|
mailbox_free(mb_pack);
|
|
return ret;
|
|
}
|
|
|
|
void tee_dump_mem(void)
|
|
{
|
|
tzmemdump(NULL);
|
|
if (tlogger_store_msg(CONFIG_TEE_LOG_ACHIVE_PATH,
|
|
sizeof(CONFIG_TEE_LOG_ACHIVE_PATH)) < 0)
|
|
tloge("[cmd_monitor_tick]tlogger store lastmsg failed\n");
|
|
}
|
|
|
|
/* get meminfo (tee_mem + N * ta_mem < 4Kbyte) from tee */
|
|
static int get_tee_meminfo_cmd(void)
|
|
{
|
|
int ret;
|
|
struct tee_mem *mem = NULL;
|
|
|
|
mem = mailbox_alloc(sizeof(*mem), MB_FLAG_ZERO);
|
|
if (!mem)
|
|
return -ENOMEM;
|
|
|
|
ret = send_dump_mem(0, TA_MEMSTAT_ALL, mem);
|
|
if (ret != 0) {
|
|
tloge("send dump failed\n");
|
|
mailbox_free(mem);
|
|
return ret;
|
|
}
|
|
|
|
mutex_lock(&g_meminfo_lock);
|
|
ret = memcpy_s(&g_tee_meminfo, sizeof(g_tee_meminfo), mem, sizeof(*mem));
|
|
if (ret != 0)
|
|
tloge("memcpy failed\n");
|
|
|
|
mutex_unlock(&g_meminfo_lock);
|
|
mailbox_free(mem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static atomic_t g_cmd_send = ATOMIC_INIT(1);
|
|
|
|
void set_cmd_send_state(void)
|
|
{
|
|
atomic_set(&g_cmd_send, 1);
|
|
}
|
|
|
|
int get_tee_meminfo(struct tee_mem *meminfo)
|
|
{
|
|
errno_t s_ret;
|
|
|
|
if (!get_tz_init_flag()) return EFAULT;
|
|
|
|
if (!meminfo)
|
|
return -EINVAL;
|
|
|
|
if (atomic_read(&g_cmd_send) != 0) {
|
|
if (get_tee_meminfo_cmd() != 0)
|
|
return -EFAULT;
|
|
} else {
|
|
atomic_set(&g_cmd_send, 0);
|
|
}
|
|
|
|
mutex_lock(&g_meminfo_lock);
|
|
s_ret = memcpy_s(meminfo, sizeof(*meminfo),
|
|
&g_tee_meminfo, sizeof(g_tee_meminfo));
|
|
mutex_unlock(&g_meminfo_lock);
|
|
if (s_ret != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(get_tee_meminfo);
|
|
|
|
static void tzdump(const char *param)
|
|
{
|
|
(void)param;
|
|
show_cmd_bitmap();
|
|
wakeup_tc_siq(SIQ_DUMP_SHELL);
|
|
}
|
|
|
|
static void tzmemdump(const char *param)
|
|
{
|
|
struct tee_mem *mem = NULL;
|
|
|
|
(void)param;
|
|
mem = mailbox_alloc(sizeof(*mem), MB_FLAG_ZERO);
|
|
if (!mem) {
|
|
tloge("mailbox alloc failed\n");
|
|
return;
|
|
}
|
|
|
|
if (send_dump_mem(1, 1, mem) != 0)
|
|
tloge("send dump mem failed\n");
|
|
|
|
mailbox_free(mem);
|
|
}
|
|
|
|
static struct opt_ops g_opt_arr[] = {
|
|
{"dump", tzdump},
|
|
{"memdump", tzmemdump},
|
|
{"dump_service", dump_services_status},
|
|
};
|
|
|
|
static ssize_t tz_dbg_opt_read(struct file *filp, char __user *ubuf,
|
|
size_t cnt, loff_t *ppos)
|
|
{
|
|
char *obuf = NULL;
|
|
char *p = NULL;
|
|
ssize_t ret;
|
|
uint32_t oboff = 0;
|
|
uint32_t i;
|
|
|
|
(void)(filp);
|
|
|
|
obuf = kzalloc(DEBUG_OPT_LEN, GFP_KERNEL);
|
|
if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)obuf))
|
|
return -ENOMEM;
|
|
p = obuf;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(g_opt_arr); i++) {
|
|
int len = snprintf_s(p, DEBUG_OPT_LEN - oboff, DEBUG_OPT_LEN -oboff -1,
|
|
"%s ", g_opt_arr[i].name);
|
|
if (len < 0) {
|
|
kfree(obuf);
|
|
tloge("snprintf opt name of idx %d failed\n", i);
|
|
return -EINVAL;
|
|
}
|
|
p += len;
|
|
oboff += (uint32_t)len;
|
|
}
|
|
obuf[oboff - 1] = '\n';
|
|
|
|
ret = simple_read_from_buffer(ubuf, cnt, ppos, obuf, oboff);
|
|
kfree(obuf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t tz_dbg_opt_write(struct file *filp,
|
|
const char __user *ubuf, size_t cnt, loff_t *ppos)
|
|
{
|
|
char buf[128] = {0};
|
|
char *value = NULL;
|
|
char *p = NULL;
|
|
uint32_t i = 0;
|
|
|
|
if (!ubuf || !filp || !ppos)
|
|
return -EINVAL;
|
|
|
|
if (cnt >= sizeof(buf))
|
|
return -EINVAL;
|
|
|
|
if (cnt == 0)
|
|
return -EINVAL;
|
|
|
|
if (copy_from_user(buf, ubuf, cnt) != 0)
|
|
return -EFAULT;
|
|
|
|
buf[cnt] = 0;
|
|
if (cnt > 0 && buf[cnt -1] == '\n')
|
|
buf[cnt - 1] = 0;
|
|
value = buf;
|
|
p = strsep(&value, ":"); /* when buf has no :, value may be NULL */
|
|
if (!p)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(g_opt_arr); i++) {
|
|
if ((strncmp(p, g_opt_arr[i].name,
|
|
strlen(g_opt_arr[i].name)) ==0) &&
|
|
strlen(p) == strlen(g_opt_arr[i].name)) {
|
|
g_opt_arr[i].func(value);
|
|
return (ssize_t)cnt;
|
|
}
|
|
}
|
|
return -EFAULT;
|
|
}
|
|
|
|
static const struct file_operations g_tz_dbg_opt_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = tz_dbg_opt_read,
|
|
.write = tz_dbg_opt_write,
|
|
};
|
|
|
|
#ifdef CONFIG_MEMSTAT_DEBUGFS
|
|
static int memstat_debug_show(struct seq_file *m, void *v)
|
|
{
|
|
struct tee_mem *mem_stat = NULL;
|
|
int ret;
|
|
uint32_t i;
|
|
(void)v;
|
|
|
|
mem_stat = kzalloc(sizeof(*mem_stat), GFP_KERNEL);
|
|
if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)mem_stat))
|
|
return -ENOMEM;
|
|
|
|
ret = get_tee_meminfo(mem_stat);
|
|
if (ret != 0) {
|
|
tloge("get tee meminfo failed\n");
|
|
kfree(mem_stat);
|
|
mem_stat = NULL;
|
|
return -EINVAL;
|
|
}
|
|
|
|
seq_printf(m, "TotalMem:%u Pmem:%u Free_Mem:%u Free_Mem_Min:%u TA_Num:%u\n",
|
|
mem_stat->total_mem, mem_stat->pmem, mem_stat->free_mem, mem_stat->free_mem_min, mem_stat->ta_num);
|
|
|
|
for (i = 0; i < mem_stat->ta_num; i++)
|
|
seq_printf(m, "ta_name:%s ta_pmem:%u pmem_max:%u pmem_limit:%u\n",
|
|
mem_stat->ta_mem_info[i].ta_name, mem_stat->ta_mem_info[i].pmem,
|
|
mem_stat->ta_mem_info[i].pmem_max, mem_stat->ta_mem_info[i].pmem_limit);
|
|
|
|
kfree(mem_stat);
|
|
mem_stat = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int tz_memstat_open(struct inode *inode, struct file *file)
|
|
{
|
|
(void)inode;
|
|
return single_open(file, memstat_debug_show, NULL);
|
|
}
|
|
|
|
static const struct file_operations g_tz_dbg_memstat_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = tz_memstat_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_TEE_TRACE
|
|
static int tee_trace_event_show(struct seq_file *m, void *v)
|
|
{
|
|
struct tee_trace_view_t view = { 0, 0, 0, 0, { 0 }, { 0 } };
|
|
struct trace_log_info log_info;
|
|
(void)v;
|
|
|
|
get_tee_trace_start(&view);
|
|
if (view.buffer_is_full == 1)
|
|
seq_printf(m, "Total Trace Events: %u (Notice: Buffer is full)\n", view.total);
|
|
else
|
|
seq_printf(m, "Total Trace Events: %u\n", view.total);
|
|
|
|
if (view.total > 0) {
|
|
uint32_t i = 0;
|
|
|
|
while (get_tee_trace_next(&view, &log_info, false) != -1) {
|
|
uint32_t task_ca = (uint32_t)(log_info.add_info);
|
|
uint32_t task_idx = (uint32_t)(log_info.add_info >> 32);
|
|
|
|
if (log_info.event_id == SCHED_IN || log_info.event_id == SCHED_OUT) {
|
|
seq_printf(m, "[%4u][cpu%3u][ca-%5u] %10llu : %s %u %s\n",
|
|
i++, log_info.cpu, log_info.ca_pid, log_info.time, log_info.event_name,
|
|
task_ca, get_tee_trace_task_name(task_idx));
|
|
} else {
|
|
seq_printf(m, "[%4u][cpu%3u][ca-%5u] %10llu : %s %llu\n",
|
|
i++, log_info.cpu, log_info.ca_pid, log_info.time, log_info.event_name,
|
|
log_info.add_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tee_trace_event_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, tee_trace_event_show, NULL);
|
|
}
|
|
|
|
struct tee_trace_cmd_t {
|
|
const char *cmd;
|
|
int (*func)(void);
|
|
} tee_trace_cmd[] = {
|
|
{"start", tee_trace_event_start},
|
|
{"loop_record", tee_trace_event_start_loop_record},
|
|
{"stop", tee_trace_event_stop}
|
|
};
|
|
|
|
static ssize_t tee_trace_event_write(struct file *filp,
|
|
const char __user *ubuf, size_t cnt, loff_t *ppos)
|
|
{
|
|
char buf[32] = {0};
|
|
uint32_t i = 0;
|
|
|
|
if (cnt >= sizeof(buf))
|
|
return -EINVAL;
|
|
|
|
if (copy_from_user(buf, ubuf, cnt))
|
|
return -EINVAL;
|
|
|
|
buf[cnt] = 0;
|
|
if (cnt > 0 && buf[cnt - 1] == '\n')
|
|
buf[cnt - 1] = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tee_trace_cmd); i++) {
|
|
if (!strncmp(buf, tee_trace_cmd[i].cmd,
|
|
strlen(tee_trace_cmd[i].cmd))) {
|
|
tee_trace_cmd[i].func();
|
|
return cnt;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct file_operations tee_trace_event_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = tee_trace_event_open,
|
|
.read = seq_read,
|
|
.write = tee_trace_event_write,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
#endif
|
|
|
|
int tzdebug_init(void)
|
|
{
|
|
#if defined(DEF_ENG) || defined(CONFIG_TZDRIVER_MODULE)
|
|
g_tz_dbg_dentry = debugfs_create_dir("tzdebug", NULL);
|
|
if (!g_tz_dbg_dentry)
|
|
return -1;
|
|
|
|
debugfs_create_file("opt", 0660, g_tz_dbg_dentry, NULL,
|
|
&g_tz_dbg_opt_fops);
|
|
|
|
#ifdef CONFIG_MEMSTAT_DEBUGFS
|
|
debugfs_create_file("memstat", 0444, g_tz_dbg_dentry, NULL,
|
|
&g_tz_dbg_memstat_fops);
|
|
#endif
|
|
|
|
#ifdef CONFIG_TEE_TRACE
|
|
debugfs_create_file("tee_trace", 0660, g_tz_dbg_dentry, NULL,
|
|
&tee_trace_event_fops);
|
|
tee_trace_event_enable();
|
|
#endif
|
|
|
|
#else
|
|
(void)g_tz_dbg_dentry;
|
|
(void)g_tz_dbg_opt_fops;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void free_tzdebug(void)
|
|
{
|
|
#if defined(DEF_ENG) || defined(CONFIG_TZDRIVER_MODULE)
|
|
if (!g_tz_dbg_dentry)
|
|
return;
|
|
|
|
debugfs_remove_recursive(g_tz_dbg_dentry);
|
|
g_tz_dbg_dentry = NULL;
|
|
#endif
|
|
} |