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>
1517 lines
36 KiB
C
1517 lines
36 KiB
C
/*
|
|
* Copyright (C) 2022 Huawei Technologies Co., Ltd.
|
|
* Decription: TEE Logging Subsystem, read the tee os log from log memory
|
|
*
|
|
* 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 "tlogger.h"
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/version.h>
|
|
#include <linux/delay.h>
|
|
#include <asm/ioctls.h>
|
|
#include <linux/syscalls.h>
|
|
#include <securec.h>
|
|
#include <asm/io.h>
|
|
#include "smc_smp.h"
|
|
#include "mailbox_mempool.h"
|
|
#include "teek_client_constants.h"
|
|
#include "tc_ns_client.h"
|
|
#include "teek_ns_client.h"
|
|
#include "log_cfg_api.h"
|
|
#include "tc_ns_log.h"
|
|
#include "ko_adapt.h"
|
|
#include "internal_functions.h"
|
|
#ifdef CONFIG_LOG_POOL
|
|
#include "shared_mem.h"
|
|
#endif
|
|
#ifdef CONFIG_TEE_REBOOT
|
|
#include "reboot.h"
|
|
#endif
|
|
|
|
/* for log item ----------------------------------- */
|
|
#define LOG_ITEM_MAGIC 0x5A5A
|
|
#define LOG_ITEM_LEN_ALIGN 64
|
|
#define LOG_ITEM_MAX_LEN 1024
|
|
#define LOG_READ_STATUS_ERROR 0x000FFFF
|
|
|
|
/* =================================================== */
|
|
#define LOGGER_LOG_TEEOS "teelog" /* tee os log */
|
|
#define LOGGERIOCTL 0xBE /* for ioctl */
|
|
|
|
#define DUMP_START_MAGIC "Dump SPI notification"
|
|
#define DUMP_END_MAGIC "Dump task states END"
|
|
|
|
#define GET_VERSION_BASE 5
|
|
#define SET_READERPOS_CUR_BASE 6
|
|
#define SET_TLOGCAT_STAT_BASE 7
|
|
#define GET_TLOGCAT_STAT_BASE 8
|
|
|
|
/* get tee verison */
|
|
#define MAX_TEE_VERSION_LEN 256
|
|
#define TEELOGGER_GET_VERSION \
|
|
_IOR(LOGGERIOCTL, GET_VERSION_BASE, char[MAX_TEE_VERSION_LEN])
|
|
/* set the log reader pos to current pos */
|
|
#define TEELOGGER_SET_READERPOS_CUR \
|
|
_IO(LOGGERIOCTL, SET_READERPOS_CUR_BASE)
|
|
#define TEELOGGER_SET_TLOGCAT_STAT \
|
|
_IO(LOGGERIOCTL, SET_TLOGCAT_STAT_BASE)
|
|
#define TEELOGGER_GET_TLOGCAT_STAT \
|
|
_IO(LOGGERIOCTL, GET_TLOGCAT_STAT_BASE)
|
|
|
|
#ifdef CONFIG_LOG_POOL
|
|
struct teelogger_log_pool {
|
|
uint64_t addr;
|
|
uint64_t size;
|
|
};
|
|
|
|
#define GET_LOG_POOL_BASE 9
|
|
#define LOG_POOL_APPEND_BASE 10
|
|
|
|
#define TEELOGGER_GET_LOG_POOL \
|
|
_IOWR(LOGGERIOCTL, GET_LOG_POOL_BASE, uint64_t)
|
|
#define TEELOGGER_LOG_POOL_APPEND \
|
|
_IOWR(LOGGERIOCTL, LOG_POOL_APPEND_BASE, struct teelogger_log_pool)
|
|
#endif
|
|
|
|
int g_tlogcat_f = 0;
|
|
|
|
#ifndef CONFIG_TEE_LOG_ACHIVE_PATH
|
|
#define CONFIG_TEE_LOG_ACHIVE_PATH "/data/log/tee/last_teemsg"
|
|
#endif
|
|
#define TEE_LOG_FILE_NAME_MAX 256
|
|
|
|
uint32_t g_last_read_offset = 0;
|
|
|
|
#define NEVER_USED_LEN 32U
|
|
#define LOG_ITEM_RESERVED_LEN 1U
|
|
|
|
/* 64 byte head + user log */
|
|
struct log_item {
|
|
unsigned char never_used[NEVER_USED_LEN];
|
|
unsigned short magic;
|
|
unsigned short reserved0;
|
|
uint32_t serial_no;
|
|
unsigned short real_len; /* log real len */
|
|
unsigned short buffer_len; /* log buffer's len, multiple of 32 bytes */
|
|
unsigned char uuid[UUID_LEN];
|
|
unsigned char log_source_type;
|
|
unsigned char reserved[LOG_ITEM_RESERVED_LEN];
|
|
unsigned char log_level;
|
|
unsigned char new_line; /* '\n' char, easy viewing log in bbox.bin file */
|
|
unsigned char log_buffer[];
|
|
};
|
|
|
|
/* --- for log mem --------------------------------- */
|
|
#define TEMP_LOG_MEM_SIZE (10 * SZ_1K)
|
|
|
|
#define LOG_BUFFER_RESERVED_LEN 11U
|
|
#define VERSION_INFO_LEN 156U
|
|
|
|
/*
|
|
* Log's buffer flag info, size: 64 bytes head + 156 bytes's version info.
|
|
* For filed description:
|
|
* last_pos : current log's end position, last log's start position.
|
|
* write_loops: Write cyclically. Init value is 0, when memory is used
|
|
* up, the value add 1.
|
|
*/
|
|
struct log_buffer_flag {
|
|
uint32_t reserved0;
|
|
uint32_t last_pos;
|
|
uint32_t write_loops;
|
|
uint32_t log_level;
|
|
/* [0] is magic failed, [1] is serial_no failed, used fior log retention feature */
|
|
uint32_t reserved[LOG_BUFFER_RESERVED_LEN];
|
|
uint32_t max_len;
|
|
unsigned char version_info[VERSION_INFO_LEN];
|
|
};
|
|
|
|
struct log_buffer {
|
|
struct log_buffer_flag flag;
|
|
unsigned char buffer_start[];
|
|
};
|
|
|
|
static struct log_buffer *g_log_buffer = NULL;
|
|
|
|
struct tlogger_log {
|
|
unsigned char *buffer_info; /* ring buffer info */
|
|
struct mutex mutex_info; /* this mutex protects buffer_info */
|
|
wait_queue_head_t wait_queue_head; /* wait queue head for reader */
|
|
struct list_head logs; /* log channels list */
|
|
struct miscdevice misc_device; /* misc device log */
|
|
struct list_head readers; /* log's readers */
|
|
};
|
|
|
|
static LIST_HEAD(m_log_list);
|
|
|
|
struct tlogger_reader {
|
|
struct tlogger_log *log; /* tlogger_log info data */
|
|
struct list_head list; /* log entry in tlogger_log's list */
|
|
/* Current reading position, start position of next read again */
|
|
uint32_t r_off;
|
|
uint32_t r_loops;
|
|
uint32_t r_sn;
|
|
uint32_t r_failtimes;
|
|
uint32_t r_from_cur;
|
|
uint32_t r_is_tlogf;
|
|
bool r_all; /* whether this reader can read all entries */
|
|
uint32_t r_ver;
|
|
};
|
|
|
|
static uint32_t g_log_mem_len = 0;
|
|
static uint32_t g_tlogcat_count = 0;
|
|
static struct tlogger_log *g_log;
|
|
|
|
static struct tlogger_log *get_reader_log(const struct file *file)
|
|
{
|
|
struct tlogger_reader *reader = NULL;
|
|
|
|
reader = file->private_data;
|
|
if (!reader)
|
|
return NULL;
|
|
|
|
return reader->log;
|
|
}
|
|
|
|
static bool check_log_item_validite(const struct log_item *item,
|
|
uint32_t item_max_size)
|
|
{
|
|
bool con = (item && (item->magic == LOG_ITEM_MAGIC) &&
|
|
(item->buffer_len > 0) &&
|
|
(item->real_len > 0) &&
|
|
(item->buffer_len % LOG_ITEM_LEN_ALIGN == 0) &&
|
|
(item->real_len <= item->buffer_len) &&
|
|
((item->buffer_len - item->real_len) < LOG_ITEM_LEN_ALIGN) &&
|
|
(item->buffer_len + sizeof(*item) <= item_max_size));
|
|
|
|
return con;
|
|
}
|
|
|
|
static struct log_item *get_next_log_item(const unsigned char *buffer_start,
|
|
uint32_t max_len, uint32_t read_pos, uint32_t scope_len, uint32_t *pos)
|
|
{
|
|
uint32_t i = 0;
|
|
struct log_item *item = NULL;
|
|
uint32_t max_size;
|
|
|
|
if ((read_pos + scope_len) > max_len)
|
|
return NULL;
|
|
|
|
while ((i + sizeof(*item) + LOG_ITEM_LEN_ALIGN) <= scope_len) {
|
|
*pos = read_pos + i;
|
|
item = (struct log_item *)(uintptr_t)(buffer_start + read_pos + i);
|
|
max_size = (((scope_len - i) > LOG_ITEM_MAX_LEN) ?
|
|
LOG_ITEM_MAX_LEN : (scope_len - i));
|
|
if (check_log_item_validite(item, max_size))
|
|
break;
|
|
|
|
i += LOG_ITEM_LEN_ALIGN;
|
|
item = NULL;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
struct reader_position {
|
|
const unsigned char *buffer_start;
|
|
uint32_t max_len;
|
|
uint32_t start_pos;
|
|
uint32_t end_pos;
|
|
};
|
|
|
|
static uint32_t parse_log_item(char __user *buf, size_t count,
|
|
struct reader_position *position, uint32_t *read_off,
|
|
bool *user_buffer_left)
|
|
{
|
|
struct log_item *next_item = NULL;
|
|
size_t buf_left;
|
|
uint32_t buf_written;
|
|
uint32_t item_len;
|
|
bool con = false;
|
|
uint32_t start_pos = position->start_pos;
|
|
|
|
buf_written = 0;
|
|
buf_left = count;
|
|
|
|
con = (!read_off || !position->buffer_start);
|
|
if (con)
|
|
return buf_written;
|
|
|
|
*user_buffer_left = true;
|
|
while (start_pos < position->end_pos) {
|
|
next_item = get_next_log_item(position->buffer_start,
|
|
position->max_len, start_pos,
|
|
position->end_pos - start_pos, &start_pos);
|
|
if (!next_item)
|
|
break;
|
|
|
|
/* copy to user */
|
|
item_len = next_item->buffer_len + sizeof(*next_item);
|
|
if (buf_left < item_len) {
|
|
*user_buffer_left = false;
|
|
break;
|
|
}
|
|
|
|
start_pos += item_len;
|
|
if (copy_to_user(buf + buf_written,
|
|
(void *)next_item, item_len) != 0)
|
|
tloge("copy failed, item len %u\n", item_len);
|
|
buf_written += item_len;
|
|
buf_left -= item_len;
|
|
}
|
|
|
|
*read_off = start_pos;
|
|
return buf_written;
|
|
}
|
|
|
|
static ssize_t get_buffer_info(struct tlogger_reader *reader,
|
|
struct log_buffer_flag *buffer_flag, struct log_buffer **log_buffer)
|
|
{
|
|
struct tlogger_log *log = NULL;
|
|
errno_t ret;
|
|
struct log_buffer *buffer_tmp = NULL;
|
|
|
|
log = reader->log;
|
|
if (!log)
|
|
return -EINVAL;
|
|
|
|
buffer_tmp = (struct log_buffer*)log->buffer_info;
|
|
if (!buffer_tmp)
|
|
return -EINVAL;
|
|
|
|
__asm__ volatile ("isb");
|
|
__asm__ volatile ("dsb sy");
|
|
|
|
mutex_lock(&log->mutex_info);
|
|
ret = memcpy_s(buffer_flag, sizeof(*buffer_flag), &buffer_tmp->flag,
|
|
sizeof(buffer_tmp->flag));
|
|
mutex_unlock(&log->mutex_info);
|
|
if (ret != 0) {
|
|
tloge("memcpy failed %d\n", ret);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
*log_buffer = buffer_tmp;
|
|
return 0;
|
|
}
|
|
|
|
#define LOG_BUFFER_MAX_LEN 0x100000
|
|
|
|
static ssize_t get_last_read_pos(struct log_buffer_flag *log_flag,
|
|
const struct tlogger_reader *reader, uint32_t *log_last_pos, uint32_t *is_read)
|
|
{
|
|
uint32_t buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer);
|
|
|
|
*is_read = 0;
|
|
|
|
if (buffer_max_len > LOG_BUFFER_MAX_LEN)
|
|
return -EINVAL;
|
|
|
|
*log_last_pos = log_flag->last_pos;
|
|
if (*log_last_pos == reader->r_off &&
|
|
log_flag->write_loops == reader->r_loops)
|
|
return 0;
|
|
|
|
if (log_flag->max_len < *log_last_pos ||
|
|
log_flag->max_len > buffer_max_len) {
|
|
tloge("invalid data maxlen %x pos %x\n",
|
|
log_flag->max_len, *log_last_pos);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (reader->r_off > log_flag->max_len) {
|
|
tloge("invalid data roff %x maxlen %x\n",
|
|
reader->r_off, log_flag->max_len);
|
|
return -EFAULT;
|
|
}
|
|
|
|
*is_read = 1;
|
|
return 0;
|
|
}
|
|
|
|
static void set_reader_position(struct reader_position *position,
|
|
const unsigned char *buffer_start, uint32_t max_len, uint32_t start_pos, uint32_t end_pos)
|
|
{
|
|
position->buffer_start = buffer_start;
|
|
position->max_len = max_len;
|
|
position->start_pos = start_pos;
|
|
position->end_pos = end_pos;
|
|
}
|
|
|
|
static ssize_t proc_read_ret(uint32_t buf_written,
|
|
const struct tlogger_reader *reader)
|
|
{
|
|
ssize_t ret;
|
|
if (buf_written == 0) {
|
|
ret = 0;
|
|
} else {
|
|
ret = buf_written;
|
|
tlogd("read length %u\n", buf_written);
|
|
g_last_read_offset = reader->r_off;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t check_read_params(const struct file *file,
|
|
const char __user *buf, size_t count)
|
|
{
|
|
if (count < LOG_ITEM_MAX_LEN)
|
|
return -EINVAL;
|
|
|
|
if (!file || !buf)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the sequence number of the last read position is smaller
|
|
* than the current minimum sequence number, the last read
|
|
* position is overwritten. And this time read data from
|
|
* minimum number, or read data from last position.
|
|
*/
|
|
static ssize_t trigger_parse_log(char __user *buf, size_t count,
|
|
uint32_t log_last_pos, struct log_buffer *log_buffer,
|
|
struct tlogger_reader *reader)
|
|
{
|
|
bool user_buffer_left = false;
|
|
uint32_t buf_written;
|
|
struct reader_position position = {0};
|
|
struct log_buffer_flag *buffer_flag = &(log_buffer->flag);
|
|
|
|
if (buffer_flag->write_loops == reader->r_loops) {
|
|
set_reader_position(&position, log_buffer->buffer_start,
|
|
buffer_flag->max_len, reader->r_off, log_last_pos);
|
|
|
|
buf_written = parse_log_item(buf, count, &position,
|
|
&reader->r_off, &user_buffer_left);
|
|
|
|
return proc_read_ret(buf_written, reader);
|
|
}
|
|
|
|
if (buffer_flag->write_loops > (reader->r_loops +1) ||
|
|
((buffer_flag->write_loops == (reader->r_loops + 1)) &&
|
|
(reader->r_off < log_last_pos))) {
|
|
reader->r_off = log_last_pos;
|
|
reader->r_loops = buffer_flag->write_loops - 1;
|
|
}
|
|
|
|
set_reader_position(&position, log_buffer->buffer_start,
|
|
buffer_flag->max_len, reader->r_off, buffer_flag->max_len);
|
|
|
|
buf_written = parse_log_item(buf, count, &position,
|
|
&reader->r_off, &user_buffer_left);
|
|
|
|
if (count > buf_written && user_buffer_left) {
|
|
set_reader_position(&position, log_buffer->buffer_start,
|
|
buffer_flag->max_len, 0, log_last_pos);
|
|
|
|
buf_written += parse_log_item(buf + buf_written,
|
|
count - buf_written, &position,
|
|
&reader->r_off, &user_buffer_left);
|
|
|
|
reader->r_loops = buffer_flag->write_loops;
|
|
}
|
|
|
|
return proc_read_ret(buf_written, reader);
|
|
}
|
|
|
|
static ssize_t process_tlogger_read(struct file *file,
|
|
char __user *buf, size_t count, loff_t *pos)
|
|
{
|
|
struct tlogger_reader *reader = NULL;
|
|
struct log_buffer *log_buffer = NULL;
|
|
ssize_t ret;
|
|
uint32_t last_pos;
|
|
uint32_t is_read;
|
|
struct log_buffer_flag buffer_flag;
|
|
|
|
(void)pos;
|
|
|
|
ret = check_read_params(file, buf, count);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
reader = file->private_data;
|
|
if (!reader)
|
|
return -EINVAL;
|
|
|
|
ret = get_buffer_info(reader, &buffer_flag, &log_buffer);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = get_last_read_pos(&buffer_flag, reader, &last_pos, &is_read);
|
|
if (is_read == 0)
|
|
return ret;
|
|
|
|
return trigger_parse_log(buf, count, last_pos, log_buffer, reader);
|
|
}
|
|
|
|
void tz_log_write(void)
|
|
{
|
|
struct log_buffer *log_buffer = NULL;
|
|
|
|
if (!g_log)
|
|
return;
|
|
|
|
log_buffer = (struct log_buffer*)g_log->buffer_info;
|
|
if (!log_buffer)
|
|
return;
|
|
|
|
if (g_last_read_offset != log_buffer->flag.last_pos) {
|
|
tlogd("wake up write tz log\n");
|
|
wake_up_interruptible(&g_log->wait_queue_head);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static struct tlogger_log *get_tlogger_log_by_minor(int minor)
|
|
{
|
|
struct tlogger_log *log = NULL;
|
|
|
|
list_for_each_entry(log, &m_log_list, logs) {
|
|
if (log->misc_device.minor == minor)
|
|
return log;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int process_tlogger_open(struct inode *inode,
|
|
struct file *file)
|
|
{
|
|
struct tlogger_log *log = NULL;
|
|
int ret;
|
|
struct tlogger_reader *reader = NULL;
|
|
|
|
tlogd("open logger open ++\n");
|
|
/* not support seek */
|
|
ret = nonseekable_open(inode, file);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
tlogd("Before get log from minor\n");
|
|
log = get_tlogger_log_by_minor(MINOR(inode->i_rdev));
|
|
if (!log)
|
|
return -ENODEV;
|
|
|
|
reader = kmalloc(sizeof(*reader), GFP_KERNEL);
|
|
if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)reader))
|
|
return -ENOMEM;
|
|
|
|
reader->log = log;
|
|
reader->r_all = true;
|
|
reader->r_off = 0;
|
|
reader->r_loops = 0;
|
|
reader->r_sn = 0;
|
|
reader->r_failtimes = 0;
|
|
reader->r_is_tlogf = 0;
|
|
reader ->r_from_cur = 0;
|
|
|
|
INIT_LIST_HEAD(&reader->list);
|
|
|
|
mutex_lock(&log->mutex_info);
|
|
list_add_tail(&reader->list, &log->readers);
|
|
g_tlogcat_count++;
|
|
mutex_unlock(&log->mutex_info);
|
|
|
|
file->private_data = reader;
|
|
tlogd("tlogcat count %u\n", g_tlogcat_count);
|
|
#ifdef CONFIG_TEE_REBOOT
|
|
get_tlogcat_pid();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int process_tlogger_release(struct inode *ignored,
|
|
struct file *file)
|
|
{
|
|
struct tlogger_reader *reader = NULL;
|
|
struct tlogger_log *log = NULL;
|
|
|
|
(void)ignored;
|
|
|
|
tlogd("logger_release ++\n");
|
|
|
|
if (!file)
|
|
return -1;
|
|
|
|
reader = file->private_data;
|
|
if (!reader) {
|
|
tloge("reader is null\n");
|
|
return -1;
|
|
}
|
|
|
|
log = reader->log;
|
|
if (!log) {
|
|
tloge("log is null\n");
|
|
return -1;
|
|
}
|
|
|
|
mutex_lock(&log->mutex_info);
|
|
list_del(&reader->list);
|
|
if (g_tlogcat_count >= 1)
|
|
g_tlogcat_count--;
|
|
mutex_unlock(&log->mutex_info);
|
|
|
|
tlogd("logger_release r_is_tlogf-%u\n", reader->r_is_tlogf);
|
|
|
|
if (reader->r_is_tlogf != 0)
|
|
g_tlogcat_f = 0;
|
|
|
|
kfree(reader);
|
|
tlogd("tlogcat count %u\n", g_tlogcat_count);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int process_tlogger_poll(struct file *file,
|
|
poll_table *wait)
|
|
{
|
|
struct tlogger_reader *reader = NULL;
|
|
struct tlogger_log *log = NULL;
|
|
struct log_buffer *buffer = NULL;
|
|
uint32_t ret = POLLOUT | POLLWRNORM;
|
|
|
|
tlogd("logger_poll ++\n");
|
|
if (!file) {
|
|
tloge("file is null\n");
|
|
return ret;
|
|
}
|
|
|
|
reader = file->private_data;
|
|
if (!reader) {
|
|
tloge("the private data is null\n");
|
|
return ret;
|
|
}
|
|
|
|
log = reader->log;
|
|
if (!log) {
|
|
tloge("log is null\n");
|
|
return ret;
|
|
}
|
|
|
|
buffer = (struct log_buffer*)log->buffer_info;
|
|
if (!buffer) {
|
|
tloge("buffer is null\n");
|
|
return ret;
|
|
}
|
|
|
|
poll_wait(file, &log->wait_queue_head, wait);
|
|
|
|
if (buffer->flag.last_pos != reader->r_off)
|
|
ret |= POLLIN | POLLRDNORM;
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define SET_READ_POS 1U
|
|
static void set_reader_cur_pos(const struct file *file)
|
|
{
|
|
struct tlogger_reader *reader = NULL;
|
|
struct tlogger_log *log = NULL;
|
|
struct log_buffer *buffer = NULL;
|
|
|
|
reader = file->private_data;
|
|
if (!reader)
|
|
return;
|
|
|
|
log = reader->log;
|
|
if (!log)
|
|
return;
|
|
|
|
buffer = (struct log_buffer*)log->buffer_info;
|
|
if (!buffer)
|
|
return;
|
|
|
|
reader->r_from_cur = SET_READ_POS;
|
|
reader->r_off = buffer->flag.last_pos;
|
|
reader->r_loops = buffer->flag.write_loops;
|
|
}
|
|
|
|
static void set_tlogcat_f_stat(const struct file *file)
|
|
{
|
|
struct tlogger_reader *reader = NULL;
|
|
|
|
if (!file)
|
|
return;
|
|
|
|
reader = file->private_data;
|
|
if (!reader)
|
|
return;
|
|
|
|
reader->r_is_tlogf = 1;
|
|
g_tlogcat_f = 1;
|
|
|
|
tlogi("set tlogcat_f-%d\n", g_tlogcat_f);
|
|
return;
|
|
}
|
|
|
|
static int get_tlogcat_f_stat(void)
|
|
{
|
|
tlogi("get tlogcat_f-%d\n", g_tlogcat_f);
|
|
return g_tlogcat_f;
|
|
}
|
|
|
|
static int check_user_arg(unsigned long arg, size_t arg_len)
|
|
{
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 18) || \
|
|
LINUX_VERSION_CODE == KERNEL_VERSION(4, 19, 71))
|
|
return (int)access_ok(VERIFY_READ,
|
|
(void __user *)(uintptr_t)arg, arg_len);
|
|
#else
|
|
return (int)access_ok((void __user *)(uintptr_t)arg, arg_len);
|
|
#endif
|
|
}
|
|
|
|
static int get_teeos_version(uint32_t cmd, unsigned long arg)
|
|
{
|
|
int ret;
|
|
|
|
if ((_IOC_DIR(cmd) & _IOC_READ) == 0) {
|
|
tloge("check get version cmd failed\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = check_user_arg(arg,
|
|
sizeof(g_log_buffer->flag.version_info));
|
|
if (ret == 0) {
|
|
tloge("check version info arg failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (copy_to_user((void __user *)(uintptr_t)arg,
|
|
(void *)g_log_buffer->flag.version_info,
|
|
sizeof(g_log_buffer->flag.version_info)) != 0) {
|
|
tloge("version info copy failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_LOG_POOL
|
|
#define LOG_POOL_SIZE 0x80000UL
|
|
#define LOG_POOL_ITEM_MAX_LEN 384
|
|
#define HALF_DIV_NUM 2
|
|
|
|
static DEFINE_MUTEX(g_log_pool_lock);
|
|
static uint64_t g_log_pool_va = 0;
|
|
static uint64_t g_log_pool_size = 0;
|
|
static uint32_t g_logserialno = 0;
|
|
|
|
static int get_log_pool(void *argp)
|
|
{
|
|
uint64_t sz = 0;
|
|
|
|
if (argp == NULL) {
|
|
tloge("invalid params\n");
|
|
return -1;
|
|
}
|
|
sz = get_log_mem_size() / HALF_DIV_NUM;
|
|
if (sz != LOG_POOL_SIZE) {
|
|
tloge("log pool size error\n");
|
|
return -1;
|
|
}
|
|
if (copy_to_user(argp, &sz, sizeof(uint64_t)) != 0) {
|
|
tloge("copy to user failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int log_pool_check(void)
|
|
{
|
|
if (g_log_pool_size != LOG_POOL_SIZE) {
|
|
tloge("log pool size error\n");
|
|
g_log_pool_size = 0;
|
|
return -1;
|
|
}
|
|
|
|
if (g_log_pool_va == 0 || (UINT64_MAX - g_log_pool_va) < g_log_pool_size) {
|
|
tloge("log pool addr error\n");
|
|
g_log_pool_va = 0;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int log_pool_item_check(struct log_item *item, struct teelogger_log_pool pool_item)
|
|
{
|
|
if (item->magic != LOG_ITEM_MAGIC || item->buffer_len == 0 || item->real_len == 0||
|
|
item->buffer_len % LOG_ITEM_LEN_ALIGN != 0 || item->real_len > item->buffer_len ||
|
|
(item->buffer_len - item->real_len) >= (uint16_t)LOG_ITEM_LEN_ALIGN ||
|
|
(uint64_t)(item->buffer_len + sizeof(struct log_item)) > pool_item.size)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int log_pool_append(void *argp)
|
|
{
|
|
struct teelogger_log_pool pool_item = {0};
|
|
struct log_buffer *pool_buffer = NULL;
|
|
struct log_item *item = NULL;
|
|
if (argp == NULL || log_pool_check() != 0) {
|
|
tloge("invalid params, g_log_pool_va or g_log_pool_size\n");
|
|
return -1;
|
|
}
|
|
if (copy_from_user((void *)&pool_item, argp, sizeof(struct teelogger_log_pool)) != 0) {
|
|
tloge("pool_item copy from user error\n");
|
|
return -1;
|
|
}
|
|
if ((uint64_t)LOG_POOL_ITEM_MAX_LEN < pool_item.size || pool_item.size < (uint64_t)sizeof(struct log_item) ||
|
|
pool_item.addr == 0 || UINT64_MAX - pool_item.addr < pool_item.size) {
|
|
tloge("pool_item addr or size error\n");
|
|
return -1;
|
|
}
|
|
|
|
mutex_lock(&g_log_pool_lock);
|
|
pool_buffer = (struct log_buffer *)(uintptr_t)g_log_pool_va;
|
|
if (pool_buffer == NULL || (uint64_t)(pool_buffer->flag).last_pos > g_log_pool_size ||
|
|
((uint64_t)sizeof(struct log_buffer) + (uint64_t)(pool_buffer->flag).last_pos) > g_log_pool_size) {
|
|
tloge("pool_buffer error\n");
|
|
mutex_unlock(&g_log_pool_lock);
|
|
return -1;
|
|
}
|
|
/* restart from head */
|
|
if (((uint64_t)sizeof(struct log_buffer) + (uint64_t)(pool_buffer->flag).last_pos +
|
|
pool_item.size) > g_log_pool_size) {
|
|
pool_buffer->flag.write_loops++;
|
|
pool_buffer->flag.last_pos = 0;
|
|
}
|
|
|
|
item = (struct log_item *)(uintptr_t)((uint64_t)(uintptr_t)pool_buffer->buffer_start +
|
|
(uint64_t)pool_buffer->flag.last_pos);
|
|
if (copy_from_user((void *)item, (void *)pool_item.addr, pool_item.size) != 0) {
|
|
tloge("item copy_from_user error\n");
|
|
mutex_unlock(&g_log_pool_lock);
|
|
return -1;
|
|
}
|
|
if (log_pool_item_check(item, pool_item) != 0) {
|
|
tloge("item check error\n");
|
|
mutex_unlock(&g_log_pool_lock);
|
|
return -1;
|
|
}
|
|
|
|
item->serial_no = ++g_logserialno;
|
|
/* reset g_logserialno */
|
|
if (item->serial_no == 0)
|
|
item->serial_no = ++g_logserialno;
|
|
item->new_line = (unsigned char)'\n';
|
|
pool_buffer->flag.reserved[1] = g_logserialno;
|
|
pool_buffer->flag.last_pos += (uint32_t)pool_item.size;
|
|
mutex_unlock(&g_log_pool_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int init_log_pool(void)
|
|
{
|
|
struct log_buffer *pool_buffer = NULL;
|
|
uint64_t paddr = get_log_mem_paddr(0);
|
|
|
|
g_log_pool_size = get_log_mem_size();
|
|
if ((UINT64_MAX - paddr) < g_log_pool_size) {
|
|
tloge("log pool paddr error\n");
|
|
return -1;
|
|
}
|
|
g_log_pool_size /= HALF_DIV_NUM;
|
|
g_log_pool_va = (uint64_t)(uintptr_t)ioremap_cache(paddr + g_log_pool_size, g_log_pool_size);
|
|
if (log_pool_check() != 0) {
|
|
tloge("log pool addr or size error\n");
|
|
return -1;
|
|
}
|
|
pool_buffer = (struct log_buffer *)(uintptr_t)g_log_pool_va;
|
|
|
|
/*
|
|
* the struct log_buffer magic field use for log retention feature,
|
|
* if hit the magic, will retain the old log before reset in log pool,
|
|
* or will memset log pool.
|
|
*/
|
|
if (pool_buffer->flag.reserved[0] != LOG_ITEM_MAGIC) {
|
|
(void)memset_s((void *)(uintptr_t)g_log_pool_va, g_log_pool_size, 0, g_log_pool_size);
|
|
pool_buffer->flag.reserved[0] = LOG_ITEM_MAGIC;
|
|
pool_buffer->flag.max_len = g_log_pool_size - sizeof(struct log_buffer);
|
|
} else {
|
|
g_logserialno = pool_buffer->flag.reserved[1];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void free_log_pool(void)
|
|
{
|
|
if (g_log_pool_va != 0)
|
|
iounmap((void __iomem *)(uintptr_t)g_log_pool_va);
|
|
g_log_pool_va = 0;
|
|
g_log_pool_size = 0;
|
|
g_logserialno = 0;
|
|
}
|
|
#endif
|
|
|
|
static long process_tlogger_ioctl(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct tlogger_log *log = NULL;
|
|
long ret = -EINVAL;
|
|
|
|
if (!file)
|
|
return -1;
|
|
|
|
log = get_reader_log(file);
|
|
if (!log) {
|
|
tloge("log is null\n");
|
|
return -1;
|
|
}
|
|
|
|
tlogd("logger_ioctl start ++\n");
|
|
mutex_lock(&log->mutex_info);
|
|
|
|
switch (cmd) {
|
|
case TEELOGGER_GET_VERSION:
|
|
if (get_teeos_version(cmd, arg) == 0)
|
|
ret = 0;
|
|
break;
|
|
case TEELOGGER_SET_READERPOS_CUR:
|
|
set_reader_cur_pos(file);
|
|
ret = 0;
|
|
break;
|
|
case TEELOGGER_SET_TLOGCAT_STAT:
|
|
set_tlogcat_f_stat(file);
|
|
ret = 0;
|
|
break;
|
|
case TEELOGGER_GET_TLOGCAT_STAT:
|
|
ret = get_tlogcat_f_stat();
|
|
break;
|
|
#ifdef CONFIG_LOG_POOL
|
|
case TEELOGGER_GET_LOG_POOL:
|
|
ret = get_log_pool(arg);
|
|
break;
|
|
case TEELOGGER_LOG_POOL_APPEND:
|
|
ret = log_pool_append(arg);
|
|
break;
|
|
#endif
|
|
default:
|
|
tloge("ioctl error default\n");
|
|
break;
|
|
}
|
|
|
|
mutex_unlock(&log->mutex_info);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long process_tlogger_compat_ioctl(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
tlogd("logger_compat_ioctl ++\n");
|
|
arg = (unsigned long)(uintptr_t)compat_ptr(arg);
|
|
return process_tlogger_ioctl(file, cmd, arg);
|
|
}
|
|
#endif
|
|
|
|
static const struct file_operations g_logger_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = process_tlogger_read,
|
|
.poll = process_tlogger_poll,
|
|
.unlocked_ioctl = process_tlogger_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = process_tlogger_compat_ioctl,
|
|
#endif
|
|
.open = process_tlogger_open,
|
|
.release = process_tlogger_release,
|
|
};
|
|
|
|
static int __init register_device(const char *log_name,
|
|
uintptr_t addr, int size)
|
|
{
|
|
int ret;
|
|
struct tlogger_log *log = NULL;
|
|
unsigned char *buffer = (unsigned char *)addr;
|
|
(void)size;
|
|
|
|
log = kzalloc(sizeof(*log), GFP_KERNEL);
|
|
if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)log)) {
|
|
tloge("kzalloc is failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
log->buffer_info = buffer;
|
|
log->misc_device.minor = MISC_DYNAMIC_MINOR;
|
|
log->misc_device.name = kstrdup(log_name, GFP_KERNEL);
|
|
if (!log->misc_device.name) {
|
|
ret = -ENOMEM;
|
|
tloge("kstrdup is failed\n");
|
|
goto out_free_log;
|
|
}
|
|
log->misc_device.fops = &g_logger_fops;
|
|
log->misc_device.parent = NULL;
|
|
|
|
init_waitqueue_head(&log->wait_queue_head);
|
|
INIT_LIST_HEAD(&log->readers);
|
|
mutex_init(&log->mutex_info);
|
|
INIT_LIST_HEAD(&log->logs);
|
|
list_add_tail(&log->logs, &m_log_list);
|
|
|
|
/* register misc device for this log */
|
|
ret = misc_register(&log->misc_device);
|
|
if (unlikely(ret)) {
|
|
tloge("failed to register misc device:%s\n",
|
|
log->misc_device.name);
|
|
goto out_free_log;
|
|
}
|
|
g_log = log;
|
|
return 0;
|
|
|
|
out_free_log:
|
|
if (log->misc_device.name)
|
|
kfree(log->misc_device.name);
|
|
|
|
kfree(log);
|
|
return ret;
|
|
}
|
|
|
|
static struct log_item *msg_get_next(unsigned char *buffer_start,
|
|
uint32_t read_pos, uint32_t scope_len, uint32_t max_len)
|
|
{
|
|
uint32_t i = 0;
|
|
struct log_item *item = NULL;
|
|
uint32_t item_max_size;
|
|
uint32_t len;
|
|
|
|
while (i <= scope_len &&
|
|
((read_pos + i + sizeof(*item)) < max_len)) {
|
|
len = (uint32_t)(scope_len - i);
|
|
item_max_size =
|
|
((len > LOG_ITEM_MAX_LEN) ? LOG_ITEM_MAX_LEN : len);
|
|
item = (struct log_item *)(buffer_start + read_pos + i);
|
|
|
|
if (check_log_item_validite(item, item_max_size)) {
|
|
if ((read_pos + i + sizeof(*item) +
|
|
item->buffer_len) > max_len) {
|
|
tloge("check item len error\n");
|
|
return NULL;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
i += LOG_ITEM_LEN_ALIGN;
|
|
item = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_TZDRIVER_MODULE
|
|
/* there is no way to chown in kernel-5.10 for ko */
|
|
static int tlogger_chown(const char *file_path, uint32_t file_path_len)
|
|
{
|
|
(void)file_path;
|
|
(void)file_path_len;
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int tlogger_chown(const char *file_path, uint32_t file_path_len)
|
|
{
|
|
(void)file_path_len;
|
|
uid_t user = ROOT_UID;
|
|
gid_t group = ROOT_GID;
|
|
int ret;
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0))
|
|
mm_segment_t old_fs;
|
|
#endif
|
|
get_log_chown(&user, &group);
|
|
|
|
/* not need modify chown attr */
|
|
if (group == ROOT_GID && user == ROOT_UID)
|
|
return 0;
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0))
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
#endif
|
|
#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)
|
|
ret = (int)ksys_chown((const char __user *)file_path, user, group);
|
|
#else
|
|
ret = (int)sys_chown((const char __user *)file_path, user, group);
|
|
#endif
|
|
if (ret != 0) {
|
|
tloge("sys chown for last teemsg file error\n");
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0))
|
|
set_fs(old_fs);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0))
|
|
set_fs(old_fs);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int write_version_to_msg(struct file *filep,
|
|
loff_t *pos)
|
|
{
|
|
ssize_t write_len;
|
|
|
|
/* first write tee versino info */
|
|
write_len = kernel_write(filep, g_log_buffer->flag.version_info,
|
|
strlen(g_log_buffer->flag.version_info), pos);
|
|
if (write_len < 0) {
|
|
tloge("Failed to write to last teemsg version\n");
|
|
return -1;
|
|
}
|
|
|
|
tlogd("Succeed to Write to last teemsg version, len=%zd\n", write_len);
|
|
return 0;
|
|
}
|
|
|
|
static int write_part_log_to_msg(struct file *filep,
|
|
unsigned char *buffer, uint32_t buffer_max_len, loff_t *pos,
|
|
uint32_t read_off, uint32_t read_off_end)
|
|
{
|
|
struct log_item *next_item = NULL;
|
|
uint32_t item_len;
|
|
uint32_t total_len = 0;
|
|
ssize_t write_len;
|
|
|
|
next_item = msg_get_next(buffer, read_off,
|
|
LOG_ITEM_MAX_LEN, buffer_max_len);
|
|
|
|
while (next_item && read_off <= read_off_end) {
|
|
item_len = next_item->buffer_len + sizeof(*next_item);
|
|
write_len = kernel_write(filep, next_item->log_buffer,
|
|
next_item->real_len, pos);
|
|
if (write_len < 0) {
|
|
tloge("Failed to write last teemsg %zd\n", write_len);
|
|
return -1;
|
|
}
|
|
|
|
tlogd("Succeed to Write last teemsg, len=%zd\n", write_len);
|
|
total_len += item_len;
|
|
read_off = (unsigned char *)next_item - buffer + item_len;
|
|
if (total_len >= buffer_max_len)
|
|
break;
|
|
|
|
next_item = msg_get_next(buffer, read_off,
|
|
LOG_ITEM_MAX_LEN, buffer_max_len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_log_to_msg(struct file *filep,
|
|
unsigned char *buffer, uint32_t buffer_max_len, loff_t *pos,
|
|
uint32_t read_off, uint32_t read_off_end)
|
|
{
|
|
if (read_off < read_off_end) {
|
|
return write_part_log_to_msg(filep, buffer, buffer_max_len, pos,
|
|
read_off, read_off_end);
|
|
} else {
|
|
if (write_part_log_to_msg(filep, buffer, buffer_max_len, pos,
|
|
read_off, buffer_max_len) != 0)
|
|
return -1;
|
|
return write_part_log_to_msg(filep, buffer, buffer_max_len, pos,
|
|
0, read_off_end);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_TEE_LOG_DUMP_PATH
|
|
static void update_dumpmsg_offset(uint32_t *read_start, uint32_t *read_end,
|
|
uint32_t read_off, uint32_t read_off_end, uint32_t *dump_start_flag, uint32_t *dump_end_flag)
|
|
{
|
|
struct log_item *next_item = NULL;
|
|
unsigned char *buffer = g_log_buffer->buffer_start;
|
|
uint32_t buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer);
|
|
ssize_t item_len;
|
|
ssize_t total_len = 0;
|
|
|
|
next_item = msg_get_next(buffer, read_off,
|
|
LOG_ITEM_MAX_LEN, buffer_max_len);
|
|
|
|
while (next_item && read_off <= read_off_end) {
|
|
item_len = next_item->buffer_len + sizeof(*next_item);
|
|
if (strstr(next_item->log_buffer, DUMP_START_MAGIC)) {
|
|
*read_start = read_off;
|
|
*dump_start_flag = 1;
|
|
} else if (strstr(next_item->log_buffer, DUMP_END_MAGIC)) {
|
|
*read_end = read_off;
|
|
*dump_end_flag = 1;
|
|
}
|
|
read_off = (unsigned char *)next_item - buffer + item_len;
|
|
total_len += item_len;
|
|
if (total_len >= buffer_max_len)
|
|
break;
|
|
|
|
next_item = msg_get_next(buffer, read_off,
|
|
LOG_ITEM_MAX_LEN, buffer_max_len);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TEE_LOG_DUMP_PATH
|
|
static int get_dumpmsg_offset(uint32_t *read_start, uint32_t *read_end)
|
|
{
|
|
uint32_t read_off = *read_start;
|
|
uint32_t read_off_end = *read_end;
|
|
uint32_t buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer);
|
|
uint32_t dump_start_flag = 0;
|
|
uint32_t dump_end_flag = 0;
|
|
|
|
if (read_off < read_off_end) {
|
|
update_dumpmsg_offset(read_start, read_end, read_off, read_off_end,
|
|
&dump_start_flag, &dump_end_flag);
|
|
} else {
|
|
update_dumpmsg_offset(read_start, read_end, read_off, buffer_max_len,
|
|
&dump_start_flag, &dump_end_flag);
|
|
update_dumpmsg_offset(read_start, read_end, 0, read_off_end,
|
|
&dump_start_flag, &dump_end_flag);
|
|
}
|
|
|
|
if (dump_start_flag == 0 || dump_end_flag == 0) {
|
|
tloge("can't find dump start or end\n");
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int get_msg_buffer(unsigned char **buffer, uint32_t *buffer_max_len,
|
|
uint32_t *read_start, uint32_t *read_end,
|
|
const char *file_path, uint32_t file_path_len)
|
|
{
|
|
errno_t rc;
|
|
int ret;
|
|
unsigned char *addr = NULL;
|
|
(void)file_path_len;
|
|
|
|
if (!g_log_buffer)
|
|
return -1;
|
|
|
|
*buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer);
|
|
|
|
if (*buffer_max_len > LOG_BUFFER_MAX_LEN)
|
|
return 0;
|
|
|
|
*read_start = 0;
|
|
*read_end = *buffer_max_len;
|
|
#ifdef CONFIG_TEE_LOG_DUMP_PATH
|
|
if (strcmp(file_path, CONFIG_TEE_LOG_DUMP_PATH) == 0) {
|
|
*read_start = g_last_read_offset;
|
|
*read_end = ((struct log_buffer*)g_log->buffer_info)->flag.last_pos;
|
|
if (get_dumpmsg_offset(read_start, read_end) != 0) {
|
|
tloge("get dump offset failed\n");
|
|
return -1;
|
|
}
|
|
}
|
|
#else
|
|
(void)file_path;
|
|
#endif
|
|
addr = kmalloc(*buffer_max_len, GFP_KERNEL);
|
|
if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)addr)) {
|
|
ret = -ENOMEM;
|
|
goto free_res;
|
|
}
|
|
|
|
rc = memcpy_s(addr, *buffer_max_len, g_log_buffer->buffer_start,
|
|
*buffer_max_len);
|
|
if (rc) {
|
|
tloge("memcpy failed %d\n", rc);
|
|
ret = -EAGAIN;
|
|
goto free_res;
|
|
}
|
|
|
|
*buffer = addr;
|
|
return 0;
|
|
|
|
free_res:
|
|
if (addr)
|
|
kfree(addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int open_msg_file(struct file **file,
|
|
const char *file_path, uint32_t file_path_len)
|
|
{
|
|
struct file *filep = NULL;
|
|
(void)file_path_len;
|
|
|
|
filep = filp_open(file_path, O_CREAT | O_RDWR | O_TRUNC, OPEN_FILE_MODE);
|
|
if (!filep || IS_ERR(filep)) {
|
|
tloge("open last teemsg file err %ld\n", PTR_ERR(filep));
|
|
return -1;
|
|
}
|
|
|
|
*file = filep;
|
|
return 0;
|
|
}
|
|
|
|
int tlogger_store_msg(const char *file_path, uint32_t file_path_len)
|
|
{
|
|
struct file *filep = NULL;
|
|
loff_t pos = 0;
|
|
int ret;
|
|
uint32_t buffer_max_len = 0;
|
|
unsigned char *buffer = NULL;
|
|
uint32_t read_start = 0;
|
|
uint32_t read_end = 0;
|
|
|
|
if (!file_path || file_path_len > TEE_LOG_FILE_NAME_MAX) {
|
|
tloge("file path is invalid\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!g_tlogcat_count) {
|
|
tlogd("tlogcat count %u\n", g_tlogcat_count);
|
|
return 0;
|
|
}
|
|
|
|
/* copy logs from log memory, then parse the logs */
|
|
ret = get_msg_buffer(&buffer, &buffer_max_len,
|
|
&read_start, &read_end, file_path, file_path_len);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
/* exception handling, store trustedcore exception info to file */
|
|
ret = open_msg_file(&filep, file_path, file_path_len);
|
|
if (ret != 0)
|
|
goto free_res;
|
|
|
|
ret = tlogger_chown(file_path, file_path_len);
|
|
if (ret != 0)
|
|
goto free_res;
|
|
|
|
ret = write_version_to_msg(filep, &pos);
|
|
if (ret != 0)
|
|
goto free_res;
|
|
|
|
ret = write_log_to_msg(filep, buffer, buffer_max_len,
|
|
&pos, read_start, read_end);
|
|
|
|
free_res:
|
|
if (buffer) {
|
|
kfree(buffer);
|
|
buffer = NULL;
|
|
}
|
|
|
|
if (filep != NULL) {
|
|
vfs_fsync(filep, 0);
|
|
filp_close(filep, 0);
|
|
}
|
|
|
|
/* trigger write teeos log */
|
|
tz_log_write();
|
|
return ret;
|
|
}
|
|
|
|
#ifdef DEF_ENG
|
|
#define KERNEL_IMG_IS_ENG 1
|
|
#endif
|
|
int register_mem_to_teeos(uint64_t mem_addr, uint32_t mem_len, bool is_cache_mem)
|
|
{
|
|
struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
|
|
struct mb_cmd_pack *mb_pack = NULL;
|
|
int ret;
|
|
|
|
mb_pack = mailbox_alloc_cmd_pack();
|
|
if (!mb_pack) {
|
|
tloge("mailbox alloc failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
|
|
smc_cmd.cmd_id = GLOBAL_CMD_ID_REGISTER_LOG_MEM;
|
|
mb_pack->operation.paramtypes = teec_param_types(
|
|
TEE_PARAM_TYPE_VALUE_INPUT,
|
|
TEE_PARAM_TYPE_VALUE_INPUT,
|
|
TEE_PARAM_TYPE_VALUE_INPUT,
|
|
TEE_PARAM_TYPE_NONE);
|
|
mb_pack->operation.params[0].value.a = mem_addr;
|
|
mb_pack->operation.params[0].value.b = mem_addr >> ADDR_TRANS_NUM;
|
|
mb_pack->operation.params[1].value.a = mem_len;
|
|
#ifdef DEF_ENG
|
|
mb_pack->operation.params[1].value.b = KERNEL_IMG_IS_ENG;
|
|
#endif
|
|
/*
|
|
* is_cache_mem: true, teeos map this memory for cache
|
|
* style; or else map to no cache style
|
|
*/
|
|
mb_pack->operation.params[2].value.a = is_cache_mem;
|
|
|
|
smc_cmd.operation_phys = mailbox_virt_to_phys((uintptr_t)&mb_pack->operation);
|
|
smc_cmd.operation_h_phys =
|
|
(uint64_t)mailbox_virt_to_phys((uintptr_t)&mb_pack->operation) >> ADDR_TRANS_NUM;
|
|
|
|
if (is_tee_rebooting())
|
|
ret = send_smc_cmd_rebooting(TSP_REQUEST, 0, 0, &smc_cmd);
|
|
else
|
|
ret = tc_ns_smc(&smc_cmd);
|
|
|
|
mailbox_free(mb_pack);
|
|
if (ret != 0)
|
|
tloge("Send log mem info failed\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int register_mem_cfg(uint64_t *addr, uint32_t *len)
|
|
{
|
|
int ret;
|
|
ret = register_log_mem(addr, len);
|
|
if (ret != 0)
|
|
tloge("register log mem failed %x\n", ret);
|
|
|
|
ret = register_log_exception();
|
|
if (ret != 0)
|
|
tloge("teeos register exception to log module failed\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int check_log_mem(uint64_t mem_addr, uint32_t mem_len)
|
|
{
|
|
if (mem_len < TEMP_LOG_MEM_SIZE) {
|
|
tloge("log mem init error, too small len:0x%x\n", mem_len);
|
|
return -1;
|
|
}
|
|
if (!mem_addr) {
|
|
tloge("mem init failed!!! addr is 0\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int register_tloger_mem(void)
|
|
{
|
|
int ret;
|
|
uint64_t mem_addr = 0;
|
|
|
|
ret = register_mem_cfg(&mem_addr, &g_log_mem_len);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = check_log_mem(mem_addr, g_log_mem_len);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
g_log_buffer =
|
|
(struct log_buffer *)map_log_mem(mem_addr, g_log_mem_len);
|
|
if (!g_log_buffer)
|
|
return -ENOMEM;
|
|
|
|
g_log_buffer->flag.max_len = g_log_mem_len - sizeof(*g_log_buffer);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int register_tloger_device(void)
|
|
{
|
|
int ret;
|
|
|
|
tlogi("tlogcat version 1.0.0\n");
|
|
ret = register_device(LOGGER_LOG_TEEOS, (uintptr_t)g_log_buffer,
|
|
sizeof(*g_log_buffer) + g_log_buffer->flag.max_len);
|
|
if (ret != 0) {
|
|
unmap_log_mem((int *)g_log_buffer);
|
|
g_log_buffer = NULL;
|
|
g_log_mem_len = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int register_tloger(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = register_tloger_mem();
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
#ifdef CONFIG_LOG_POOL
|
|
ret = init_log_pool();
|
|
if (ret != 0)
|
|
tloge("init_log_pool init failed\n");
|
|
#endif
|
|
|
|
ret = register_tloger_device();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void unregister_mem_cfg(void)
|
|
{
|
|
if (g_log_buffer)
|
|
unmap_log_mem((int *)g_log_buffer);
|
|
|
|
unregister_log_exception();
|
|
}
|
|
|
|
static void unregister_tlogger(void)
|
|
{
|
|
struct tlogger_log *current_log = NULL;
|
|
struct tlogger_log *next_log = NULL;
|
|
|
|
list_for_each_entry_safe(current_log, next_log, &m_log_list, logs) {
|
|
/* we have to delete all the entry inside m_log_list */
|
|
misc_deregister(¤t_log->misc_device);
|
|
kfree(current_log->misc_device.name);
|
|
list_del(¤t_log->logs);
|
|
kfree(current_log);
|
|
}
|
|
|
|
#ifdef CONFIG_LOG_POOL
|
|
free_log_pool();
|
|
#endif
|
|
unregister_mem_cfg();
|
|
g_log_buffer = NULL;
|
|
g_log_mem_len = 0;
|
|
}
|
|
|
|
#ifdef CONFIG_TZDRIVER_MODULE
|
|
int init_tlogger_service(void)
|
|
{
|
|
return register_tloger();
|
|
}
|
|
|
|
void free_tlogger_service(void)
|
|
{
|
|
unregister_tlogger();
|
|
}
|
|
#else
|
|
static int __init init_tlogger_service(void)
|
|
{
|
|
return register_tloger();
|
|
}
|
|
|
|
static void __exit free_tlogger_service(void)
|
|
{
|
|
unregister_tlogger();
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TZDRIVER
|
|
device_initcall(init_tlogger_service);
|
|
module_exit(free_tlogger_service);
|
|
|
|
MODULE_AUTHOR("iTrustee");
|
|
MODULE_DESCRIPTION("TrustCore Logger");
|
|
MODULE_VERSION("1.00");
|
|
#endif
|