212 lines
5.4 KiB
C
212 lines
5.4 KiB
C
|
/*
|
||
|
* Copyright (c) 2013-2018 NVIDIA Corporation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License along
|
||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/syscalls.h>
|
||
|
#include <linux/list.h>
|
||
|
#include <linux/completion.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/bitops.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
|
||
|
#include <asm/page.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
|
||
|
#include "ote_protocol.h"
|
||
|
|
||
|
#define LOGBUF_SIZE 8192
|
||
|
|
||
|
struct circular_buffer {
|
||
|
uint32_t size; /* Indicates the total size of the buffer */
|
||
|
uint32_t start; /* Starting point of valid data in buffer */
|
||
|
uint32_t end; /* First character which is empty (can be written to) */
|
||
|
uint32_t overflow; /* Indicator whether buffer has overwritten itself */
|
||
|
char *buf;
|
||
|
};
|
||
|
|
||
|
static int ote_logging_enabled;
|
||
|
struct circular_buffer *cb;
|
||
|
|
||
|
/*
|
||
|
* Initialize the shared buffer for TLK logging.
|
||
|
* The shared buffer is allocated in DMA memory to get uncached memory
|
||
|
* since TLK directly writes to the physical address of the shared buffer.
|
||
|
* The structure is declared in DMA memory too since it's members will
|
||
|
* also be updated by the TLK directly to their physical addresses.
|
||
|
*/
|
||
|
static int circ_buf_init(struct circular_buffer **cbptr)
|
||
|
{
|
||
|
|
||
|
dma_addr_t tp;
|
||
|
|
||
|
*cbptr = (struct circular_buffer *) dma_alloc_coherent(NULL,
|
||
|
sizeof(struct circular_buffer), &tp, GFP_KERNEL);
|
||
|
if (!*cbptr) {
|
||
|
pr_err("%s: no memory available for circular buffer struct\n",
|
||
|
__func__);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
memset(*cbptr, 0, sizeof(struct circular_buffer));
|
||
|
|
||
|
(*cbptr)->start = 0;
|
||
|
(*cbptr)->end = 0;
|
||
|
(*cbptr)->size = LOGBUF_SIZE;
|
||
|
|
||
|
(*cbptr)->buf = (char *) dma_alloc_coherent(NULL, LOGBUF_SIZE,
|
||
|
&tp, GFP_KERNEL);
|
||
|
if (!(*cbptr)->buf) {
|
||
|
pr_err("%s: no memory available for shared buffer\n",
|
||
|
__func__);
|
||
|
/* Frees the memory allocated using dma_alloc_coherent */
|
||
|
dma_free_coherent(NULL,
|
||
|
sizeof(struct circular_buffer), cbptr, tp);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
memset((*cbptr)->buf, 0, LOGBUF_SIZE);
|
||
|
|
||
|
(*cbptr)->overflow = 0;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Copy the contents of the circular buffer into a char buffer in order.
|
||
|
* This helps to treat the buffer like a string and use it to tokenize it
|
||
|
* into lines, tag and display it.
|
||
|
*/
|
||
|
static int circ_buf_copy(struct circular_buffer *cb, char *text)
|
||
|
{
|
||
|
if (cb->end == cb->start)
|
||
|
return 0;
|
||
|
|
||
|
if (cb->end > cb->start) {
|
||
|
if (abs(cb->end - cb->start) > LOGBUF_SIZE) {
|
||
|
pr_err("%s: cbuf pointers corrupted\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
memcpy(text, cb->buf + cb->start, cb->end - cb->start);
|
||
|
|
||
|
} else if (cb->start > cb->end) {
|
||
|
if (abs(cb->end - cb->start) > LOGBUF_SIZE) {
|
||
|
pr_err("%s: cbuf pointers corrupted\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
memcpy(text, cb->buf + cb->start, cb->size - cb->start);
|
||
|
memcpy(text + cb->size - cb->start, cb->buf, cb->end);
|
||
|
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function which prints TLK logs.
|
||
|
* Tokenizes the TLK logs into lines, tags each line
|
||
|
* and prints it out to kmsg file.
|
||
|
*/
|
||
|
void ote_print_logs(void)
|
||
|
{
|
||
|
char *text = NULL;
|
||
|
char *temp = NULL;
|
||
|
char *buffer = NULL;
|
||
|
|
||
|
if (!ote_logging_enabled)
|
||
|
return;
|
||
|
|
||
|
buffer = kzalloc(LOGBUF_SIZE, GFP_KERNEL);
|
||
|
if (!buffer) {
|
||
|
pr_err("%s: memory allocation failed\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* This detects if the buffer proved to be too small to hold the data.
|
||
|
* If buffer is not large enough, it overwrites it's oldest data,
|
||
|
* This warning serves to alert the user to possibly use a bigger buffer
|
||
|
*/
|
||
|
if (cb->overflow == 1) {
|
||
|
pr_warn("[TLK] **WARNING** TLK buffer overwritten.\n");
|
||
|
cb->overflow = 0;
|
||
|
}
|
||
|
|
||
|
if (circ_buf_copy(cb, buffer) != 0) {
|
||
|
kfree(buffer);
|
||
|
return;
|
||
|
}
|
||
|
cb->buf[cb->end] = '\0';
|
||
|
|
||
|
/*
|
||
|
* In case no delimiter was found, strsep returns the entire string as
|
||
|
* token and the original string is made as NULL
|
||
|
*/
|
||
|
text = buffer;
|
||
|
temp = strsep(&text, "\n");
|
||
|
while (temp != NULL) {
|
||
|
if (strnlen(temp, LOGBUF_SIZE))
|
||
|
pr_info("[TLK] %s\n", temp);
|
||
|
temp = strsep(&text, "\n");
|
||
|
}
|
||
|
|
||
|
/* Indicate that buffer is empty */
|
||
|
cb->start = cb->end;
|
||
|
kfree(buffer);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Call function to initialize circular buffer.
|
||
|
* An SMC is made to send the virtual address of the structure to
|
||
|
* the secure OS.
|
||
|
*/
|
||
|
static int ote_logger_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
if (circ_buf_init(&cb) != 0)
|
||
|
return -1;
|
||
|
|
||
|
/* enable logging only if secure firmware supports it */
|
||
|
if (!tlk_send_smc(TE_SMC_INIT_LOGGER, (uintptr_t)cb, 0))
|
||
|
ote_logging_enabled = 1;
|
||
|
|
||
|
ote_print_logs();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id ote_logger_of_match[] = {
|
||
|
{ .compatible = "android,ote-logger",},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
static struct platform_driver ote_logger_driver = {
|
||
|
.probe = ote_logger_probe,
|
||
|
.driver = {
|
||
|
.name = "ote-logger",
|
||
|
.owner = THIS_MODULE,
|
||
|
.of_match_table = ote_logger_of_match,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static int __init ote_logger_driver_init(void)
|
||
|
{
|
||
|
return platform_driver_register(&ote_logger_driver);
|
||
|
}
|
||
|
|
||
|
subsys_initcall(ote_logger_driver_init);
|