6db4831e98
Android 14
1024 lines
25 KiB
C
1024 lines
25 KiB
C
// 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, ...)
|
|
*/
|
|
/*
|
|
* This is log driver
|
|
*
|
|
* Memory log is supported - a shared memory is used for memory log
|
|
* Driver provides read function for user-space debug apps getting log
|
|
*/
|
|
|
|
#include <linux/platform_device.h>
|
|
#include <gz-trusty/smcall.h>
|
|
#include <gz-trusty/trusty.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/log2.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/signal.h> /* Linux kernel 4.14 */
|
|
#include <linux/wait.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/delay.h>
|
|
#include <asm/page.h>
|
|
#include "gz-log.h"
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of.h>
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <asm/arch_timer.h>
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
#if IS_BUILTIN(CONFIG_MTK_GZ_LOG)
|
|
#include "gz_trace_builtin.h"
|
|
#else
|
|
#include "gz_trace_module.h"
|
|
#endif
|
|
#include <linux/vmalloc.h>
|
|
|
|
struct gz_trace_dump_t {
|
|
u64 ktime_base;
|
|
u64 cntvct_base;
|
|
u32 put;
|
|
struct list_head list;
|
|
};
|
|
#endif
|
|
|
|
/* NOTE: log_rb will be put at the begin of the memory buffer.
|
|
* The actual data buffer size is
|
|
* lower_power_of_2(TRUSTY_LOG_SIZE - sizeof(struct log_rb)).
|
|
* If LOG_SIZE is PAGE_SIZE * power of 2, it will waste half of buffer.
|
|
* so that, set the buffer size (power_of_2 + 1) PAGES.
|
|
**/
|
|
#define TRUSTY_LOG_SIZE (PAGE_SIZE * 65)
|
|
#define TRUSTY_LINE_BUFFER_SIZE 256
|
|
|
|
struct gz_log_state {
|
|
struct device *dev;
|
|
struct device *trusty_dev;
|
|
struct proc_dir_entry *proc;
|
|
|
|
/*
|
|
* This lock is here to ensure only one consumer will read
|
|
* from the log ring buffer at a time.
|
|
*/
|
|
struct mutex lock;
|
|
/* FIXME: extend struct log_rb to uint64_t */
|
|
struct log_rb *log;
|
|
uint32_t get_proc;
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
uint32_t get_trace;
|
|
struct task_struct *trace_task_fd;
|
|
struct completion trace_dump_event;
|
|
bool trace_exit;
|
|
struct list_head gz_trace_dump_list;
|
|
struct mutex gz_trace_dump_mux;
|
|
struct notifier_block callback_notifier;
|
|
atomic_t gz_trace_onoff;
|
|
struct dentry *gz_log_dbg_root;
|
|
struct dentry *sys_gz_trace_on;
|
|
#endif
|
|
|
|
enum tee_id_t tee_id;
|
|
struct notifier_block call_notifier;
|
|
struct notifier_block panic_notifier;
|
|
|
|
wait_queue_head_t gz_log_wq;
|
|
atomic_t gz_log_event_count;
|
|
atomic_t readable;
|
|
int poll_event;
|
|
char line_buffer[TRUSTY_LINE_BUFFER_SIZE];
|
|
};
|
|
|
|
struct gz_log_context {
|
|
phys_addr_t paddr;
|
|
void *virt;
|
|
struct page *pages;
|
|
size_t size;
|
|
enum {DYNAMIC, STATIC_MAP, STATIC_NOMAP} flag;
|
|
struct gz_log_state *gls;
|
|
};
|
|
|
|
static struct gz_log_context glctx = {
|
|
.paddr = 0,
|
|
.virt = NULL,
|
|
.size = 0,
|
|
.flag = DYNAMIC,
|
|
};
|
|
|
|
#if IS_BUILTIN(CONFIG_MTK_GZ_LOG)
|
|
static int __init gz_log_context_init(struct reserved_mem *rmem)
|
|
{
|
|
unsigned long node;
|
|
|
|
if (!rmem) {
|
|
pr_info("[%s] ERROR: invalid reserved memory\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
glctx.paddr = rmem->base;
|
|
glctx.size = rmem->size;
|
|
|
|
node = rmem->fdt_node;
|
|
if (!of_get_flat_dt_prop(node, "no-map", NULL))
|
|
glctx.flag = STATIC_MAP;
|
|
else
|
|
glctx.flag = STATIC_NOMAP;
|
|
|
|
pr_info("[%s] rmem:%s base(0x%llx) size(0x%zx) flag(%u)\n",
|
|
__func__, rmem->name, glctx.paddr, glctx.size, glctx.flag);
|
|
return 0;
|
|
}
|
|
RESERVEDMEM_OF_DECLARE(gz_log, "mediatek,gz-log", gz_log_context_init);
|
|
#else
|
|
static void gz_log_find_mblock(void)
|
|
{
|
|
struct device_node *mblock_root = NULL, *gz_node = NULL;
|
|
struct reserved_mem *rmem;
|
|
|
|
mblock_root = of_find_node_by_path("/reserved-memory");
|
|
if (!mblock_root) {
|
|
pr_info("%s not found /reserved-memory\n", __func__);
|
|
return;
|
|
}
|
|
|
|
gz_node = of_find_compatible_node(mblock_root, NULL, "mediatek,gz-log");
|
|
if (!gz_node) {
|
|
pr_info("%s not found gz-log\n", __func__);
|
|
return;
|
|
}
|
|
|
|
rmem = of_reserved_mem_lookup(gz_node);
|
|
if (!rmem) {
|
|
pr_info("[%s] ERROR: not found address\n", __func__);
|
|
return;
|
|
}
|
|
|
|
glctx.paddr = rmem->base;
|
|
glctx.size = rmem->size;
|
|
if (!of_find_property(gz_node, "no-map", NULL))
|
|
glctx.flag = STATIC_MAP;
|
|
else
|
|
glctx.flag = STATIC_NOMAP;
|
|
|
|
pr_info("[%s] rmem:%s base(0x%llx) size(0x%zx) flag(%u)\n",
|
|
__func__, gz_node->name, glctx.paddr, glctx.size, glctx.flag);
|
|
}
|
|
#endif
|
|
|
|
static int gz_log_page_init(void)
|
|
{
|
|
if (glctx.virt)
|
|
return 0;
|
|
|
|
#if IS_MODULE(CONFIG_MTK_GZ_LOG)
|
|
gz_log_find_mblock();
|
|
#endif
|
|
|
|
if (glctx.flag >= STATIC_MAP) {
|
|
if (glctx.flag == STATIC_MAP)
|
|
glctx.virt = phys_to_virt(glctx.paddr);
|
|
else
|
|
glctx.virt = memremap(glctx.paddr, glctx.size, MEMREMAP_WB);
|
|
}
|
|
|
|
if (!glctx.virt) {
|
|
glctx.flag = DYNAMIC;
|
|
glctx.size = TRUSTY_LOG_SIZE;
|
|
glctx.virt = kzalloc(glctx.size, GFP_KERNEL);
|
|
|
|
if (!glctx.virt)
|
|
return -ENOMEM;
|
|
|
|
glctx.paddr = virt_to_phys(glctx.virt);
|
|
}
|
|
|
|
pr_info("[%s] set by %s, virt addr:%p, sz:0x%zx\n",
|
|
__func__,
|
|
glctx.flag == STATIC_NOMAP ? "static_nomap" :
|
|
glctx.flag == STATIC_MAP ? "static_map" : "dynamic",
|
|
glctx.virt, glctx.size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* get_gz_log_buffer was called in arch_initcall */
|
|
void get_gz_log_buffer(unsigned long *addr, unsigned long *paddr,
|
|
unsigned long *size, unsigned long *start)
|
|
{
|
|
gz_log_page_init();
|
|
|
|
if (!glctx.virt) {
|
|
*addr = *paddr = *size = *start = 0;
|
|
pr_info("[%s] ERR gz_log init failed\n", __func__);
|
|
return;
|
|
}
|
|
*addr = (unsigned long)glctx.virt;
|
|
*paddr = (unsigned long)glctx.paddr;
|
|
pr_info("[%s] virtual address:0x%lx, paddr:0x%lx\n",
|
|
__func__, (unsigned long)*addr, *paddr);
|
|
*size = glctx.size;
|
|
*start = 0;
|
|
}
|
|
EXPORT_SYMBOL(get_gz_log_buffer);
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
static struct gz_trace_dump_t *gz_trace_add_dump_tail(
|
|
struct list_head *head, struct mutex *mux)
|
|
{
|
|
struct gz_trace_dump_t *entry;
|
|
|
|
entry = vzalloc(sizeof(struct gz_trace_dump_t));
|
|
if (entry) {
|
|
mutex_lock(mux);
|
|
list_add_tail(&entry->list, head);
|
|
mutex_unlock(mux);
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
static void gz_trace_free(struct list_head *head, struct mutex *mux)
|
|
{
|
|
struct gz_trace_dump_t *entry, *entry_tmp;
|
|
|
|
mutex_lock(mux);
|
|
list_for_each_entry_safe(entry, entry_tmp, head, list) {
|
|
list_del(&entry->list);
|
|
vfree(entry);
|
|
entry = NULL;
|
|
}
|
|
mutex_unlock(mux);
|
|
}
|
|
#endif
|
|
|
|
/* driver functions */
|
|
static int trusty_log_call_notify(struct notifier_block *nb,
|
|
unsigned long action, void *data)
|
|
{
|
|
struct gz_log_state *gls = container_of(nb, struct gz_log_state,
|
|
call_notifier);
|
|
|
|
if (action != TRUSTY_CALL_RETURNED)
|
|
return NOTIFY_DONE;
|
|
|
|
atomic_inc(&gls->gz_log_event_count);
|
|
wake_up_interruptible(&gls->gz_log_wq);
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static int trusty_log_panic_notify(struct notifier_block *nb,
|
|
unsigned long action, void *data)
|
|
{
|
|
struct gz_log_state *gls = container_of(nb, struct gz_log_state,
|
|
panic_notifier);
|
|
|
|
/*
|
|
* Don't grab the spin lock to hold up the panic notifier, even
|
|
* though this is racy.
|
|
*/
|
|
pr_info("trusty-log panic notifier - trusty version %s",
|
|
trusty_version_str_get(gls->trusty_dev));
|
|
atomic_inc(&gls->gz_log_event_count);
|
|
wake_up_interruptible(&gls->gz_log_wq);
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static bool trusty_supports_logging(struct device *device)
|
|
{
|
|
int ret;
|
|
|
|
ret = trusty_std_call32(device,
|
|
MTEE_SMCNR(SMCF_SC_SHARED_LOG_VERSION, device),
|
|
TRUSTY_LOG_API_VERSION, 0, 0);
|
|
if (ret == SM_ERR_UNDEFINED_SMC) {
|
|
pr_info("trusty-log not supported on secure side.\n");
|
|
return false;
|
|
} else if (ret < 0) {
|
|
pr_info("trusty std call (GZ_SHARED_LOG_VERSION) failed: %d\n",
|
|
ret);
|
|
return false;
|
|
}
|
|
|
|
if (ret == TRUSTY_LOG_API_VERSION) {
|
|
pr_info("trusty-log API supported: %d\n", ret);
|
|
return true;
|
|
}
|
|
|
|
pr_info("trusty-log unsupported api version: %d, supported: %d\n",
|
|
ret, TRUSTY_LOG_API_VERSION);
|
|
return false;
|
|
}
|
|
|
|
static int log_read_line(struct gz_log_state *s, int put, int get)
|
|
{
|
|
struct log_rb *log = s->log;
|
|
int i;
|
|
char c = '\0';
|
|
size_t max_to_read =
|
|
min((size_t)(put - get), sizeof(s->line_buffer) - 1);
|
|
size_t mask = log->sz - 1;
|
|
|
|
for (i = 0; i < max_to_read && c != '\n';)
|
|
s->line_buffer[i++] = c = log->data[get++ & mask];
|
|
|
|
s->line_buffer[i] = '\0';
|
|
|
|
return i;
|
|
}
|
|
|
|
static int is_buf_empty(struct gz_log_state *gls)
|
|
{
|
|
struct log_rb *log = gls->log;
|
|
uint32_t get, put;
|
|
|
|
get = gls->get_proc;
|
|
put = log->put;
|
|
return (get == put);
|
|
}
|
|
|
|
static int do_gz_log_read(struct gz_log_state *gls,
|
|
char __user *buf, size_t size)
|
|
{
|
|
struct log_rb *log = gls->log;
|
|
uint32_t get, put, alloc, read_chars = 0, copy_chars = 0;
|
|
int ret = 0;
|
|
|
|
if (!is_power_of_2(log->sz))
|
|
pr_info("[%s] Error log size 0x%x\n", __func__, log->sz);
|
|
|
|
/*
|
|
* For this ring buffer, at any given point, alloc >= put >= get.
|
|
* The producer side of the buffer is not locked, so the put and alloc
|
|
* pointers must be read in a defined order (put before alloc) so
|
|
* that the above condition is maintained. A read barrier is needed
|
|
* to make sure the hardware and compiler keep the reads ordered.
|
|
*/
|
|
get = gls->get_proc;
|
|
put = log->put;
|
|
/* make sure the hardware and compiler reads the correct put & alloc*/
|
|
rmb();
|
|
alloc = log->alloc;
|
|
|
|
if (alloc - get > log->sz) {
|
|
pr_notice("trusty: log overflow, lose some msg.");
|
|
get = alloc - log->sz;
|
|
}
|
|
|
|
if (get > put)
|
|
return -EFAULT;
|
|
|
|
if (is_buf_empty(gls))
|
|
return 0;
|
|
|
|
while (get != put) {
|
|
read_chars = log_read_line(gls, put, get);
|
|
/* Force the loads from log_read_line to complete. */
|
|
rmb();
|
|
if (copy_chars + read_chars > (uint32_t)size)
|
|
break;
|
|
|
|
ret = copy_to_user(buf + copy_chars, gls->line_buffer,
|
|
read_chars);
|
|
if (ret) {
|
|
pr_notice("[%s] copy_to_user failed ret %d\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
get += read_chars;
|
|
copy_chars += read_chars;
|
|
}
|
|
gls->get_proc = get;
|
|
|
|
return copy_chars;
|
|
}
|
|
|
|
static ssize_t gz_log_read(struct file *file, char __user *buf, size_t size,
|
|
loff_t *ppos)
|
|
{
|
|
struct gz_log_state *gls = PDE_DATA(file_inode(file));
|
|
int ret = 0;
|
|
|
|
/* sanity check */
|
|
if (!buf)
|
|
return -EINVAL;
|
|
|
|
if (atomic_xchg(&gls->readable, 0)) {
|
|
ret = do_gz_log_read(gls, buf, size);
|
|
gls->poll_event = atomic_read(&gls->gz_log_event_count);
|
|
atomic_set(&gls->readable, 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int gz_log_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct gz_log_state *gls = PDE_DATA(inode);
|
|
int ret;
|
|
|
|
ret = nonseekable_open(inode, file);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
gls->poll_event = atomic_read(&gls->gz_log_event_count);
|
|
return 0;
|
|
}
|
|
|
|
static int gz_log_release(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int gz_log_poll(struct file *file, poll_table *wait)
|
|
{
|
|
struct gz_log_state *gls = PDE_DATA(file_inode(file));
|
|
int mask = 0;
|
|
|
|
if (!is_buf_empty(gls))
|
|
return POLLIN | POLLRDNORM;
|
|
|
|
poll_wait(file, &gls->gz_log_wq, wait);
|
|
|
|
if (gls->poll_event != atomic_read(&gls->gz_log_event_count))
|
|
mask |= POLLIN | POLLRDNORM;
|
|
return mask;
|
|
}
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
/* example */
|
|
/* ====> GZ (4)[ 67.128605]GZT svc-< session 0 */
|
|
#define T_SVC_E_STR "GZT svc->"
|
|
#define T_SVC_E_SZ strlen(T_SVC_E_STR)
|
|
#define T_SVC_L_STR "GZT svc-<"
|
|
#define T_SVC_L_SZ strlen(T_SVC_L_STR)
|
|
#define T_DBG_STR "GZT dbg"
|
|
#define T_DBG_SZ strlen(T_DBG_STR)
|
|
#define T_OFFSET strlen("====> GZ (4)[ 53.137165]")
|
|
#define CPUSTR_OFFSET strlen("====> GZ (")
|
|
|
|
|
|
#define SESSION_MSG "0"
|
|
#define SESSION_SZ strlen(SESSION_MSG)
|
|
#define SESSION_OFFSET strlen("====> GZ (4)[ 67.128605]GZT svc-< session ")
|
|
|
|
|
|
enum gz_trace_type_t {
|
|
GZ_TRACE_DEBUG = 0,
|
|
GZ_TRACE_SVC_ENTER,
|
|
GZ_TRACE_SVC_LEAVE,
|
|
};
|
|
|
|
/* arch counter is 13M, mult is 161319385, shift is 21 */
|
|
static inline uint64_t arch_counter_to_ns(uint64_t cyc)
|
|
{
|
|
#define ARCH_TIMER_MULT 161319385
|
|
#define ARCH_TIMER_SHIFT 21
|
|
return (cyc * ARCH_TIMER_MULT) >> ARCH_TIMER_SHIFT;
|
|
}
|
|
|
|
static void correct_to_kernel_time(u64 time_us_base, u64 diff_cyc,
|
|
char *output, size_t output_sz)
|
|
{
|
|
u64 cyc2ns_diff;
|
|
u64 cyc2us_diff;
|
|
u64 ktime_us;
|
|
u64 gzlog_ktime_us;
|
|
u64 gzlog_ktime_s;
|
|
|
|
cyc2ns_diff = arch_counter_to_ns(diff_cyc);
|
|
cyc2us_diff = div_u64(cyc2ns_diff, NSEC_PER_USEC);
|
|
|
|
ktime_us = time_us_base - cyc2us_diff;
|
|
|
|
div_u64_rem(ktime_us, USEC_PER_SEC, (u32 *)&gzlog_ktime_us);
|
|
gzlog_ktime_s = div_u64(ktime_us, USEC_PER_SEC);
|
|
|
|
snprintf(output, output_sz, "%llu.%06u", gzlog_ktime_s, (u32)gzlog_ktime_us);
|
|
}
|
|
|
|
static void gz_trace_svc_msg(char *msg_raw, char session, char type,
|
|
struct gz_trace_dump_t *trace_dump_info)
|
|
{
|
|
char *msg_cpu;
|
|
char *msg_tmp;
|
|
char *msg_session;
|
|
char *msg_srvname;
|
|
char *msg_cntvct;
|
|
char msg_ktime[32] = {0};
|
|
u64 gz_cntvnt;
|
|
int err;
|
|
|
|
if (session == '0')
|
|
return;
|
|
|
|
msg_cpu = msg_raw + CPUSTR_OFFSET;
|
|
msg_tmp = msg_raw + SESSION_OFFSET;
|
|
|
|
msg_session = strsep(&msg_tmp, " ");
|
|
if (!msg_session)
|
|
return;
|
|
|
|
msg_srvname = strsep(&msg_tmp, " ");
|
|
if (!msg_srvname)
|
|
return;
|
|
|
|
msg_cntvct = strsep(&msg_tmp, " ");
|
|
if (!msg_cntvct)
|
|
return;
|
|
err = kstrtou64(msg_cntvct, 10, &gz_cntvnt);
|
|
if (err || gz_cntvnt > trace_dump_info->cntvct_base)
|
|
return;
|
|
|
|
correct_to_kernel_time(
|
|
div_u64(trace_dump_info->ktime_base, NSEC_PER_USEC),
|
|
trace_dump_info->cntvct_base - gz_cntvnt,
|
|
msg_ktime, sizeof(msg_ktime));
|
|
|
|
#define GZ_TRACE_DUMP_RAW_DATA 0
|
|
#if !GZ_TRACE_DUMP_RAW_DATA
|
|
GZ_TRUSTY_TRACE_INJECTION("%c|%d|%s|%s|%c|%s",
|
|
type,
|
|
get_current()->pid,
|
|
strlen(msg_srvname) > 8 ?
|
|
msg_srvname + strlen(msg_srvname) - 8 :
|
|
msg_srvname,
|
|
msg_session,
|
|
*msg_cpu,
|
|
msg_ktime);
|
|
#else
|
|
GZ_TRUSTY_TRACE_INJECTION("%c|%d|%s|%s|%c|%s|%lu|%lu|%s",
|
|
type,
|
|
get_current()->pid,
|
|
strlen(msg_srvname) > 8 ?
|
|
msg_srvname + strlen(msg_srvname) - 8 :
|
|
msg_srvname,
|
|
msg_session,
|
|
*msg_cpu,
|
|
msg_ktime,
|
|
trace_dump_info->ktime_base,
|
|
trace_dump_info->cntvct_base,
|
|
msg_cntvct);
|
|
#endif
|
|
}
|
|
|
|
|
|
static void gz_trace_parse(struct gz_log_state *gls, u32 get, u32 put,
|
|
struct gz_trace_dump_t *trace_dump_info)
|
|
{
|
|
int i = 0;
|
|
size_t mask;
|
|
char msg_raw[TRUSTY_LINE_BUFFER_SIZE];
|
|
size_t max_to_read;
|
|
|
|
mask = gls->log->sz - 1;
|
|
|
|
dev_dbg(gls->dev, "%s get(%d) put(%d), mask=0x%zx\n",
|
|
__func__, get, put, mask);
|
|
|
|
while (get < put) {
|
|
max_to_read = min((size_t)(put - get), sizeof(msg_raw) - 1);
|
|
for (i = 0 ; i < max_to_read ; i++) {
|
|
msg_raw[i] = gls->log->data[get++ & mask];
|
|
if (msg_raw[i] == '\n')
|
|
break;
|
|
}
|
|
msg_raw[i] = '\0';
|
|
|
|
if (strncmp(msg_raw + T_OFFSET, T_SVC_E_STR, T_SVC_E_SZ) == 0)
|
|
gz_trace_svc_msg(msg_raw, *(char *)(msg_raw + SESSION_OFFSET),
|
|
'S', trace_dump_info);
|
|
else if (strncmp(msg_raw + T_OFFSET, T_SVC_L_STR, T_SVC_L_SZ) == 0)
|
|
gz_trace_svc_msg(msg_raw, *(char *)(msg_raw + SESSION_OFFSET),
|
|
'F', trace_dump_info);
|
|
//else if (strncmp(msg_raw + T_OFFSET, T_DBG_STR, T_DBG_SZ) == 0)
|
|
// trace_type = GZ_TRACE_DEBUG;
|
|
else
|
|
continue;
|
|
|
|
}
|
|
}
|
|
|
|
static int gz_trace_task_entry(void *data)
|
|
{
|
|
struct gz_log_state *gls = (struct gz_log_state *)data;
|
|
struct gz_trace_dump_t *trace_dump_info_src;
|
|
struct gz_trace_dump_t trace_dump_info_use;
|
|
uint32_t get;
|
|
uint32_t put;
|
|
long timeout = MAX_SCHEDULE_TIMEOUT;
|
|
|
|
|
|
if (!gls || !gls->log)
|
|
return -ENOMEM;
|
|
|
|
dev_info(gls->dev, "%s->\n", __func__);
|
|
|
|
while (!kthread_should_stop()) {
|
|
wait_for_completion_timeout(&gls->trace_dump_event, timeout);
|
|
|
|
memset((void *)&trace_dump_info_use, 0, sizeof(trace_dump_info_use));
|
|
mutex_lock(&gls->gz_trace_dump_mux);
|
|
trace_dump_info_src = list_first_entry_or_null(&gls->gz_trace_dump_list,
|
|
struct gz_trace_dump_t, list);
|
|
if (trace_dump_info_src) {
|
|
memcpy((void *)&trace_dump_info_use, trace_dump_info_src,
|
|
sizeof(trace_dump_info_use));
|
|
list_del(&trace_dump_info_src->list);
|
|
vfree(trace_dump_info_src);
|
|
}
|
|
mutex_unlock(&gls->gz_trace_dump_mux);
|
|
|
|
if (trace_dump_info_use.put) {
|
|
get = gls->get_trace;
|
|
put = trace_dump_info_use.put;
|
|
|
|
if (get > put)
|
|
dev_info(gls->dev, "%s get(%u)>put(%u)\n", __func__, get, put);
|
|
else if (get < put)
|
|
gz_trace_parse(gls, get, put, &trace_dump_info_use);
|
|
|
|
gls->get_trace = put;
|
|
}
|
|
if (gls->trace_exit)
|
|
timeout = msecs_to_jiffies(1000);
|
|
}
|
|
dev_info(gls->dev, "%s<-\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int trusty_log_callback_notify(struct notifier_block *nb,
|
|
unsigned long action, void *data)
|
|
{
|
|
if (action == TRUSTY_CALLBACK_SYSTRACE) {
|
|
struct gz_log_state *gls = container_of(nb, struct gz_log_state,
|
|
callback_notifier);
|
|
struct gz_trace_dump_t *trace_dump_info;
|
|
|
|
if (atomic_read(&gls->gz_trace_onoff)) {
|
|
trace_dump_info = gz_trace_add_dump_tail(&gls->gz_trace_dump_list,
|
|
&gls->gz_trace_dump_mux);
|
|
if (trace_dump_info) {
|
|
trace_dump_info->ktime_base = ktime_get();
|
|
trace_dump_info->cntvct_base = arch_counter_get_cntvct();
|
|
trace_dump_info->put = gls->log->put;
|
|
complete(&gls->trace_dump_event);
|
|
} else
|
|
gls->get_trace = gls->log->put;
|
|
} else
|
|
gls->get_trace = gls->log->put;
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static ssize_t gz_trace_on_write(struct file *filp, const char __user *ubuf,
|
|
size_t cnt, loff_t *fpos)
|
|
{
|
|
struct seq_file *s = filp->private_data;
|
|
struct gz_log_state *gls = s->private;
|
|
char buf[2];
|
|
|
|
if (cnt > sizeof(buf))
|
|
return -EINVAL;
|
|
|
|
if (copy_from_user(buf, ubuf, cnt))
|
|
return -EFAULT;
|
|
|
|
buf[cnt-1] = 0;
|
|
|
|
if (buf[0] == '0')
|
|
atomic_set(&gls->gz_trace_onoff, 0);
|
|
else if (buf[0] == '1')
|
|
atomic_set(&gls->gz_trace_onoff, 1);
|
|
else
|
|
return -EFAULT;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
static int gz_trace_on_read(struct seq_file *s, void *unused)
|
|
{
|
|
struct gz_log_state *gls = s->private;
|
|
|
|
seq_printf(s, "%d\n", atomic_read(&gls->gz_trace_onoff));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gz_trace_on_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, gz_trace_on_read, inode->i_private);
|
|
}
|
|
|
|
|
|
static const struct file_operations gz_trace_on_fops = {
|
|
.open = gz_trace_on_open,
|
|
.write = gz_trace_on_write,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
#endif /* ENABLE_GZ_TRACE_DUMP */
|
|
|
|
static const struct file_operations proc_gz_log_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = gz_log_open,
|
|
.read = gz_log_read,
|
|
.release = gz_log_release,
|
|
.poll = gz_log_poll,
|
|
};
|
|
|
|
static int trusty_gz_send_ktime(struct platform_device *pdev)
|
|
{
|
|
uint64_t current_ktime = 0;
|
|
uint64_t current_cnt = 0;
|
|
uint64_t diff_all;
|
|
uint32_t diff_msb;
|
|
uint32_t diff_lsb;
|
|
|
|
current_ktime = sched_clock();
|
|
current_cnt = arch_counter_get_cntvct();
|
|
diff_all = current_cnt - div64_u64((13 * current_ktime), 1000);
|
|
diff_msb = (diff_all >> 32);
|
|
diff_lsb = (diff_all & U32_MAX);
|
|
|
|
trusty_fast_call32(pdev->dev.parent,
|
|
MTEE_SMCNR(MT_SMCF_FC_KTIME_ALIGN, pdev->dev.parent),
|
|
diff_msb, diff_lsb, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int trusty_gz_log_resume(struct platform_device *pdev)
|
|
{
|
|
return trusty_gz_send_ktime(pdev);
|
|
}
|
|
|
|
static int trusty_gz_log_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
struct gz_log_state *gls = NULL;
|
|
struct device_node *pnode = pdev->dev.parent->of_node;
|
|
int tee_id = 0;
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
uint32_t mask;
|
|
int cpu;
|
|
#endif
|
|
|
|
trusty_gz_send_ktime(pdev);
|
|
|
|
if (!trusty_supports_logging(pdev->dev.parent))
|
|
return -ENXIO;
|
|
|
|
ret = of_property_read_u32(pnode, "tee-id", &tee_id);
|
|
if (ret != 0)
|
|
dev_info(&pdev->dev, "tee_id is not set\n");
|
|
else
|
|
dev_info(&pdev->dev, "--- init gz-log for MTEE %d ---\n",
|
|
tee_id);
|
|
|
|
gz_log_page_init();
|
|
gls = kzalloc(sizeof(*gls), GFP_KERNEL);
|
|
if (!gls) {
|
|
ret = -ENOMEM;
|
|
goto error_alloc_state;
|
|
}
|
|
|
|
mutex_init(&gls->lock);
|
|
gls->dev = &pdev->dev;
|
|
gls->trusty_dev = gls->dev->parent;
|
|
gls->tee_id = tee_id;
|
|
gls->get_proc = 0;
|
|
|
|
/* STATIC: memlog already is added at preloader stage.
|
|
* DYNAMIC: add memlog as usual.
|
|
*/
|
|
if (glctx.flag == DYNAMIC) {
|
|
ret = trusty_std_call32(gls->trusty_dev,
|
|
MTEE_SMCNR(SMCF_SC_SHARED_LOG_ADD, gls->trusty_dev),
|
|
(u32)(glctx.paddr), (u32)((u64)glctx.paddr >> 32),
|
|
glctx.size);
|
|
if (ret < 0) {
|
|
dev_info(&pdev->dev,
|
|
"std call(GZ_SHARED_LOG_ADD) failed: %d %pa\n",
|
|
ret, &glctx.paddr);
|
|
goto error_std_call;
|
|
}
|
|
}
|
|
|
|
gls->log = glctx.virt;
|
|
dev_info(&pdev->dev, "gls->log virtual address:%p\n", gls->log);
|
|
if (!gls->log) {
|
|
ret = -ENOMEM;
|
|
goto error_alloc_log;
|
|
}
|
|
glctx.gls = gls;
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
gls->callback_notifier.notifier_call = trusty_log_callback_notify;
|
|
ret = trusty_callback_notifier_register(gls->trusty_dev,
|
|
&gls->callback_notifier);
|
|
if (ret < 0) {
|
|
dev_info(&pdev->dev,
|
|
"can not register trusty callback notifier\n");
|
|
goto error_callback_notifier;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&gls->gz_trace_dump_list);
|
|
mutex_init(&gls->gz_trace_dump_mux);
|
|
gls->trace_exit = false;
|
|
gls->get_trace = 0;
|
|
atomic_set(&gls->gz_trace_onoff, 0);
|
|
init_completion(&gls->trace_dump_event);
|
|
gls->trace_task_fd =
|
|
kthread_run(gz_trace_task_entry, (void *)gls, "gz_trace");
|
|
if (IS_ERR(gls->trace_task_fd)) {
|
|
dev_info(&pdev->dev, "%s unable create kthread\n", __func__);
|
|
ret = PTR_ERR(gls->trace_task_fd);
|
|
goto error_trace_task_run;
|
|
}
|
|
set_user_nice(gls->trace_task_fd, 5);
|
|
mask = (u32)trusty_fast_call32(gls->trusty_dev,
|
|
MTEE_SMCNR(SMCF_FC_GET_CMASK, gls->trusty_dev),
|
|
0, 0, 0);
|
|
dev_info(&pdev->dev, "%s mask=0x%x\n", __func__, mask);
|
|
if ((mask != U32_MAX) && (mask != 0x0)) {
|
|
struct cpumask task_cmask;
|
|
|
|
mask = ~mask;
|
|
dev_info(&pdev->dev, "%s bind mask=0x%x\n", __func__, mask);
|
|
cpumask_clear(&task_cmask);
|
|
for_each_possible_cpu(cpu) {
|
|
if (cpu > 31) {
|
|
dev_info(&pdev->dev,
|
|
"%s not support cpu# > 32\n",
|
|
__func__);
|
|
continue;
|
|
}
|
|
if (mask & (1<<cpu))
|
|
cpumask_set_cpu(cpu, &task_cmask);
|
|
}
|
|
|
|
if (!cpumask_empty(&task_cmask))
|
|
set_cpus_allowed_ptr(gls->trace_task_fd, &task_cmask);
|
|
}
|
|
#endif
|
|
|
|
gls->call_notifier.notifier_call = trusty_log_call_notify;
|
|
ret = trusty_call_notifier_register(gls->trusty_dev,
|
|
&gls->call_notifier);
|
|
if (ret < 0) {
|
|
dev_info(&pdev->dev,
|
|
"can not register trusty call notifier\n");
|
|
goto error_call_notifier;
|
|
}
|
|
|
|
gls->panic_notifier.notifier_call = trusty_log_panic_notify;
|
|
ret = atomic_notifier_chain_register(&panic_notifier_list,
|
|
&gls->panic_notifier);
|
|
if (ret < 0) {
|
|
dev_info(&pdev->dev, "failed to register panic notifier\n");
|
|
goto error_panic_notifier;
|
|
}
|
|
init_waitqueue_head(&gls->gz_log_wq);
|
|
atomic_set(&gls->gz_log_event_count, 0);
|
|
atomic_set(&gls->readable, 1);
|
|
platform_set_drvdata(pdev, gls);
|
|
|
|
/* create /proc/gz_log */
|
|
gls->proc = proc_create_data("gz_log", 0440, NULL, &proc_gz_log_fops,
|
|
gls);
|
|
if (!gls->proc) {
|
|
dev_info(&pdev->dev, "gz_log proc_create failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
gls->gz_log_dbg_root = debugfs_create_dir("gz_log", NULL);
|
|
gls->sys_gz_trace_on =
|
|
debugfs_create_file("gz_trace_on", 0644, gls->gz_log_dbg_root,
|
|
gls, &gz_trace_on_fops);
|
|
if (!gls->sys_gz_trace_on) {
|
|
dev_info(&pdev->dev, "gz_trace_on node failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
error_panic_notifier:
|
|
trusty_call_notifier_unregister(gls->trusty_dev, &gls->call_notifier);
|
|
error_call_notifier:
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
gls->trace_exit = true;
|
|
kthread_stop(gls->trace_task_fd);
|
|
error_trace_task_run:
|
|
trusty_callback_notifier_unregister(gls->trusty_dev,
|
|
&gls->callback_notifier);
|
|
error_callback_notifier:
|
|
#endif
|
|
trusty_std_call32(gls->trusty_dev,
|
|
MTEE_SMCNR(SMCF_SC_SHARED_LOG_RM, gls->trusty_dev),
|
|
(u32)glctx.paddr, (u32)((u64)glctx.paddr >> 32), 0);
|
|
error_std_call:
|
|
if (glctx.flag == STATIC_NOMAP)
|
|
memunmap(glctx.virt);
|
|
else if (glctx.flag == DYNAMIC)
|
|
kfree(glctx.virt);
|
|
error_alloc_log:
|
|
mutex_destroy(&gls->lock);
|
|
kfree(gls);
|
|
error_alloc_state:
|
|
return ret;
|
|
}
|
|
|
|
static int trusty_gz_log_remove(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct gz_log_state *gls = platform_get_drvdata(pdev);
|
|
|
|
dev_info(&pdev->dev, "%s\n", __func__);
|
|
|
|
proc_remove(gls->proc);
|
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
|
&gls->panic_notifier);
|
|
trusty_call_notifier_unregister(gls->trusty_dev, &gls->call_notifier);
|
|
|
|
#if ENABLE_GZ_TRACE_DUMP
|
|
gz_trace_free(&gls->gz_trace_dump_list, &gls->gz_trace_dump_mux);
|
|
gls->trace_exit = true;
|
|
complete_all(&gls->trace_dump_event);
|
|
kthread_stop(gls->trace_task_fd);
|
|
trusty_callback_notifier_unregister(gls->trusty_dev,
|
|
&gls->callback_notifier);
|
|
#endif
|
|
|
|
ret = trusty_std_call32(gls->trusty_dev,
|
|
MTEE_SMCNR(SMCF_SC_SHARED_LOG_RM, gls->trusty_dev),
|
|
(u32)glctx.paddr, (u32)((u64)glctx.paddr >> 32), 0);
|
|
if (ret)
|
|
pr_info("std call(GZ_SHARED_LOG_RM) failed: %d\n", ret);
|
|
|
|
if (glctx.flag == STATIC_NOMAP)
|
|
memunmap(glctx.virt);
|
|
else if (glctx.flag == DYNAMIC)
|
|
kfree(glctx.virt);
|
|
|
|
mutex_destroy(&gls->lock);
|
|
kfree(gls);
|
|
memset(&glctx, 0, sizeof(glctx));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id trusty_gz_of_match[] = {
|
|
{ .compatible = "android,trusty-gz-log-v1", },
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver trusty_gz_log_driver = {
|
|
.probe = trusty_gz_log_probe,
|
|
.resume = trusty_gz_log_resume,
|
|
.remove = trusty_gz_log_remove,
|
|
.driver = {
|
|
.name = "trusty-gz-log",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = trusty_gz_of_match,
|
|
},
|
|
};
|
|
|
|
static __init int trusty_gz_log_init(void)
|
|
{
|
|
return platform_driver_register(&trusty_gz_log_driver);
|
|
}
|
|
|
|
static void __exit trusty_gz_log_exit(void)
|
|
{
|
|
platform_driver_unregister(&trusty_gz_log_driver);
|
|
}
|
|
|
|
arch_initcall(trusty_gz_log_init);
|
|
module_exit(trusty_gz_log_exit);
|
|
/*module_platform_driver(trusty_gz_log_driver);*/
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|