1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-09-26 19:04:54 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

481 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015-2019, MICROTRUST Incorporated
* Copyright (C) 2015 Google, Inc.
*
*/
#define IMSG_TAG "[tz_log]"
#include <imsg_log.h>
#include <linux/platform_device.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/log2.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <asm/page.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <teei_client_main.h>
#include "tz_log.h"
#include "nt_smc_call.h"
#include "notify_queue.h"
#include "switch_queue.h"
#include "utdriver_macro.h"
#include "log_perf.h"
#define TEE_LOG_TYPE 0x10
struct tz_log_state *g_tz_log_state;
static struct completion teei_log_comp;
int init_tlog_comp_fn(void)
{
init_completion(&teei_log_comp);
return 0;
}
void teei_notify_log_fn(void)
{
if (!completion_done(&teei_log_comp))
complete(&teei_log_comp);
}
static int __tz_driver_read_logs(struct tz_log_state *s, char *buffer,
uint32_t get, unsigned int cnt)
{
struct log_rb *log = s->log;
int i = 0;
size_t mask = log->sz - 1;
for (i = 0; i < cnt;)
buffer[i++] = log->data[get++ & mask];
return i;
}
int tz_driver_read_logs(char *buffer, unsigned long count)
{
struct tz_log_state *local_s = NULL;
uint32_t get = 0;
uint32_t put = 0;
uint32_t alloc = 0;
int read_chars = 0;
struct log_rb *log = NULL;
unsigned int real_cnt = 0;
local_s = g_tz_log_state;
log = local_s->log;
get = local_s->read_get;
put = log->put;
if (put != get) {
alloc = log->alloc;
if ((alloc - get) > log->sz) {
IMSG_INFO("log overflow.\n");
get = alloc - log->sz;
}
if ((put - get) > (unsigned int)count)
real_cnt = (unsigned int)count;
else
real_cnt = put - get;
read_chars = __tz_driver_read_logs(local_s,
buffer, get, real_cnt);
get += read_chars;
}
local_s->read_get = get;
return read_chars;
}
static int log_read_line(struct tz_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 void tz_driver_dump_logs(struct tz_log_state *s)
{
struct log_rb *log = s->log;
struct boot_log_rb *boot_log = s->boot_log;
uint32_t get, put, alloc;
int read_chars;
static DEFINE_RATELIMIT_STATE(_rs,
TZ_LOG_RATELIMIT_INTERVAL,
TZ_LOG_RATELIMIT_BURST);
if (unlikely(log->put == 0)) {
IMSG_DEBUG("TEE log buffer not ready yet\n");
return;
}
WARN_ON(!is_power_of_2(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 = s->get;
while ((put = log->put) != get) {
/* Make sure that the read of put occurs */
/* before the read of log data */
rmb();
/* Read a line from the log */
read_chars = log_read_line(s, put, get);
/* Force the loads from log_read_line to complete. */
rmb();
alloc = log->alloc;
/*
* Discard the line that was just read if the data could
* have been corrupted by the producer.
*/
if (alloc - get > log->sz) {
IMSG_ERROR("log overflow.");
get = alloc - log->sz;
continue;
}
/*
* Due to UART speed is slow, printing large number of
* messages to UART will cause system reset by watchdog.
* It can prevent from printing message to uart by using
* KERN_DEBUG log level if with default print setting.
* (default setting is print message to uart
* if log level >= KERN_INFO)
*/
if (likely(is_teei_ready()))
IMSG_PRINTK("[TZ_LOG] %s", s->line_buffer);
else
IMSG_PRINTK("[TZ_LOG] %s", s->line_buffer);
/*
* Dump early log to boot log buffer
* until boot log buffer is full
*/
if (boot_log->put + read_chars < boot_log->sz) {
char *log_buf = &boot_log->data[boot_log->put];
memcpy(log_buf, s->line_buffer, read_chars);
boot_log->put += read_chars;
}
/* Print warning message */
/* if log output frequency is over rate limit */
__ratelimit(&_rs);
get += read_chars;
#if ENABLE_LOG_PERF == 1
measure_log_perf(IMSG_TAG, read_chars,
*(uint32_t *)(s->line_buffer));
#endif
}
s->get = get;
}
int teei_log_fn(void *work)
{
int retVal = 0;
#if IS_ENABLED(CONFIG_MICROTRUST_TZ_LOG)
struct tz_log_state *s;
s = g_tz_log_state;
#endif
while (1) {
if (switch_input_index == switch_output_index) {
retVal = wait_for_completion_interruptible(
&teei_log_comp);
if (retVal != 0)
continue;
}
#if IS_ENABLED(CONFIG_MICROTRUST_TZ_LOG)
msleep(20);
tz_driver_dump_logs(s);
#endif
}
return NOTIFY_OK;
}
static int tz_log_panic_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct tz_log_state *s;
/*
* Don't grab the spin lock to hold up the panic notifier, even
* though this is racy.
*/
s = container_of(nb, struct tz_log_state, panic_notifier);
IMSG_INFO("tz log panic notifier\n");
tz_driver_dump_logs(s);
return NOTIFY_OK;
}
#ifdef ENABLED_TEEI_BOOT_LOG
static struct tz_log_state *get_tz_log_state(void)
{
struct tz_driver_state *drv_state = get_tz_drv_state();
struct platform_device *pdev = drv_state->tz_log_pdev;
return pdev->dev.platform_data;
}
static void *boot_log_seq_start(struct seq_file *f, loff_t *pos)
{
struct tz_log_state *s = get_tz_log_state();
struct boot_log_rb *log = s->boot_log;
if (*pos >= log->put)
return NULL;
return (void *)log;
}
static void *boot_log_seq_next(struct seq_file *f, void *v, loff_t *pos)
{
struct boot_log_rb *log = v;
if (*pos >= log->put)
return NULL;
*pos = log->get;
return v;
}
static void boot_log_seq_stop(struct seq_file *f, void *v)
{
}
static int boot_log_read_line(struct boot_log_rb *log, char *out)
{
int put = log->put;
int get = log->get;
int i;
char c = '\0';
size_t max_to_read = min((size_t)(put - get),
(size_t)TZ_LINE_BUFFER_SIZE - 1);
size_t mask = log->sz - 1;
for (i = 0; i < max_to_read && c != '\n';)
out[i++] = c = log->data[get++ & mask];
out[i] = '\0';
return i;
}
static int boot_log_seq_show(struct seq_file *f, void *v)
{
struct boot_log_rb *log = v;
char line_buffer[TZ_LINE_BUFFER_SIZE];
uint32_t read_chars;
read_chars = boot_log_read_line(log, line_buffer);
if (read_chars) {
seq_printf(f, "%s", line_buffer);
log->get += read_chars;
}
return 0;
}
static const struct seq_operations boot_log_seq_ops = {
.start = boot_log_seq_start,
.next = boot_log_seq_next,
.stop = boot_log_seq_stop,
.show = boot_log_seq_show
};
static int boot_log_open(struct inode *inode, struct file *file)
{
struct tz_log_state *s = get_tz_log_state();
struct boot_log_rb *log = s->boot_log;
log->get = 0;
return seq_open(file, &boot_log_seq_ops);
};
static const struct file_operations boot_log_fops = {
.open = boot_log_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
static struct dentry *root_entry;
static int tz_log_debugfs_init(void)
{
root_entry = debugfs_create_dir("tz_log", NULL);
if (!root_entry) {
IMSG_WARN("Can not create tz_log debugfs\n");
return -1;
}
debugfs_create_file("boot_log", 0444, root_entry, NULL, &boot_log_fops);
return 0;
}
#endif
int teei_change_log_status(unsigned long new_status)
{
struct completion *wait_completion = NULL;
int retVal = 0;
teei_cpus_read_lock();
wait_completion = kmalloc(sizeof(struct completion), GFP_KERNEL);
if (wait_completion == NULL) {
IMSG_ERROR("TEEI: Failed to alloc completion[%s]\n", __func__);
teei_cpus_read_unlock();
return -ENOMEM;
}
init_completion(wait_completion);
retVal = add_work_entry(SMC_CALL_TYPE, N_INVOKE_T_NQ, 0, 0, 0);
if (retVal != 0) {
IMSG_ERROR("TEEI: Failed to add_work_entry[%s]\n", __func__);
kfree(wait_completion);
teei_cpus_read_unlock();
return retVal;
}
retVal = add_nq_entry(TEEI_MODIFY_TEE_CONFIG, TEE_LOG_TYPE,
(unsigned long long)(wait_completion),
new_status, 0, 0);
if (retVal != 0) {
IMSG_ERROR("TEEI: Failed to add one nq to n_t_buffer\n");
kfree(wait_completion);
teei_cpus_read_unlock();
return retVal;
}
teei_notify_switch_fn();
wait_for_completion(wait_completion);
kfree(wait_completion);
teei_cpus_read_unlock();
return 0;
}
int tz_log_probe(struct platform_device *pdev)
{
struct tz_log_state *s;
int result;
IMSG_DEBUG("%s\n", __func__);
s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
result = -ENOMEM;
goto error_alloc_state;
}
g_tz_log_state = s;
spin_lock_init(&s->lock);
s->dev = &pdev->dev;
s->get = 0;
s->read_get = 0;
s->log_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO | GFP_DMA,
get_order(TZ_LOG_SIZE));
if (!s->log_pages) {
result = -ENOMEM;
goto error_alloc_log;
}
s->log = page_address(s->log_pages);
s->boot_log_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO | GFP_DMA,
get_order(TZ_LOG_SIZE));
if (!s->boot_log_pages) {
result = -ENOMEM;
goto error_alloc_boot_log;
}
s->boot_log = page_address(s->boot_log_pages);
s->boot_log->put = 0;
s->boot_log->sz = rounddown_pow_of_two(
TZ_LOG_SIZE - sizeof(struct boot_log_rb));
s->panic_notifier.notifier_call = tz_log_panic_notify;
result = atomic_notifier_chain_register(&panic_notifier_list,
&s->panic_notifier);
if (result < 0) {
IMSG_ERROR("failed to register panic notifier\n");
goto error_panic_notifier;
}
platform_device_add_data(pdev, s, sizeof(struct tz_log_state));
#ifdef ENABLED_TEEI_BOOT_LOG
tz_log_debugfs_init();
#endif
return 0;
error_panic_notifier:
__free_pages(s->boot_log_pages, get_order(TZ_LOG_SIZE));
error_alloc_boot_log:
__free_pages(s->log_pages, get_order(TZ_LOG_SIZE));
error_alloc_log:
kfree(s);
g_tz_log_state = NULL;
error_alloc_state:
return result;
}
int tz_log_remove(struct platform_device *pdev)
{
struct tz_log_state *s = dev_get_platdata(&pdev->dev);
IMSG_DEBUG("%s\n", __func__);
atomic_notifier_chain_unregister(&panic_notifier_list,
&s->panic_notifier);
__free_pages(s->log_pages, get_order(TZ_LOG_SIZE));
__free_pages(s->boot_log_pages, get_order(TZ_LOG_SIZE));
kfree(s);
g_tz_log_state = NULL;
return 0;
}