blob: 54adcc609e56bf4c6c59170f56cccc3539012f85 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2017, Amlogic, Inc.
*
* 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 <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include "optee_smc.h"
#include "log.h"
#include "optee_private.h"
#define LOGGER_LOOPBUFFER_MAGIC 0xAA00AA00
#define LOGGER_LOOPBUFFER_OFFSET 0x00000080
#define OPTEE_LOG_READ_MAX PAGE_SIZE
#define OPTEE_LOG_LINE_MAX 1024
#define OPTEE_LOG_TIMER_INTERVAL 1
#undef pr_fmt
#define pr_fmt(fmt) "[TEE] " fmt
struct loopbuffer_ctl_s {
unsigned int magic;
unsigned int inited;
unsigned int total_size;
unsigned int fill_size;
unsigned int mode;
unsigned int reader;
unsigned int writer;
char *buffer;
};
static void *log_buf_va;
static u8 line_buff[OPTEE_LOG_LINE_MAX];
struct delayed_work log_work;
static struct workqueue_struct *log_workqueue;
static ssize_t log_buff_get_read_buff(char **buf, int len)
{
int writer;
int reader;
int read_size = 0;
struct loopbuffer_ctl_s *ctl = (struct loopbuffer_ctl_s *)log_buf_va;
if (!ctl || len <= 0)
return 0;
writer = ctl->writer;
reader = ctl->reader;
if (reader == writer)
read_size = 0;
else if (reader < writer)
read_size = writer - reader;
else
read_size = ctl->total_size - reader;
if (read_size > len)
read_size = len;
*buf = (char *)log_buf_va + LOGGER_LOOPBUFFER_OFFSET + reader;
ctl->reader += read_size;
if (ctl->reader == ctl->total_size)
ctl->reader = 0;
return read_size;
}
static size_t log_print_text(char *buf, size_t size)
{
char *line = line_buff;
const char *text = buf;
const char *next = NULL;
s32 remaining = size;
size_t line_size = 0;
size_t scan_size = 0;
if (!buf || !size)
return 0;
while (text && remaining > 0) {
/* Reserve 2 bytes for EOL and EOS */
scan_size = remaining > (OPTEE_LOG_LINE_MAX - 2) ?
(OPTEE_LOG_LINE_MAX - 2) : remaining;
next = memchr(text, '\n', scan_size);
if (next) {
/* EOL is found */
next++;
line_size = next - text;
/* Add a extra EOS */
line[line_size] = '\0';
} else {
/* No EOL found.*/
line_size = scan_size;
/* Truncate string to scan_size and add EOL and EOS. */
line[line_size] = '\n';
line[line_size + 1] = '\0';
}
memcpy(line, text, line_size);
remaining -= line_size;
text += line_size;
pr_notice("%s", line);
}
/* All remaining should be consumed */
if (!text || remaining) {
pr_err("WARNING: text(%p) is NULL or remaining(%d) is not 0.\n",
text, remaining);
}
return size;
}
static void do_log_timer(struct work_struct *work)
{
size_t len;
char *read_buff = NULL;
len = log_buff_get_read_buff(&read_buff, OPTEE_LOG_READ_MAX);
if (len > 0)
log_print_text(read_buff, len);
if (queue_delayed_work(log_workqueue, &log_work, OPTEE_LOG_TIMER_INTERVAL * HZ) == 0)
pr_err("%s:%d Failed to join the workqueue\n", __func__, __LINE__);
}
int optee_log_init(void *va)
{
int rc = 0;
size_t size = 0;
phys_addr_t begin = 0;
phys_addr_t end = 0;
struct arm_smccc_res smccc = { 0 };
struct loopbuffer_ctl_s *log_ctl = NULL;
arm_smccc_smc(OPTEE_SMC_ENABLE_LOGGER, 1, 0, 0, 0, 0, 0, 0,
&smccc);
if (smccc.a0 != TEEC_SUCCESS) {
pr_err("smc enable logger failed, res = 0x%lx\n", smccc.a0);
rc = -EACCES;
goto err;
}
if (smccc.a1 == LOGGER_SHM_ADDR_MAGIC) {
/*
* for new logger solution
* a1: logger get flags
* a2: logger start address
* a3: logger size
*/
begin = roundup(smccc.a2, PAGE_SIZE);
end = rounddown(smccc.a2 + smccc.a3, PAGE_SIZE);
size = end - begin;
log_buf_va = memremap(begin, size, MEMREMAP_WB);
} else {
/* for previous logger solution */
log_buf_va = va;
}
if (!log_buf_va) {
pr_err("shared memory logger ioremap failed\n");
return -ENOMEM;
}
log_ctl = (struct loopbuffer_ctl_s *)log_buf_va;
if (log_ctl->magic != LOGGER_LOOPBUFFER_MAGIC || log_ctl->inited != 1) {
pr_err("tee log buffer init failed\n");
rc = -EINVAL;
goto err;
}
/* init workqueue */
log_workqueue = create_singlethread_workqueue("tee-log-wq");
INIT_DELAYED_WORK(&log_work, do_log_timer);
if (queue_delayed_work(log_workqueue, &log_work, OPTEE_LOG_TIMER_INTERVAL * HZ) == 0) {
pr_err("%s:%d failed to join the workqueue.\n", __func__, __LINE__);
rc = -EBUSY;
goto err;
}
return rc;
err:
arm_smccc_smc(OPTEE_SMC_ENABLE_LOGGER, 0, 0, 0, 0, 0, 0, 0, &smccc);
return rc;
}
void optee_log_uninit(void)
{
struct arm_smccc_res smccc;
if (log_workqueue) {
cancel_delayed_work_sync(&log_work);
destroy_workqueue(log_workqueue);
}
arm_smccc_smc(OPTEE_SMC_ENABLE_LOGGER, 0, 0, 0, 0, 0, 0, 0, &smccc);
}