322 lines
7.2 KiB
C
322 lines
7.2 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (C) 2019 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <linux/arm-smccc.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#if defined(CONFIG_MTK_GIC_V3_EXT)
|
||
|
#include <linux/irqchip/mtk-gic-extend.h>
|
||
|
#endif
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_irq.h>
|
||
|
#include <linux/of_address.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/proc_fs.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/soc/mediatek/mtk_sip_svc.h>
|
||
|
#include <tee_sanity.h>
|
||
|
#include <linux/sched/clock.h>
|
||
|
#include <archcounter_timesync.h>
|
||
|
#include <asm/arch_timer.h>
|
||
|
|
||
|
#define CREATE_TRACE_POINTS
|
||
|
#include <trace_tee.h>
|
||
|
|
||
|
static u64 boot_to_kernel_ns;
|
||
|
|
||
|
static void set_boot_to_kernel_time(void)
|
||
|
{
|
||
|
u64 gud_init_time;
|
||
|
u64 boot_time;
|
||
|
|
||
|
gud_init_time = sched_clock();
|
||
|
boot_time = mtk_get_archcounter_time(arch_counter_get_cntvct());
|
||
|
|
||
|
boot_to_kernel_ns = boot_time - gud_init_time;
|
||
|
}
|
||
|
|
||
|
static void convert_kernel_time(char *tee_timestamp, char *output,
|
||
|
size_t output_sz)
|
||
|
{
|
||
|
u64 boot_to_kernel_us;
|
||
|
u64 kernel_time_us;
|
||
|
u64 tee_time_us;
|
||
|
u64 tee_time_s;
|
||
|
int err;
|
||
|
|
||
|
if (!tee_timestamp || !output || !output_sz) {
|
||
|
pr_err(PFX "param check failed\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
err = kstrtou64(tee_timestamp, 10, &tee_time_us);
|
||
|
if (err) {
|
||
|
pr_err(PFX "kstrtou64 failed\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
boot_to_kernel_us = div_u64(boot_to_kernel_ns, NSEC_PER_USEC);
|
||
|
kernel_time_us = tee_time_us - boot_to_kernel_us;
|
||
|
|
||
|
div_u64_rem(kernel_time_us, USEC_PER_SEC, (u32 *)&tee_time_us);
|
||
|
tee_time_s = div_u64(kernel_time_us, USEC_PER_SEC);
|
||
|
|
||
|
snprintf(output, output_sz, "%llu.%06u", tee_time_s, (u32)tee_time_us);
|
||
|
}
|
||
|
|
||
|
int32_t mtk_tee_log_tracing(u32 cpuid, u16 tee_pid, char *line, u32 line_len)
|
||
|
{
|
||
|
struct tee_trace_struct tee_trace_info;
|
||
|
char ktimestamp[128] = {0};
|
||
|
char trace_buf[128] = {0};
|
||
|
char *trace_buf_ptr;
|
||
|
char delim[] = ":";
|
||
|
char *prefix;
|
||
|
char *postfix;
|
||
|
char *timestamp;
|
||
|
|
||
|
bool trace_start = false;
|
||
|
bool trace_end = false;
|
||
|
|
||
|
if (unlikely(line == NULL)) {
|
||
|
pr_err(PFX "%s:%d NULL pointer\n", __func__, __LINE__);
|
||
|
return TEE_TRACE_PREFIX_NOT_MATCH;
|
||
|
}
|
||
|
|
||
|
prefix = strstr(line, TEE_BEGIN_TRACE);
|
||
|
postfix = strstr(line, TEE_END_TRACE);
|
||
|
|
||
|
if (prefix) {
|
||
|
trace_start = true;
|
||
|
line = prefix;
|
||
|
|
||
|
} else if (postfix) {
|
||
|
trace_end = true;
|
||
|
line = postfix;
|
||
|
|
||
|
} else
|
||
|
return TEE_TRACE_PREFIX_NOT_MATCH;
|
||
|
|
||
|
strncpy(trace_buf, line, (line_len >= sizeof(trace_buf)) ?
|
||
|
sizeof(trace_buf) : line_len);
|
||
|
|
||
|
trace_buf_ptr = trace_buf;
|
||
|
prefix = strsep(&trace_buf_ptr, delim);
|
||
|
if (!prefix) {
|
||
|
pr_err(PFX "strsep prefix failed\n");
|
||
|
return TEE_TRACE_PARSE_FAILED;
|
||
|
}
|
||
|
|
||
|
timestamp = strsep(&trace_buf_ptr, delim);
|
||
|
if (!timestamp) {
|
||
|
pr_err(PFX "strsep timestamp failed\n");
|
||
|
return TEE_TRACE_PARSE_FAILED;
|
||
|
}
|
||
|
|
||
|
postfix = strsep(&trace_buf_ptr, delim);
|
||
|
if (!postfix) {
|
||
|
pr_err(PFX "strsep postfix failed\n");
|
||
|
return TEE_TRACE_PARSE_FAILED;
|
||
|
}
|
||
|
|
||
|
convert_kernel_time(timestamp, ktimestamp, sizeof(ktimestamp));
|
||
|
|
||
|
tee_trace_info.cpuid = cpuid;
|
||
|
tee_trace_info.ktimestamp = ktimestamp;
|
||
|
tee_trace_info.tee_pid = tee_pid;
|
||
|
tee_trace_info.tee_postfix = postfix;
|
||
|
|
||
|
if (trace_start)
|
||
|
trace_tee_sched_start(&tee_trace_info);
|
||
|
else if (trace_end)
|
||
|
trace_tee_sched_end(&tee_trace_info);
|
||
|
|
||
|
return TEE_TRACE_OK;
|
||
|
}
|
||
|
|
||
|
static bool enable_ut;
|
||
|
static bool enable_read_hwirq;
|
||
|
static uint32_t tee_sanity_irq;
|
||
|
static uint32_t tee_sanity_hwirq;
|
||
|
|
||
|
static void tee_ut(uint32_t cmd)
|
||
|
{
|
||
|
struct arm_smccc_res res;
|
||
|
|
||
|
pr_info(PFX "%s, cmd=0x%x\n", __func__, cmd);
|
||
|
if (cmd == TEE_UT_READ_INTR) {
|
||
|
pr_info(PFX "tee_sanity_hwirq:0x%x\n", tee_sanity_hwirq);
|
||
|
enable_read_hwirq = true;
|
||
|
|
||
|
} else if (cmd == TEE_UT_TRIGGER_INTR) {
|
||
|
pr_info(PFX "trigger interrupt(0x%x)...\n", tee_sanity_hwirq);
|
||
|
|
||
|
arm_smccc_smc(MTK_SIP_KERNEL_TEE_CONTROL, TEE_OP_ID_SET_PENDING,
|
||
|
0, 0, 0, 0, 0, 0, &res);
|
||
|
|
||
|
pr_info(PFX "trigger interrupt done!\n");
|
||
|
|
||
|
} else {
|
||
|
pr_err(PFX "%s, cmd=0x%x is not supported\n", __func__, cmd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static irqreturn_t tee_sanity_isr(int intr, void *args)
|
||
|
{
|
||
|
if (intr == tee_sanity_irq)
|
||
|
pr_info(PFX "receive interrupt success!\n");
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
ssize_t tee_sanity_dbg_read(struct file *file, char __user *buffer,
|
||
|
size_t count, loff_t *ppos)
|
||
|
{
|
||
|
char msg_buf[1024] = {0};
|
||
|
char *p = msg_buf;
|
||
|
int len;
|
||
|
int ret;
|
||
|
|
||
|
if (enable_read_hwirq) {
|
||
|
tee_log(p, msg_buf, "%d\n", tee_sanity_hwirq);
|
||
|
|
||
|
} else {
|
||
|
tee_log(p, msg_buf, "tee_sanity debug status:\n");
|
||
|
tee_log(p, msg_buf, "\tenable_ut: %d\n", enable_ut);
|
||
|
tee_log(p, msg_buf, "\ttee_sanity_irq: %d\n", tee_sanity_irq);
|
||
|
tee_log(p, msg_buf, "\ttee_sanity_hwirq: %d\n",
|
||
|
tee_sanity_hwirq);
|
||
|
tee_log(p, msg_buf, "\n");
|
||
|
}
|
||
|
|
||
|
len = p - msg_buf;
|
||
|
ret = simple_read_from_buffer(buffer, count, ppos, msg_buf, len);
|
||
|
|
||
|
if (!ret)
|
||
|
enable_read_hwirq = false;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ssize_t tee_sanity_dbg_write(struct file *file, const char __user *buffer,
|
||
|
size_t count, loff_t *data)
|
||
|
{
|
||
|
unsigned long param = 0;
|
||
|
char input[32] = {0};
|
||
|
char *parm_str;
|
||
|
char *cmd_str;
|
||
|
char *pinput;
|
||
|
int err, len;
|
||
|
|
||
|
len = (count < (sizeof(input) - 1)) ? count : (sizeof(input) - 1);
|
||
|
if (copy_from_user(input, buffer, len)) {
|
||
|
pr_err(PFX "copy from user failed!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
input[len] = '\0';
|
||
|
pinput = input;
|
||
|
|
||
|
cmd_str = strsep(&pinput, " ");
|
||
|
if (cmd_str == NULL)
|
||
|
return -EINVAL;
|
||
|
|
||
|
parm_str = strsep(&pinput, " ");
|
||
|
if (parm_str == NULL)
|
||
|
return -EINVAL;
|
||
|
|
||
|
err = kstrtol(parm_str, 10, ¶m);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
|
||
|
if (!strncmp(cmd_str, "enable_ut", sizeof("enable_ut"))) {
|
||
|
enable_ut = (param != 0);
|
||
|
pr_info(PFX "enable_ut = %s\n",
|
||
|
enable_ut ? "enable" : "disable");
|
||
|
return count;
|
||
|
|
||
|
} else if (!strncmp(cmd_str, "tee_ut", sizeof("tee_ut"))) {
|
||
|
if (enable_ut)
|
||
|
tee_ut(param);
|
||
|
else
|
||
|
pr_info(PFX "enable_ut is not enabled\n");
|
||
|
|
||
|
return count;
|
||
|
|
||
|
} else {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static const struct file_operations tee_sanity_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.write = tee_sanity_dbg_write,
|
||
|
.read = tee_sanity_dbg_read,
|
||
|
};
|
||
|
|
||
|
static const struct of_device_id tee_sanity_dt_match[] = {
|
||
|
{ .compatible = "mediatek,tee_sanity" },
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
static int tee_sanity_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device_node *node = pdev->dev.of_node;
|
||
|
int ret;
|
||
|
|
||
|
pr_info(PFX "driver registered\n");
|
||
|
|
||
|
if (IS_ERR(node)) {
|
||
|
pr_err(PFX "cannot find device node\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
tee_sanity_irq = irq_of_parse_and_map(node, 0);
|
||
|
if (!tee_sanity_irq) {
|
||
|
pr_err(PFX "Failed to parse and map the interrupt.\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_MTK_GIC_V3_EXT)
|
||
|
tee_sanity_hwirq = virq_to_hwirq(tee_sanity_irq);
|
||
|
pr_debug(PFX "tee_sanity_irq: 0x%x, hwirq: 0x%x\n",
|
||
|
tee_sanity_irq, tee_sanity_hwirq);
|
||
|
#else
|
||
|
pr_err(PFX "MTK_GIC_V3_EXT is not supported\n");
|
||
|
#endif
|
||
|
|
||
|
ret = devm_request_irq(&pdev->dev, tee_sanity_irq,
|
||
|
(irq_handler_t)tee_sanity_isr,
|
||
|
IRQF_TRIGGER_RISING, "tee_sanity", NULL);
|
||
|
if (ret) {
|
||
|
pr_err(PFX "Failed to request tee_sanity irq, ret(%d)\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
proc_create("tee_sanity", 0664, NULL, &tee_sanity_fops);
|
||
|
|
||
|
/* Init boot to kernel time */
|
||
|
set_boot_to_kernel_time();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct platform_driver tee_sanity_driver = {
|
||
|
.probe = tee_sanity_probe,
|
||
|
.driver = {
|
||
|
.name = KBUILD_MODNAME,
|
||
|
.of_match_table = tee_sanity_dt_match,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
module_platform_driver(tee_sanity_driver);
|
||
|
|
||
|
MODULE_DESCRIPTION("Mediatek TEE sanity Driver");
|
||
|
MODULE_AUTHOR("Neal Liu <neal.liu@mediatek.com>");
|
||
|
MODULE_LICENSE("GPL");
|