/* * 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 #include #include #include #include #include #include #include #include #include #include #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);