blob: 5a8e3771a8927b0f8b4c929b86853757700ba687 [file] [log] [blame]
/*
* Copyright (c) 2017, ASR Micro Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/uaccess.h>
#include <linux/tee_drv.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <soc/asr/ramdump.h>
#include "asr_tee_getmsg.h"
#include "asr_tee_spinlock.h"
#include "optee_smc.h"
#define VIRTUAL_UART_SIZE 0x3000
#define VIRTUART_STRING_MAX_SIZE 2048
#define SINGLE_PRINT_MAX_SIZE 512
static struct virtual_uart_buf *log_buffer = NULL;
static char virtuart_current_string[VIRTUART_STRING_MAX_SIZE];
static int ringbuf_sync(struct virtual_uart_buf *pbuf, char *string, int max_size)
{
uint32_t start, end, pos;
uint32_t i;
if (pbuf->flags & VIRTUART_FLAG_OVERFLOW) {
start = pbuf->write;
end = pbuf->write;
pbuf->flags &= ~VIRTUART_FLAG_OVERFLOW;
} else {
if (pbuf->read == pbuf->write)
return 1;
start = pbuf->read;
end = pbuf->write;
}
i = 0;
for (pos = start; pos != end; pos++, i++) {
if (pos >= pbuf->size)
pos -= pbuf->size;
string[i] = pbuf->buf[pos];
/* Cannot happen.. but nothing is impossible */
if (string[i] == 0 && i > 0)
break;
if (string[i] == '\n' && i > SINGLE_PRINT_MAX_SIZE) {
string[i+1] = 0;
pbuf->read = pos + 1;
return 0;
}
/* long string? */
if (i == (max_size - 3)) {
/* use '*' to indicate message has been droped */
string[max_size - 2] = '*';
string[max_size - 1] = 0;
pbuf->read = pbuf->write;
return 0;
}
}
if (i > 1)
string[i] = 0;
pbuf->read = pbuf->write;
return 0;
}
char *get_msg_from_tee(void)
{
char *p = NULL;
unsigned long flags;
/* Do not touch pbuf if not initialized by optee os */
if (log_buffer->magic != VIRTUART_MAGIC_NUMBER)
return 0;
memset(virtuart_current_string, 0x0, VIRTUART_STRING_MAX_SIZE);
local_irq_save(flags);
local_fiq_disable();
optee_spin_lock(&log_buffer->lock);
if(ringbuf_sync(log_buffer, virtuart_current_string,
VIRTUART_STRING_MAX_SIZE) == 0)
p = virtuart_current_string;
optee_spin_unlock(&log_buffer->lock);
local_fiq_enable();
local_irq_restore(flags);
return p;
}
void print_msg_from_tee(void)
{
char *p = NULL;
if (log_buffer == NULL)
return;
do {
p = get_msg_from_tee();
if (p != NULL)
pr_alert("%s", p);
}while(p != NULL);
return;
}
#define OPTEE_SMC_FUNCID_DELIVER_VIRTUART 19
#define OPTEE_SMC_CALL_DELIVER_VIRTUART \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DELIVER_VIRTUART)
static bool optee_msg_deliver_virtual_uart_addr(optee_invoke_fn *invoke_fn, phys_addr_t addr)
{
struct arm_smccc_res res;
u32 addr1, addr2;
#ifdef CONFIG_PHYS_ADDR_T_64BIT
addr1 = ((u64)addr >> 32) & 0xFFFFFFFF;
#else
addr1 = 0;
#endif
addr2 = addr & 0xFFFFFFFF;
invoke_fn(OPTEE_SMC_CALL_DELIVER_VIRTUART, addr1, addr2, VIRTUAL_UART_SIZE, 0, 0, 0, 0, &res);
if (res.a0 == 0)
return true;
return false;
}
void tee_setup_virtual_uart(optee_invoke_fn *invoke_fn)
{
log_buffer = kmalloc(VIRTUAL_UART_SIZE, GFP_KERNEL | __GFP_ZERO);
if (!log_buffer) {
pr_warn("can't alloc memory for optee virtual uart!\n");
return;
}
pr_info("optee virtual uart log addr:0x%llx\n", (u64)virt_to_phys(log_buffer));
/* the fields of log_buffer are initilized by opteeos */
if(!optee_msg_deliver_virtual_uart_addr(invoke_fn, virt_to_phys(log_buffer))) {
pr_warn("can't set up virtual uart buffer in optee!\n");
kfree(log_buffer);
return;
}
return;
}