tegrakernel/kernel/nvidia/security/tlk_driver/ote_log.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);