initial import

internal commit: 14ac2bc2299032fa6714d1fefa7cf0987b3e3085

Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Eddie Dong
2018-03-07 20:57:14 +08:00
committed by Jack Ren
commit f4cd4338fd
156 changed files with 41265 additions and 0 deletions

236
debug/console.c Normal file
View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "serial_internal.h"
static spinlock_t lock;
static uint32_t serial_handle = SERIAL_INVALID_HANDLE;
#define CONSOLE_KICK_TIMER_TIMEOUT 40 /* timeout is 40ms*/
uint32_t get_serial_handle(void)
{
return serial_handle;
}
static int print_char(char x)
{
serial_puts(serial_handle, &x, 1);
if (x == '\n')
serial_puts(serial_handle, "\r", 1);
return 0;
}
int console_init(void)
{
spinlock_init(&lock);
serial_handle = serial_open("STDIO");
return 0;
}
int console_putc(int ch)
{
int res = -1;
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE)
res = print_char(ch);
spinlock_release(&lock);
return res;
}
int console_puts(const char *s)
{
int res = -1;
const char *p;
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE) {
res = 0;
while (*s) {
/* start output at the beginning of the string search
* for end of string or '\n'
*/
p = s;
while (*p && *p != '\n')
++p;
/* write all characters up to p */
serial_puts(serial_handle, s, p - s);
res += p - s;
if (*p == '\n') {
print_char('\n');
++p;
res += 2;
}
/* continue at position p */
s = p;
}
}
spinlock_release(&lock);
return res;
}
int console_write(const char *s, size_t len)
{
int res = -1;
const char *e;
const char *p;
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE) {
/* calculate pointer to the end of the string */
e = s + len;
res = 0;
/* process all characters */
while (s != e) {
/* search for '\n' or the end of the string */
p = s;
while ((p != e) && (*p != '\n'))
++p;
/* write all characters processed so far */
serial_puts(serial_handle, s, p - s);
res += p - s;
/* write '\n' if end of string is not reached */
if (p != e) {
print_char('\n');
++p;
res += 2;
}
/* continue at next position */
s = p;
}
}
spinlock_release(&lock);
return res;
}
void console_dump_bytes(const void *p, unsigned int len)
{
const unsigned char *x = p;
const unsigned char *e = x + len;
int i;
/* dump all bytes */
while (x < e) {
/* write the address of the first byte in the row */
printf("%08x: ", (vaddr_t) x);
/* print one row (16 bytes) as hexadecimal values */
for (i = 0; i < 16; i++)
printf("%02x ", x[i]);
/* print one row as ASCII characters (if possible) */
for (i = 0; i < 16; i++) {
if ((x[i] < ' ') || (x[i] >= 127))
console_putc('.');
else
console_putc(x[i]);
}
/* continue with next row */
console_putc('\n');
/* set pointer one row ahead */
x += 16;
}
}
static void console_read(void)
{
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE) {
/* Get all the data available in the RX FIFO */
serial_get_rx_data(serial_handle);
}
spinlock_release(&lock);
}
static void console_handler(void)
{
/* Dump the RX FIFO to a circular buffer */
console_read();
/* serial Console Rx operation */
vuart_console_rx_chars(serial_handle);
/* serial Console Tx operation */
vuart_console_tx_chars();
shell_kick_session();
}
static int console_timer_callback(__unused uint64_t data)
{
/* Kick HV-Shell and Uart-Console tasks */
console_handler();
/* Restart the timer */
console_setup_timer();
return 0;
}
void console_setup_timer(void)
{
/* Start an one-shot timer */
if (add_timer(console_timer_callback, 0,
rdtsc() + TIME_MS_DELTA * CONSOLE_KICK_TIMER_TIMEOUT) < 0)
pr_err("Failed to add console kick timer");
}

368
debug/dump.c Normal file
View File

@@ -0,0 +1,368 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
/*
* readable exception descriptors.
*/
static const char *const excp_names[] = {
[0] = "Divide Error",
[1] = "RESERVED",
[2] = "NMI",
[3] = "Breakpoint",
[4] = "Overflow",
[5] = "BOUND range exceeded",
[6] = "Invalid Opcode",
[7] = "Device Not Available",
[8] = "Double Fault",
[9] = "Coprocessor Segment Overrun",
[10] = "Invalid TSS",
[11] = "Segment Not Present",
[12] = "Stack Segment Fault",
[13] = "General Protection",
[14] = "Page Fault",
[15] = "Intel Reserved",
[16] = "x87 FPU Floating Point Error",
[17] = "Alignment Check",
[18] = "Machine Check",
[19] = "SIMD Floating Point Exception",
[20] = "Virtualization Exception",
[21] = "Intel Reserved",
[22] = "Intel Reserved",
[23] = "Intel Reserved",
[24] = "Intel Reserved",
[25] = "Intel Reserved",
[26] = "Intel Reserved",
[27] = "Intel Reserved",
[28] = "Intel Reserved",
[29] = "Intel Reserved",
[30] = "Intel Reserved",
[31] = "Intel Reserved"
};
static void dump_guest_reg(struct vcpu *vcpu)
{
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
printf("\n\n================================================");
printf("================================\n\n");
printf("Guest Registers:\r\n");
printf("= VM ID %d ==== vCPU ID %d === pCPU ID %d ===="
"world %d =============\r\n",
vcpu->vm->attr.id, vcpu->vcpu_id, vcpu->pcpu_id,
vcpu->arch_vcpu.cur_context);
printf("= RIP=0x%016llx RSP=0x%016llx "
"RFLAGS=0x%016llx\r\n",
cur_context->rip,
cur_context->rsp,
cur_context->rflags);
printf("= CR0=0x%016llx CR2=0x%016llx "
" CR3=0x%016llx\r\n",
cur_context->cr0,
cur_context->cr2,
cur_context->cr3);
printf("= RAX=0x%016llx RBX=0x%016llx "
"RCX=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.rax,
cur_context->guest_cpu_regs.regs.rbx,
cur_context->guest_cpu_regs.regs.rcx);
printf("= RDX=0x%016llx RDI=0x%016llx "
"RSI=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.rdx,
cur_context->guest_cpu_regs.regs.rdi,
cur_context->guest_cpu_regs.regs.rsi);
printf("= RBP=0x%016llx R8=0x%016llx "
"R9=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.rbp,
cur_context->guest_cpu_regs.regs.r8,
cur_context->guest_cpu_regs.regs.r9);
printf("= R10=0x%016llx R11=0x%016llx "
"R12=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.r10,
cur_context->guest_cpu_regs.regs.r11,
cur_context->guest_cpu_regs.regs.r12);
printf("= R13=0x%016llx R14=0x%016llx "
"R15=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.r13,
cur_context->guest_cpu_regs.regs.r14,
cur_context->guest_cpu_regs.regs.r15);
printf("\r\n");
}
static void dump_guest_stack(struct vcpu *vcpu)
{
uint64_t gpa;
uint64_t hpa;
uint32_t i;
uint64_t *tmp;
uint64_t page1_size;
uint64_t page2_size;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
gpa = gva2gpa(vcpu->vm, cur_context->cr3, cur_context->rsp);
hpa = gpa2hpa(vcpu->vm, gpa);
printf("\r\nGuest Stack:\r\n");
printf("Dump stack for vcpu %d, from gva 0x%016llx ->"
"gpa 0x%016llx -> hpa 0x%016llx \r\n",
vcpu->vcpu_id, cur_context->rsp, gpa, hpa);
/* Need check if cross 2 pages*/
if (((cur_context->rsp % CPU_PAGE_SIZE) + DUMP_STACK_SIZE)
<= CPU_PAGE_SIZE) {
tmp = HPA2HVA(hpa);
for (i = 0; i < DUMP_STACK_SIZE/32; i++) {
printf("addr(0x%llx): 0x%016llx 0x%016llx "
"0x%016llx 0x%016llx\r\n", (hpa+i*32),
tmp[i*4], tmp[i*4+1],
tmp[i*4+2], tmp[i*4+3]);
}
} else {
tmp = HPA2HVA(hpa);
page1_size = CPU_PAGE_SIZE
- (cur_context->rsp % CPU_PAGE_SIZE);
for (i = 0; i < page1_size/32; i++) {
printf("addr(0x%llx): 0x%016llx 0x%016llx 0x%016llx "
"0x%016llx\r\n", (hpa+i*32), tmp[i*4],
tmp[i*4+1], tmp[i*4+2], tmp[i*4+3]);
}
gpa = gva2gpa(vcpu->vm, cur_context->cr3,
cur_context->rsp + page1_size);
hpa = gpa2hpa(vcpu->vm, gpa);
printf("Dump stack for vcpu %d, from gva 0x%016llx ->"
"gpa 0x%016llx -> hpa 0x%016llx \r\n",
vcpu->vcpu_id, cur_context->rsp + page1_size,
gpa, hpa);
tmp = HPA2HVA(hpa);
page2_size = DUMP_STACK_SIZE - page1_size;
for (i = 0; i < page2_size/32; i++) {
printf("addr(0x%llx): 0x%016llx 0x%016llx 0x%016llx "
"0x%016llx\r\n", (hpa+i*32), tmp[i*4],
tmp[i*4+1], tmp[i*4+2], tmp[i*4+3]);
}
}
printf("\r\n");
}
static void show_guest_call_trace(struct vcpu *vcpu)
{
uint64_t gpa;
uint64_t hpa;
uint64_t *hva;
uint64_t bp;
uint64_t count = 0;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
bp = cur_context->guest_cpu_regs.regs.rbp;
printf("Guest Call Trace: **************************************\r\n");
printf("Maybe the call trace is not accurate, pls check stack!!\r\n");
/* if enable compiler option(no-omit-frame-pointer) the stack layout
* should be like this when call a function for x86_64
*
* | |
* rbp+8 | return address |
* rbp | rbp | push rbp
* | | mov rsp rbp
*
* rsp | |
*
* try to print out call trace,here can not check if the rbp is valid
* if the address is invalid, it will cause hv page fault
* then halt system */
while ((count++ < CALL_TRACE_HIERARCHY_MAX) && (bp != 0)) {
gpa = gva2gpa(vcpu->vm, cur_context->cr3, bp);
hpa = gpa2hpa(vcpu->vm, gpa);
hva = HPA2HVA(hpa);
printf("BP_GVA(0x%016llx)->BP_GPA(0x%016llx)"
"->BP_HPA(0x%016llx) RIP=0x%016llx\r\n", bp, gpa, hpa,
*(uint64_t *)((uint64_t)hva + sizeof(uint64_t)));
/* Get previous rbp*/
bp = *hva;
}
printf("\r\n");
}
static void dump_guest_context(uint32_t cpu_id)
{
struct vcpu *vcpu;
vcpu = per_cpu(vcpu, cpu_id);
if (vcpu != NULL) {
dump_guest_reg(vcpu);
dump_guest_stack(vcpu);
show_guest_call_trace(vcpu);
}
}
static void show_host_call_trace(uint64_t rsp, uint64_t rbp, uint32_t cpu_id)
{
int i = 0;
int cb_hierarchy = 0;
uint64_t *sp = (uint64_t *)rsp;
printf("\r\nHost Stack: \r\n");
for (i = 0; i < DUMP_STACK_SIZE/32; i++) {
printf("addr(0x%llx) 0x%016llx 0x%016llx 0x%016llx "
"0x%016llx\r\n", (rsp+i*32), sp[i*4], sp[i*4+1],
sp[i*4+2], sp[i*4+3]);
}
printf("\r\n");
printf("Host Call Trace:\r\n");
if (rsp >
(uint64_t)&per_cpu(stack, cpu_id)[STACK_SIZE - 1]
|| rsp < (uint64_t)&per_cpu(stack, cpu_id)[0]) {
return;
}
/* if enable compiler option(no-omit-frame-pointer) the stack layout
* should be like this when call a function for x86_64
*
* | |
* rbp+8 | return address |
* rbp | rbp | push rbp
* | | mov rsp rbp
*
* rsp | |
*
*
* if the address is invalid, it will cause hv page fault
* then halt system */
while ((rbp <=
(uint64_t)&per_cpu(stack, cpu_id)[STACK_SIZE - 1])
&& (rbp >= (uint64_t)&per_cpu(stack, cpu_id)[0])
&& (cb_hierarchy++ < CALL_TRACE_HIERARCHY_MAX)) {
printf("----> 0x%016llx\r\n",
*(uint64_t *)(rbp + sizeof(uint64_t)));
if (*(uint64_t *)(rbp + 2*sizeof(uint64_t))
== SP_BOTTOM_MAGIC) {
break;
}
rbp = *(uint64_t *)rbp;
}
printf("\r\n");
}
void __assert(uint32_t line, const char *file, char *txt)
{
uint32_t cpu_id = get_cpu_id();
uint64_t rsp = cpu_rsp_get();
uint64_t rbp = cpu_rbp_get();
pr_fatal("Assertion failed in file %s,line %u : %s",
file, line, txt);
show_host_call_trace(rsp, rbp, cpu_id);
dump_guest_context(cpu_id);
do {
asm volatile ("pause" ::: "memory");
} while (1);
}
void dump_exception(struct intr_ctx *ctx, uint32_t cpu_id)
{
const char *name = "Not defined";
static int nested = 1;
/* avoid endless loop, only dump the first exception */
if (nested++ > 1)
return;
if (ctx->vector < 0x20)
name = excp_names[ctx->vector];
printf("\n\n================================================");
printf("================================\n=\n");
printf("= Unhandled exception: %d (%s)\n", ctx->vector, name);
printf("= CPU ID = %d", cpu_id);
/* Dump host register*/
printf("\r\nHost Registers:\r\n");
printf("= Vector=0x%016llX RIP=0x%016llX\n",
ctx->vector, ctx->rip);
printf("= RAX=0x%016llX RBX=0x%016llX RCX=0x%016llX\n",
ctx->rax, ctx->rbx, ctx->rcx);
printf("= RDX=0x%016llX RDI=0x%016llX RSI=0x%016llX\n",
ctx->rdx, ctx->rdi, ctx->rsi);
printf("= RSP=0x%016llX RBP=0x%016llX RBX=0x%016llX\n",
ctx->rsp, ctx->rbp, ctx->rbx);
printf("= R8=0x%016llX R9=0x%016llX R10=0x%016llX\n",
ctx->r8, ctx->r9, ctx->r10);
printf("= R11=0x%016llX R12=0x%016llX R13=0x%016llX\n",
ctx->r11, ctx->r12, ctx->r13);
printf("= RFLAGS=0x%016llX R14=0x%016llX R15=0x%016llX\n",
ctx->rflags, ctx->r14, ctx->r15);
printf("= ERRCODE=0x%016llX CS=0x%016llX SS=0x%016llX\n",
ctx->error_code, ctx->cs, ctx->ss);
printf("\r\n");
/* Dump host stack */
show_host_call_trace(ctx->rsp, ctx->rbp, cpu_id);
/* Dump guest context */
dump_guest_context(cpu_id);
printf("= System halted\n");
printf("=====================================================");
printf("===========================\n");
}
void dump_interrupt(struct intr_ctx *ctx)
{
printf("\n\n==========================================");
printf("======================================\n=\n");
printf("\n=\n");
printf("= Vector=0x%016llX RIP=0x%016llX\n",
ctx->vector, ctx->rip);
printf("= RAX=0x%016llX RBX=0x%016llX RCX=0x%016llX\n",
ctx->rax, ctx->rbx, ctx->rcx);
printf("= RDX=0x%016llX RDI=0x%016llX RSI=0x%016llX\n",
ctx->rdx, ctx->rdi, ctx->rsi);
printf("= RSP=0x%016llX RBP=0x%016llX RBX=0x%016llX\n",
ctx->rsp, ctx->rbp, ctx->rbx);
printf("= R8=0x%016llX R9=0x%016llX R10=0x%016llX\n",
ctx->r8, ctx->r9, ctx->r10);
printf("= R11=0x%016llX R12=0x%016llX R13=0x%016llX\n",
ctx->r11, ctx->r12, ctx->r13);
printf("= RFLAGS=0x%016llX R14=0x%016llX R15=0x%016llX\n",
ctx->rflags, ctx->r14, ctx->r15);
printf("= ERRCODE=0x%016llX CS=0x%016llX SS=0x%016llX\n",
ctx->error_code, ctx->cs, ctx->ss);
printf("=\n");
printf("= system halted\n");
printf("===============================================");
printf("=================================\n");
}

161
debug/logmsg.c Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define LOG_ENTRY_SIZE 80
/* Size of buffer used to store a message being logged,
* should align to LOG_ENTRY_SIZE.
*/
#define LOG_MESSAGE_MAX_SIZE (4 * LOG_ENTRY_SIZE)
DEFINE_CPU_DATA(char [LOG_MESSAGE_MAX_SIZE], logbuf);
struct logmsg {
uint32_t flags;
unsigned int seq;
spinlock_t lock;
};
static struct logmsg logmsg;
void init_logmsg(__unused uint32_t mem_size, uint32_t flags)
{
logmsg.flags = flags;
logmsg.seq = 0;
}
void do_logmsg(uint32_t severity, const char *fmt, ...)
{
va_list args;
uint64_t timestamp;
uint32_t cpu_id;
bool do_console_log;
bool do_mem_log;
char *buffer;
spinlock_rflags;
do_console_log = ((logmsg.flags & LOG_FLAG_STDOUT) &&
(severity <= console_loglevel));
do_mem_log = ((logmsg.flags & LOG_FLAG_MEMORY) &&
(severity <= mem_loglevel));
if (!do_console_log && !do_mem_log)
return;
/* Get time-stamp value */
timestamp = rdtsc();
/* Scale time-stamp appropriately */
timestamp = TICKS_TO_US(timestamp);
/* Get CPU ID */
cpu_id = get_cpu_id();
buffer = per_cpu(logbuf, cpu_id);
memset(buffer, 0, LOG_MESSAGE_MAX_SIZE);
/* Put time-stamp, CPU ID and severity into buffer */
snprintf(buffer, LOG_MESSAGE_MAX_SIZE,
"[%lluus][cpu=%u][sev=%u][seq=%u]:",
timestamp, cpu_id, severity,
atomic_inc_return(&logmsg.seq));
/* Put message into remaining portion of local buffer */
va_start(args, fmt);
vsnprintf(buffer + strnlen_s(buffer, LOG_MESSAGE_MAX_SIZE),
LOG_MESSAGE_MAX_SIZE
- strnlen_s(buffer, LOG_MESSAGE_MAX_SIZE), fmt, args);
va_end(args);
/* Check if flags specify to output to stdout */
if (do_console_log) {
spinlock_irqsave_obtain(&(logmsg.lock));
/* Send buffer to stdout */
printf("%s\n\r", buffer);
spinlock_irqrestore_release(&(logmsg.lock));
}
/* Check if flags specify to output to memory */
if (do_mem_log) {
int i, msg_len;
struct shared_buf *sbuf = (struct shared_buf *)
per_cpu(sbuf, cpu_id)[ACRN_HVLOG];
if (sbuf != NULL) {
msg_len = strnlen_s(buffer, LOG_MESSAGE_MAX_SIZE);
for (i = 0; i < (msg_len - 1) / LOG_ENTRY_SIZE + 1;
i++) {
sbuf_put(sbuf, (uint8_t *)buffer +
i * LOG_ENTRY_SIZE);
}
}
}
}
void print_logmsg_buffer(uint32_t cpu_id)
{
spinlock_rflags;
char buffer[LOG_ENTRY_SIZE + 1];
int read_cnt;
struct shared_buf *sbuf;
if (cpu_id >= (uint32_t)phy_cpu_num)
return;
sbuf = (struct shared_buf *)per_cpu(sbuf, cpu_id)[ACRN_HVLOG];
if (sbuf != NULL) {
spinlock_irqsave_obtain(&(logmsg.lock));
printf("CPU%d: head: 0x%x, tail: 0x%x\n\r",
cpu_id, sbuf->head, sbuf->tail);
spinlock_irqrestore_release(&(logmsg.lock));
do {
memset(buffer, 0, LOG_ENTRY_SIZE + 1);
read_cnt = sbuf_get(sbuf, (uint8_t *)buffer);
if (read_cnt > 0) {
uint32_t idx;
idx = (read_cnt < LOG_ENTRY_SIZE) ?
read_cnt : LOG_ENTRY_SIZE;
buffer[idx] = '\0';
spinlock_irqsave_obtain(&(logmsg.lock));
printf("%s\n\r", buffer);
spinlock_irqrestore_release(&(logmsg.lock));
}
} while (read_cnt > 0);
}
}

824
debug/printf.c Normal file
View File

@@ -0,0 +1,824 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#ifndef NULL
#define NULL ((void *) 0)
#endif
#define PRINT_STRING_MAX_LEN 4096
/** Command for the emit function: copy string to output. */
#define PRINT_CMD_COPY 0x00000000
/** Command for the emit function: fill output with first character. */
#define PRINT_CMD_FILL 0x00000001
/** Use upper case letters for hexadecimal format. */
#define PRINT_FLAG_UPPER 0x00000001
/** Use alternate form. */
#define PRINT_FLAG_ALTERNATE_FORM 0x00000002
/** Use '0' instead of ' ' for padding. */
#define PRINT_FLAG_PAD_ZERO 0x00000004
/** Use left instead of right justification. */
#define PRINT_FLAG_LEFT_JUSTIFY 0x00000008
/** Always use the sign as prefix. */
#define PRINT_FLAG_SIGN 0x00000010
/** Use ' ' as prefix if no sign is used. */
#define PRINT_FLAG_SPACE 0x00000020
/** The original value was a (unsigned) char. */
#define PRINT_FLAG_CHAR 0x00000040
/** The original value was a (unsigned) short. */
#define PRINT_FLAG_SHORT 0x00000080
/** The original value was a (unsigned) long. */
#define PRINT_FLAG_LONG 0x00000100
/** The original value was a (unsigned) long long. */
#define PRINT_FLAG_LONG_LONG 0x00000200
/** The value is interpreted as unsigned. */
#define PRINT_FLAG_UINT32 0x00000400
/** Structure used to parse parameters and variables to subroutines. */
struct print_param {
/** A pointer to the function that is used to emit characters. */
int (*emit)(int, const char *, int, void *);
/** An opaque pointer that is passed as third argument to the emit
* function.
*/
void *data;
/** Contains variables which are recalculated for each argument. */
struct {
/** A bitfield with the parsed format flags. */
int flags;
/** The parsed format width. */
int width;
/** The parsed format precision. */
int precision;
/** The bitmask for unsigned values. */
unsigned long long mask;
/** A pointer to the preformated value. */
const char *value;
/* The number of characters in the preformated value buffer. */
uint32_t valuelen;
/** A pointer to the values prefix. */
const char *prefix;
/** The number of characters in the prefix buffer. */
uint32_t prefixlen;
} vars;
};
/** Structure used to save (v)snprintf() specific values */
struct snprint_param {
/** The destination buffer. */
char *dst;
/** The size of the destination buffer. */
int sz;
/** Counter for written chars. */
int wrtn;
};
/** The characters to use for upper case hexadecimal conversion.
*
* Note that this array is 17 bytes long. The first 16 characters
* are used to convert a 4 bit number to a printable character.
* The last character is used to determine the prefix for the
* alternate form.
*/
static const char upper_hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'X'
};
/** The characters to use for lower case hexadecimal conversion.
*
* Note that this array is 17 bytes long. The first 16 characters
* are used to convert a 4 bit number to a printable character.
* The last character is used to determine the prefix for the
* alternate form.
*/
static const char lower_hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'x'
};
static const char *get_int(const char *s, int *x)
{
int negative = 0;
*x = 0;
/* evaluate leading '-' for negative numbers */
if (*s == '-') {
negative = 1;
++s;
}
/* parse uint32_teger */
while ((*s >= '0') && (*s <= '9'))
*x = *x * 10 + (*s++ - '0');
/* apply sign to result */
if (negative)
*x = -*x;
return s;
}
static const char *get_flags(const char *s, int *flags)
{
/* contains the flag characters */
static const char flagchars[] = "#0- +";
/* contains the numeric flags for the characters above */
static const int fl[sizeof(flagchars)] = {
PRINT_FLAG_ALTERNATE_FORM, /* # */
PRINT_FLAG_PAD_ZERO, /* 0 */
PRINT_FLAG_LEFT_JUSTIFY, /* - */
PRINT_FLAG_SIGN, /* + */
PRINT_FLAG_SPACE /* ' ' */
};
const char *pos;
/* parse multiple flags */
while (*s) {
/* get index of flag. Terminate loop if no flag character was
* found
*/
pos = strchr(flagchars, *s);
if (pos == 0)
break;
/* apply matching flags and continue with the next character */
++s;
*flags |= fl[pos - flagchars];
}
/* Spec says that '-' has a higher priority than '0' */
if (*flags & PRINT_FLAG_LEFT_JUSTIFY)
*flags &= ~PRINT_FLAG_PAD_ZERO;
/* Spec says that '+' has a higher priority than ' ' */
if (*flags & PRINT_FLAG_SIGN)
*flags &= ~PRINT_FLAG_SPACE;
return s;
}
static const char *get_length_modifier(const char *s,
int *flags, unsigned long long *mask)
{
/* check for h[h] (char/short) */
if (*s == 'h') {
if (*++s == 'h') {
*flags |= PRINT_FLAG_CHAR;
*mask = 0x000000FF;
++s;
} else {
*flags |= PRINT_FLAG_SHORT;
*mask = 0x0000FFFF;
}
}
/* check for l[l] (long/long long) */
else if (*s == 'l') {
if (*++s == 'l') {
*flags |= PRINT_FLAG_LONG_LONG;
++s;
} else
*flags |= PRINT_FLAG_LONG;
}
return s;
}
static int format_number(struct print_param *param)
{
/* contains the character used for padding */
char pad;
/* effective width of the result */
uint32_t width;
/* number of characters to insert for width (w) and precision (p) */
uint32_t p, w;
/* the result */
int res;
/* initialize variables */
p = w = 0;
res = 0;
width = param->vars.valuelen + param->vars.prefixlen;
/* calculate additional characters for precision */
if ((uint32_t)(param->vars.precision) > width)
p = param->vars.precision - width;
/* calculate additional characters for width */
if ((uint32_t)(param->vars.width) > (width + p))
w = param->vars.width - (width + p);
/* handle case of right justification */
if ((param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) == 0) {
/* assume ' ' as padding character */
pad = ' ';
/*
* if padding with 0 is used, we have to emit the prefix (if any
* ) first to achieve the expected result. However, if a blank is
* used for padding, the prefix is emitted after the padding.
*/
if (param->vars.flags & PRINT_FLAG_PAD_ZERO) {
/* use '0' for padding */
pad = '0';
/* emit prefix, return early if an error occurred */
res = param->emit(PRINT_CMD_COPY, param->vars.prefix,
param->vars.prefixlen, param->data);
if (param->vars.prefix && (res < 0))
return res;
/* invalidate prefix */
param->vars.prefix = 0;
param->vars.prefixlen = 0;
}
/* fill the width with the padding character, return early if
* an error occurred
*/
res = param->emit(PRINT_CMD_FILL, &pad, w, param->data);
if (res < 0)
return res;
}
/* emit prefix (if any), return early in case of an error */
res = param->emit(PRINT_CMD_COPY, param->vars.prefix,
param->vars.prefixlen, param->data);
if (param->vars.prefix && (res < 0))
return res;
/* insert additional 0's for precision, return early if an error
* occurred
*/
res = param->emit(PRINT_CMD_FILL, "0", p, param->data);
if (res < 0)
return res;
/* emit the pre-calculated result, return early in case of an error */
res = param->emit(PRINT_CMD_COPY, param->vars.value,
param->vars.valuelen, param->data);
if (res < 0)
return res;
/* handle left justification */
if ((param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) != 0) {
/* emit trailing blanks, return early in case of an error */
res = param->emit(PRINT_CMD_FILL, " ", w, param->data);
if (res < 0)
return res;
}
/* done, return the last result */
return res;
}
static int print_pow2(struct print_param *param,
unsigned long long v, uint32_t shift)
{
/* max buffer required for octal representation of unsigned long long */
char digitbuff[22];
/* Insert position for the next character+1 */
char *pos = digitbuff + sizeof(digitbuff);
/* buffer for the 0/0x/0X prefix */
char prefix[2];
/* pointer to the digits translation table */
const char *digits;
/* mask to extract next character */
unsigned long long mask;
int ret;
/* calculate mask */
mask = (1ULL << shift) - 1;
/* determine digit translation table */
digits = (param->vars.flags & PRINT_FLAG_UPPER) ?
upper_hex_digits : lower_hex_digits;
/* apply mask for short/char */
v &= param->vars.mask;
/* determine prefix for alternate form */
if ((v == 0) && (param->vars.flags & PRINT_FLAG_ALTERNATE_FORM)) {
prefix[0] = '0';
param->vars.prefix = prefix;
param->vars.prefixlen = 1;
if (shift == 4) {
param->vars.prefixlen = 2;
prefix[1] = digits[16];
}
}
/* determine digits from right to left */
do {
*--pos = digits[(v & mask)];
} while (v >>= shift);
/* assign parameter and apply width and precision */
param->vars.value = pos;
param->vars.valuelen = digitbuff + sizeof(digitbuff) - pos;
ret = format_number(param);
param->vars.value = NULL;
param->vars.valuelen = 0;
return ret;
}
static int print_decimal(struct print_param *param, long long value)
{
/* max. required buffer for unsigned long long in decimal format */
char digitbuff[20];
/* pointer to the next character position (+1) */
char *pos = digitbuff + sizeof(digitbuff);
/* current value in 32/64 bit */
union u_qword v;
/* next value in 32/64 bit */
union u_qword nv;
/* helper union for division result */
struct udiv_result d;
int ret;
/* assume an unsigned 64 bit value */
v.qword = ((unsigned long long)value) & param->vars.mask;
/*
* assign sign and correct value if value is negative and
* value must be interpreted as signed
*/
if (((param->vars.flags & PRINT_FLAG_UINT32) == 0) && (value < 0)) {
v.qword = (unsigned long long)-value;
param->vars.prefix = "-";
param->vars.prefixlen = 1;
}
/* determine sign if explicit requested in the format string */
if (!param->vars.prefix) {
if (param->vars.flags & PRINT_FLAG_SIGN) {
param->vars.prefix = "+";
param->vars.prefixlen = 1;
} else if (param->vars.flags & PRINT_FLAG_SPACE) {
param->vars.prefix = " ";
param->vars.prefixlen = 1;
}
}
/* process 64 bit value as long as needed */
while (v.dwords.high != 0) {
/* determine digits from right to left */
udiv64(v.qword, 10, &d);
*--pos = d.r.dwords.low + '0';
v.qword = d.q.qword;
}
/* process 32 bit (or reduced 64 bit) value */
do {
/* determine digits from right to left. The compiler should be
* able to handle a division and multiplication by the constant
* 10.
*/
nv.dwords.low = v.dwords.low / 10;
*--pos = (v.dwords.low - (10 * nv.dwords.low)) + '0';
} while ((v.dwords.low = nv.dwords.low) != 0);
/* assign parameter and apply width and precision */
param->vars.value = pos;
param->vars.valuelen = digitbuff + sizeof(digitbuff) - pos;
ret = format_number(param);
param->vars.value = NULL;
param->vars.valuelen = 0;
return ret;
}
static int print_string(struct print_param *param, const char *s)
{
/* the length of the string (-1) if unknown */
int len;
/* the number of additional characters to insert to reach the required
* width
*/
uint32_t w;
/* the last result of the emit function */
int res;
w = 0;
len = -1;
/* we need the length of the string if either width or precision is
* given
*/
if (param->vars.precision || param->vars.width)
len = strnlen_s(s, PRINT_STRING_MAX_LEN);
/* precision gives the max. number of characters to emit. */
if (param->vars.precision && (len > param->vars.precision))
len = param->vars.precision;
/* calculate the number of additional characters to get the required
* width
*/
if (param->vars.width > 0 && param->vars.width > len)
w = param->vars.width - len;
/* emit additional characters for width, return early if an error
* occurred
*/
if ((param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) == 0) {
res = param->emit(PRINT_CMD_FILL, " ", w, param->data);
if (res < 0)
return res;
}
/* emit the string, return early if an error occurred */
res = param->emit(PRINT_CMD_COPY, s, len, param->data);
if (res < 0)
return res;
/* emit additional characters on the right, return early if an error
* occurred
*/
if (param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) {
res = param->emit(PRINT_CMD_FILL, " ", w, param->data);
if (res < 0)
return res;
}
return res;
}
static int do_print(const char *fmt, struct print_param *param,
__builtin_va_list args)
{
/* the result of this function */
int res = 0;
/* temp. storage for the next character */
char ch;
/* temp. pointer to the start of an analysed character sequence */
const char *start;
/* main loop: analyse until there are no more characters */
while (*fmt) {
/* mark the current position and search the next '%' */
start = fmt;
while (*fmt && (*fmt != '%'))
fmt++;
/*
* pass all characters until the next '%' to the emit function.
* Return early if the function fails
*/
res = param->emit(PRINT_CMD_COPY, start, fmt - start,
param->data);
if (res < 0)
return res;
/* continue only if the '%' character was found */
if (*fmt == '%') {
/* mark current position in the format string */
start = fmt++;
/* initialize the variables for the next argument */
memset(&(param->vars), 0, sizeof(param->vars));
param->vars.mask = 0xFFFFFFFFFFFFFFFFULL;
/*
* analyze the format specification:
* - get the flags
* - get the width
* - get the precision
* - get the length modifier
*/
fmt = get_flags(fmt, &(param->vars.flags));
fmt = get_int(fmt, &(param->vars.width));
if (*fmt == '.') {
fmt++;
fmt = get_int(fmt, &(param->vars.precision));
if (param->vars.precision < 0)
param->vars.precision = 0;
}
fmt = get_length_modifier(fmt, &(param->vars.flags),
&(param->vars.mask));
ch = *fmt++;
/* a single '%'? => print out a single '%' */
if (ch == '%') {
res = param->emit(PRINT_CMD_COPY, &ch, 1,
param->data);
}
/* decimal number */
else if ((ch == 'd') || (ch == 'i')) {
res = print_decimal(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
long long)
: (long long)
__builtin_va_arg(args,
int));
}
/* unsigned decimal number */
else if (ch == 'u') {
param->vars.flags |= PRINT_FLAG_UINT32;
res = print_decimal(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
unsigned long long)
: (unsigned long long)
__builtin_va_arg(args,
unsigned int));
}
/* octal number */
else if (ch == 'o') {
res = print_pow2(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
unsigned long long)
: (unsigned long long)
__builtin_va_arg(args,
uint32_t),
3);
}
/* hexadecimal number */
else if ((ch == 'X') || (ch == 'x')) {
if (ch == 'X')
param->vars.flags |= PRINT_FLAG_UPPER;
res = print_pow2(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
unsigned long long)
: (unsigned long long)
__builtin_va_arg(args,
uint32_t),
4);
}
/* string argument */
else if (ch == 's') {
const char *s = __builtin_va_arg(args, char *);
if (s == NULL)
s = "(null)";
res = print_string(param, s);
}
/* pointer argument */
else if (ch == 'p') {
param->vars.flags |= PRINT_FLAG_ALTERNATE_FORM;
/* XXXCRG res=print_pow2(param,
* (uint32_t) __builtin_va_arg(args,
* void *),4);
*/
res = print_pow2(param, (unsigned long long)
__builtin_va_arg(args, void *), 4);
}
/* single character argument */
else if (ch == 'c') {
char c[2];
c[0] = __builtin_va_arg(args, int);
c[1] = 0;
res = print_string(param, c);
}
/* default: print the format specifier as it is */
else {
res = param->emit(PRINT_CMD_COPY, start,
fmt - start, param->data);
}
}
/* return if an error occurred */
if (res < 0)
return res;
}
/* done. Return the result of the last emit function call */
return res;
}
static int charout(int cmd, const char *s, int sz, void *hnd)
{
/* pointer to an integer to store the number of characters */
int *nchars = (int *)hnd;
/* working pointer */
const char *p = s;
/* copy mode ? */
if (cmd == PRINT_CMD_COPY) {
/* copy all characters until NUL is found */
if (sz < 0)
s += console_puts(s);
/* copy 'sz' characters */
else
s += console_write(s, sz);
return (*nchars += (s - p));
}
/* fill mode */
else {
*nchars += sz;
while (sz--)
console_putc(*s);
}
return *nchars;
}
int vprintf(const char *fmt, va_list args)
{
/* struct to store all necessary parameters */
struct print_param param;
/* the result of this function */
int res = 0;
/* argument fo charout() */
int nchars = 0;
/* initialize parameters */
memset(&param, 0, sizeof(param));
param.emit = charout;
param.data = &nchars;
/* execute the printf() */
res = do_print(fmt, &param, args);
/* done */
return res;
}
int printf(const char *fmt, ...)
{
/* variable argument list needed for do_print() */
va_list args;
/* the result of this function */
int res;
va_start(args, fmt);
/* execute the printf() */
res = vprintf(fmt, args);
/* destroy parameter list */
va_end(args);
/* done */
return res;
}
static int charmem(int cmd, const char *s, int sz, void *hnd)
{
/* pointer to the snprint parameter list */
struct snprint_param *param = (struct snprint_param *) hnd;
/* pointer to the destination */
char *p = param->dst + param->wrtn;
/* characters actually written */
int n = 0;
/* copy mode ? */
if (cmd == PRINT_CMD_COPY) {
if (sz < 0) {
while (*s) {
if (n < param->sz - param->wrtn)
*p = *s;
p++;
s++;
n++;
}
} else {
while (*s && n < sz) {
if (n < param->sz - param->wrtn)
*p = *s;
p++;
s++;
n++;
}
}
param->wrtn += n;
return n;
}
/* fill mode */
else {
n = (sz < param->sz - param->wrtn) ? sz : 0;
param->wrtn += sz;
memset(p, *s, n);
}
return n;
}
int vsnprintf(char *dst, int sz, const char *fmt, va_list args)
{
char c[1];
/* the result of this function */
int res = 0;
if (sz <= 0 || !dst) {
dst = c;
sz = 1;
}
/* struct to store all necessary parameters */
struct print_param param;
/* struct to store snprintf specific parameters */
struct snprint_param snparam;
/* initialize parameters */
memset(&snparam, 0, sizeof(snparam));
snparam.dst = dst;
snparam.sz = sz;
memset(&param, 0, sizeof(param));
param.emit = charmem;
param.data = &snparam;
/* execute the printf() */
if (do_print(fmt, &param, args) < 0)
return -1;
/* ensure the written string is NULL terminated */
if (snparam.wrtn < sz)
snparam.dst[snparam.wrtn] = '\0';
else
snparam.dst[sz - 1] = '\0';
/* return the number of chars which would be written */
res = snparam.wrtn;
/* done */
return res;
}
int snprintf(char *dest, int sz, const char *fmt, ...)
{
/* variable argument list needed for do_print() */
va_list args;
/* the result of this function */
int res;
va_start(args, fmt);
/* execute the printf() */
res = vsnprintf(dest, sz, fmt, args);
/* destroy parameter list */
va_end(args);
/* done */
return res;
}

194
debug/sbuf.c Normal file
View File

@@ -0,0 +1,194 @@
/*
* SHARED BUFFER
*
* Copyright (C) 2017 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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.
*
* Li Fei <fei1.li@intel.com>
*
*/
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
DEFINE_CPU_DATA(uint64_t * [ACRN_SBUF_ID_MAX], sbuf);
static inline bool sbuf_is_empty(struct shared_buf *sbuf)
{
return (sbuf->head == sbuf->tail);
}
static inline uint32_t sbuf_next_ptr(uint32_t pos,
uint32_t span, uint32_t scope)
{
pos += span;
pos = (pos >= scope) ? (pos - scope) : pos;
return pos;
}
static inline uint32_t sbuf_calculate_allocate_size(uint32_t ele_num,
uint32_t ele_size)
{
uint64_t sbuf_allocate_size;
sbuf_allocate_size = ele_num * ele_size;
sbuf_allocate_size += SBUF_HEAD_SIZE;
if (sbuf_allocate_size > SBUF_MAX_SIZE) {
pr_err("%s, num=0x%x, size=0x%x exceed 0x%x",
__func__, ele_num, ele_size, SBUF_MAX_SIZE);
return 0;
}
return sbuf_allocate_size;
}
struct shared_buf *sbuf_allocate(uint32_t ele_num, uint32_t ele_size)
{
struct shared_buf *sbuf;
uint32_t sbuf_allocate_size;
if (!ele_num || !ele_size) {
pr_err("%s invalid parameter!", __func__);
return NULL;
}
sbuf_allocate_size = sbuf_calculate_allocate_size(ele_num, ele_size);
if (!sbuf_allocate_size)
return NULL;
sbuf = malloc(sbuf_allocate_size);
if (sbuf == NULL) {
pr_err("%s no memory!", __func__);
return NULL;
}
memset(sbuf, 0, SBUF_HEAD_SIZE);
sbuf->ele_num = ele_num;
sbuf->ele_size = ele_size;
sbuf->size = ele_num * ele_size;
sbuf->magic = SBUF_MAGIC;
pr_info("%s ele_num=0x%x, ele_size=0x%x allocated",
__func__, ele_num, ele_size);
return sbuf;
}
void sbuf_free(struct shared_buf *sbuf)
{
if ((sbuf == NULL) || sbuf->magic != SBUF_MAGIC) {
pr_err("%s invalid parameter!", __func__);
return;
}
sbuf->magic = 0;
free(sbuf);
}
int sbuf_get(struct shared_buf *sbuf, uint8_t *data)
{
const void *from;
if ((sbuf == NULL) || (data == NULL))
return -EINVAL;
if (sbuf_is_empty(sbuf)) {
/* no data available */
return 0;
}
from = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->head;
memcpy_s((void *)data, sbuf->ele_size, from, sbuf->ele_size);
sbuf->head = sbuf_next_ptr(sbuf->head, sbuf->ele_size, sbuf->size);
return sbuf->ele_size;
}
/**
* The high caller should guarantee each time there must have
* sbuf->ele_size data can be write form data and this function
* should guarantee execution atomically.
*
* flag:
* If OVERWRITE_EN set, buf can store (ele_num - 1) elements at most.
* Should use lock to guarantee that only one read or write at
* the same time.
* if OVERWRITE_EN not set, buf can store (ele_num - 1) elements
* at most. Shouldn't modify the sbuf->head.
*
* return:
* ele_size: write succeeded.
* 0: no write, buf is full
* negative: failed.
*/
int sbuf_put(struct shared_buf *sbuf, uint8_t *data)
{
void *to;
uint32_t next_tail;
bool trigger_overwrite = false;
if ((sbuf == NULL) || (data == NULL))
return -EINVAL;
next_tail = sbuf_next_ptr(sbuf->tail, sbuf->ele_size, sbuf->size);
/* if this write would trigger overrun */
if (next_tail == sbuf->head) {
/* accumulate overrun count if necessary */
sbuf->overrun_cnt += sbuf->flags & OVERRUN_CNT_EN;
if (!(sbuf->flags & OVERWRITE_EN)) {
/* if not enable over write, return here. */
return 0;
}
trigger_overwrite = true;
}
to = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->tail;
memcpy_s(to, sbuf->ele_size, data, sbuf->ele_size);
if (trigger_overwrite) {
sbuf->head = sbuf_next_ptr(sbuf->head,
sbuf->ele_size, sbuf->size);
}
sbuf->tail = next_tail;
return sbuf->ele_size;
}
int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, uint64_t *hva)
{
if (pcpu_id >= (uint32_t) phy_cpu_num ||
sbuf_id >= ACRN_SBUF_ID_MAX)
return -EINVAL;
per_cpu(sbuf, pcpu_id)[sbuf_id] = hva;
return 0;
}

365
debug/serial.c Normal file
View File

@@ -0,0 +1,365 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "serial_internal.h"
static struct uart *sio_ports[SERIAL_MAX_DEVS];
static uint8_t sio_initialized[SERIAL_MAX_DEVS];
static struct uart *get_uart_by_id(char *uart_id, uint32_t *index)
{
/* Initialize the index to the start of array. */
*index = 0;
while (sio_ports[*index] != NULL) {
if (strncmp(sio_ports[*index]->tgt_uart->uart_id, uart_id,
strnlen_s(sio_ports[*index]->tgt_uart->uart_id,
SERIAL_ID_MAX_LENGTH)) == 0)
break;
/* No device is found if index reaches end of array. */
if (++(*index) == SERIAL_MAX_DEVS)
return NULL;
}
return sio_ports[*index];
}
int serial_init(void)
{
uint32_t index = 0;
int status = 0;
while (index < SERIAL_MAX_DEVS) {
/* Allocate memory for generic control block of enabled UART */
sio_ports[index] = calloc(1, sizeof(struct uart));
if (!sio_ports[index]) {
status = -ENOMEM;
break;
}
sio_ports[index]->tgt_uart = &(Tgt_Uarts[index]);
/*
* Set the open flag to false to indicate that UART port is
* not opened yet.
*/
sio_ports[index]->open_flag = false;
/* Reset the tx lock */
spinlock_init(&sio_ports[index]->tx_lock);
sio_ports[index]->rx_sio_queue = sbuf_allocate(
sio_ports[index]->tgt_uart->buffer_size,
sizeof(uint8_t));
if (sio_ports[index]->rx_sio_queue != NULL) {
sbuf_set_flags(sio_ports[index]->rx_sio_queue,
OVERWRITE_EN);
/* Call target specific initialization function */
status = sio_ports[index]->tgt_uart->
init(sio_ports[index]->tgt_uart);
if (status == 0)
sio_initialized[index] = true;
} else {
status = -ENOMEM;
break;
}
index++;
}
return status;
}
uint32_t serial_open(char *uart_id)
{
int status = SERIAL_DEV_NOT_FOUND;
struct uart *uart;
uint32_t index;
/* Get UART control block from given character ID */
uart = get_uart_by_id(uart_id, &index);
if (uart != NULL && index < SERIAL_MAX_DEVS &&
sio_initialized[index] &&
(uart->open_flag == false)) {
/* Reset the buffer lock */
spinlock_init(&uart->buffer_lock);
/* Configure the UART port to default settings. */
uart->config.data_bits = DATA_8;
uart->config.stop_bits = STOP_1;
uart->config.parity_bits = PARITY_NONE;
uart->config.baud_rate = BAUD_115200;
uart->config.flow_control = FLOW_NONE;
uart->config.read_mode = SUSPEND;
/* Open the UART hardware with default configuration. */
status = uart->tgt_uart->open(uart->tgt_uart, &(uart->config));
if (status == 0)
uart->open_flag = true;
}
/* Already open serial device */
else if (uart != NULL && uart->open_flag == true) {
/* Reset the buffer lock */
spinlock_init(&uart->buffer_lock);
status = 0;
}
return (status == 0) ?
SERIAL_ENCODE_INDEX(index) :
SERIAL_INVALID_HANDLE;
}
int serial_get_rx_data(uint32_t uart_handle)
{
uint32_t index;
struct uart *uart;
int data_avail, rx_byte_status;
uint32_t lsr_reg, bytes_read;
uint8_t ch;
int total_bytes_read = 0;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return 0;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return 0;
uart = sio_ports[index];
if (uart == NULL)
return 0;
/* Place all the data available in RX FIFO, in circular buffer */
while ((data_avail = uart->tgt_uart->rx_data_is_avail(
uart->tgt_uart, &lsr_reg))) {
/* Read the byte */
uart->tgt_uart->read(uart->tgt_uart, (void *)&ch, &bytes_read);
/* Get RX status for this byte */
rx_byte_status = uart->tgt_uart->get_rx_err(lsr_reg);
/*
* Check if discard errors in RX character
* (parity / framing errors)
*/
if (rx_byte_status >= SD_RX_PARITY_ERROR) {
/* Increase error status if bad data */
uart->rx_error.parity_errors +=
(rx_byte_status == SD_RX_PARITY_ERROR);
uart->rx_error.frame_errors +=
(rx_byte_status == SD_RX_FRAME_ERROR);
} else {
/* Update the overrun errors */
uart->rx_error.overrun_errors +=
(rx_byte_status == SD_RX_OVERRUN_ERROR);
/* Enter Critical Section */
spinlock_obtain(&uart->buffer_lock);
/* Put the item on circular buffer */
sbuf_put(uart->rx_sio_queue, &ch);
/* Exit Critical Section */
spinlock_release(&uart->buffer_lock);
}
/* Update the total bytes read */
total_bytes_read += bytes_read;
}
return total_bytes_read;
}
int serial_getc(uint32_t uart_handle)
{
uint8_t ch;
struct uart *port;
uint32_t index;
int status = SERIAL_DEV_NOT_FOUND;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
goto exit;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
goto exit;
port = sio_ports[index];
if (port == NULL)
goto exit;
/* First read a character from the circular buffer regardless of the
* read mode of UART port. If status is not CBUFFER_EMPTY, character
* read from UART port is returned to the caller. Otherwise, if read
* mode is not NO_SUSPEND, thread is blocked until a character is read
* from the port. Serial target specific HISR unblocks the thread when
* a character is received and character is then read from the circular
* buffer.
*/
/* Disable interrupts for critical section */
spinlock_obtain(&port->buffer_lock);
status = sbuf_get(port->rx_sio_queue, &ch);
/* Restore interrupts to original level. */
spinlock_release(&port->buffer_lock);
exit:
/* Return the character read, otherwise return the error status */
return ((status > 0) ? (int)(ch) : SERIAL_EOF);
}
int serial_gets(uint32_t uart_handle, char *buffer, uint32_t length)
{
char *data_read = buffer;
int c;
struct uart *port;
uint32_t index;
int status = 0;
if ((buffer == NULL) || (length == 0))
return 0;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return 0;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return 0;
port = sio_ports[index];
if ((port != NULL) && (port->open_flag == true)) {
for (; length > 0; data_read++, length--) {
/* Disable interrupts for critical section */
spinlock_obtain(&port->buffer_lock);
status = sbuf_get(port->rx_sio_queue, (uint8_t *)&c);
/* Restore interrupts to original level. */
spinlock_release(&port->buffer_lock);
if (status <= 0)
break;
/* Save character in buffer */
*data_read = (char) c;
}
}
/* Return actual number of bytes read */
return (int)(data_read - buffer);
}
static int serial_putc(uint32_t uart_handle, int c)
{
uint32_t index, bytes_written = 0;
struct uart *uart;
int busy;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return SERIAL_EOF;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return SERIAL_EOF;
uart = sio_ports[index];
if (uart == NULL)
return SERIAL_EOF;
/* Wait for TX hardware to be ready */
do {
busy = uart->tgt_uart->tx_is_busy(uart->tgt_uart);
} while (busy);
/* Transmit character */
uart->tgt_uart->write(uart->tgt_uart, &(c), &bytes_written);
/* Return character written or EOF for error */
return ((bytes_written > 0) ? c : (SERIAL_EOF));
}
int serial_puts(uint32_t uart_handle, const char *s, uint32_t length)
{
const char *old_data = s;
uint32_t index;
struct uart *port;
int retval = 0;
if ((s == NULL) || (length == 0))
return 0;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return 0;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return 0;
port = sio_ports[index];
if (port == NULL)
return 0;
/*
* Grab the semaphore so that strings between threads do not
* get mixed.
*/
spinlock_obtain(&port->tx_lock);
/*
* Loop through the string until desired length of bytes have
* been written or SERIAL_EOF is returned.
*/
for (; length > 0 && retval != SERIAL_EOF; s++, length--)
retval = serial_putc(uart_handle, (int) *s);
/* Allow other threads to use this service. */
spinlock_release(&port->tx_lock);
/* Return actual number of bytes written */
return (int)(s - old_data);
}

208
debug/serial_internal.h Normal file
View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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.
*/
#ifndef SERIAL_INTER_H
#define SERIAL_INTER_H
struct shared_buf;
/* Maximum serial devices supported by the platform. */
#define SERIAL_MAX_DEVS 1
/* Maximum length of unique id of each UART port enabled in platform. */
#define SERIAL_ID_MAX_LENGTH 8
/* SERIAL error values */
#define SERIAL_SUCCESS 0
#define SERIAL_EOF -1
#define SERIAL_ERROR -2
#define SERIAL_DEV_NOT_FOUND -3
#define INVALID_COM_PORT -4
#define SERIAL_NO_char_AVAIL -5
#define SERIAL_INVALID_HANDLE 0xFFFFFFFF
/* Pending interrupt defines */
#define SD_NO_INTERRUPT 0
#define SD_RX_INTERRUPT 1
/* RX error defines */
#define SD_RX_NO_ERROR 0
#define SD_RX_OVERRUN_ERROR 1
#define SD_RX_PARITY_ERROR 2
#define SD_RX_FRAME_ERROR 3
/* Defines for encoding/decoding the unique UART handle of each port. */
#define SERIAL_MAGIC_NUM 0x005500AA
#define SERIAL_VALIDATE_HANDLE(handle) \
((handle & 0xFFFF00FF) == (SERIAL_MAGIC_NUM))
#define SERIAL_ENCODE_INDEX(index) ((SERIAL_MAGIC_NUM) | (index << 8))
#define SERIAL_DECODE_INDEX(handle) ((handle & 0x0000FF00) >> 8)
#define NO_SUSPEND 0
#define SUSPEND 0xFFFFFFFFUL
/* Enumeration values to set UART Configuration */
typedef enum _baudenum_ {
/* Baud Rate Options */
BAUD_110 = 110, /* not supported on OMAP5 */
BAUD_300 = 300,
BAUD_600 = 600,
BAUD_1200 = 1200,
BAUD_2400 = 2400,
BAUD_4800 = 4800,
BAUD_9600 = 9600,
BAUD_14400 = 14400,
BAUD_19200 = 19200,
BAUD_28800 = 28800,
BAUD_38400 = 38400,
BAUD_57600 = 57600,
BAUD_115200 = 115200,
BAUD_230400 = 230400,
BAUD_460800 = 460800,
BAUD_921600 = 921600,
BAUD_1843000 = 1843000,
BAUD_36884000 = 36884000
} BAUD_ENUM;
typedef enum _flowenum_ {
/* Flow Control Bits */
FLOW_NONE = 0,
FLOW_HARD = 1,
FLOW_X = 2
} FLOW_ENUM;
typedef enum _parityenum_ {
/* Parity Bits */
PARITY_NONE = 0,
PARITY_ODD = 1,
PARITY_EVEN = 2,
PARITY_MARK = 3,
PARITY_SPACE = 4
} PARITY_ENUM;
typedef enum _stopenum_ {
/* Stop Bits */
STOP_1 = 1,
STOP_2 = 2
} STOP_ENUM;
typedef enum _dataenum_ {
/* Data bits */
DATA_7 = 7,
DATA_8 = 8
} DATA_ENUM;
/* Control Block definition about error in Rx data */
struct rx_error {
uint32_t parity_errors;
uint32_t frame_errors;
uint32_t overrun_errors;
uint32_t general_errors;
};
/* Control Block definition for configuration specific
* parameters of UART
*/
struct uart_config {
uint32_t data_bits;
uint32_t stop_bits;
uint32_t parity_bits;
uint32_t baud_rate;
uint32_t flow_control;
/* Read mode of UART port in interrupt mode. It can be NO_SUSPEND or
* SUSPEND or (1-4,294,967,293). SUSPEND means unlimited blocking,
* NO_SUSPEND means non-blocking and some integer value means timeout
* blocking support. By default, it is set to SUSPEND.
*/
uint32_t read_mode;
};
/* Control Block definition for target specific driver
* of UART
*/
struct tgt_uart {
char uart_id[SERIAL_ID_MAX_LENGTH];
mmio_addr_t base_address;
uint32_t clock_frequency;
uint32_t buffer_size;
unsigned int open_count;
/* Target specific function pointers. */
int (*init)(struct tgt_uart *tgt_uart);
int (*open)(struct tgt_uart *tgt_uart, struct uart_config *config);
void (*close)(struct tgt_uart *tgt_uart);
void (*read)(struct tgt_uart *tgt_uart,
void *buffer, uint32_t *bytes_read);
void (*write)(struct tgt_uart *tgt_uart,
const void *buffer, uint32_t *bytes_written);
bool (*tx_is_busy)(struct tgt_uart *tgt_uart);
bool (*rx_data_is_avail)(struct tgt_uart *tgt_uart, uint32_t *lsr_reg);
int (*get_rx_err)(uint32_t rx_data);
};
/* Control Block definition of light-weight serial driver */
struct uart {
/* Pointer to target specific Control Block of UART */
struct tgt_uart *tgt_uart;
/* Configuration of UART */
struct uart_config config;
/* Errors in data received from UART port */
struct rx_error rx_error;
/* Pointer to receive circular buffer */
struct shared_buf *rx_sio_queue;
/* Lock to provide mutual exclusion for transmitting data to UART port*/
spinlock_t tx_lock;
/* Lock to provide mutual exclusion for accessing shared buffer */
spinlock_t buffer_lock;
/* Flag to indicate whether UART port is opened or not */
uint8_t open_flag;
};
/* Null terminated array of target specific UART control blocks */
extern struct tgt_uart Tgt_Uarts[SERIAL_MAX_DEVS];
uint32_t serial_open(char *uart_id);
int serial_getc(uint32_t uart_handle);
int serial_gets(uint32_t uart_handle, char *buffer, uint32_t length);
int serial_puts(uint32_t uart_handle, const char *s, uint32_t length);
int serial_get_rx_data(uint32_t uart_handle);
#endif /* !SERIAL_INTER_H */

1099
debug/shell_internal.c Normal file

File diff suppressed because it is too large Load Diff

187
debug/shell_internal.h Normal file
View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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.
*/
#ifndef SHELL_INTER_H
#define SHELL_INTER_H
#include <spinlock.h>
struct shell;
/* Structure to hold the details about shell input and output */
struct shell_io {
void *io_session_info;
int (*io_init)(struct shell *);
int (*io_deinit)(struct shell *);
void (*io_puts)(struct shell *, char *);
uint8_t (*io_getc)(struct shell *);
void (*io_special)(struct shell *, uint8_t);
bool io_echo_on;
};
#define SHELL_CMD_MAX_LEN 100
#define SHELL_NAME_MAX_LEN 50
#define SHELL_PARA_MAX_LEN 64
#define SHELL_HELP_MAX_LEN 256
#define SHELL_STRING_MAX_LEN (CPU_PAGE_SIZE << 2)
/* Shell Control Block */
struct shell {
struct shell_io session_io; /* Session I/O information */
char input_line[2][SHELL_CMD_MAX_LEN + 1]; /* current & last */
char name[SHELL_NAME_MAX_LEN]; /* Session name */
uint32_t input_line_len; /* Length of current input line */
uint32_t input_line_active; /* Active input line index */
struct list_head cmd_list; /* List of registered commands */
uint32_t cmd_count; /* Count of added commands */
};
/* Shell Command Function */
typedef int (*shell_cmd_fn_t)(struct shell *, int, char **);
/* Shell Command */
struct shell_cmd {
struct list_head node; /* Linked list node */
char *str; /* Command string */
char *cmd_param; /* Command parameter string */
char *help_str; /* Help text associated with the command */
shell_cmd_fn_t fcn; /* Command call-back function */
};
/* Shell Command list with parameters and help description */
#define SHELL_CMD_HELP "help"
#define SHELL_CMD_HELP_PARAM NULL
#define SHELL_CMD_HELP_HELP "Display info about the supported shell commands."
#define SHELL_CMD_VM_LIST "vm_list"
#define SHELL_CMD_VM_LIST_PARAM NULL
#define SHELL_CMD_VM_LIST_HELP "Lists all VMs (VM Name, VM ID, VM State)"
#define SHELL_CMD_VCPU_LIST "vcpu_list"
#define SHELL_CMD_VCPU_LIST_PARAM NULL
#define SHELL_CMD_VCPU_LIST_HELP "Lists all VCPU in all VMs"
#define SHELL_CMD_VCPU_PAUSE "vcpu_pause"
#define SHELL_CMD_VCPU_PAUSE_PARAM "<vm id, vcpu id>"
#define SHELL_CMD_VCPU_PAUSE_HELP "Pause a specific vcpu"
#define SHELL_CMD_VCPU_RESUME "vcpu_resume"
#define SHELL_CMD_VCPU_RESUME_PARAM "<vm id, vcpu id>"
#define SHELL_CMD_VCPU_RESUME_HELP "Resume a specific vcpu"
#define SHELL_CMD_VCPU_DUMPREG "vcpu_dumpreg"
#define SHELL_CMD_VCPU_DUMPREG_PARAM "<vm id, vcpu id>"
#define SHELL_CMD_VCPU_DUMPREG_HELP "Dump registers for a specific vcpu"
#define SHELL_CMD_VCPU_DUMPMEM "vcpu_dumpmem"
#define SHELL_CMD_VCPU_DUMPMEM_PARAM "<vcpu id, gva, length>"
#define SHELL_CMD_VCPU_DUMPMEM_HELP "Dump memory for a specific vcpu"
#define SHELL_CMD_VM_CONSOLE "vm_console"
#define SHELL_CMD_VM_CONSOLE_PARAM NULL
#define SHELL_CMD_VM_CONSOLE_HELP "Switch to SOS's console"
#define SHELL_CMD_INTERRUPT "int"
#define SHELL_CMD_INTERRUPT_PARAM NULL
#define SHELL_CMD_INTERRUPT_HELP "show interrupt info per CPU"
#define SHELL_CMD_PTDEV "pt"
#define SHELL_CMD_PTDEV_PARAM NULL
#define SHELL_CMD_PTDEV_HELP "show pass-through device info"
#define SHELL_CMD_REQ "lsreq"
#define SHELL_CMD_REQ_PARAM NULL
#define SHELL_CMD_REQ_HELP "show ioreq info"
#define SHELL_CMD_IOAPIC "dump_ioapic"
#define SHELL_CMD_IOAPIC_PARAM NULL
#define SHELL_CMD_IOAPIC_HELP "show native ioapic info"
#define SHELL_CMD_VIOAPIC "vioapic"
#define SHELL_CMD_VIOAPIC_PARAM "<vm id>"
#define SHELL_CMD_VIOAPIC_HELP "show vioapic info"
#define SHELL_CMD_VMEXIT "vmexit"
#define SHELL_CMD_VMEXIT_PARAM NULL
#define SHELL_CMD_VMEXIT_HELP "show vmexit profiling"
#define SHELL_CMD_LOGDUMP "logdump"
#define SHELL_CMD_LOGDUMP_PARAM "<pcpu id>"
#define SHELL_CMD_LOGDUMP_HELP "log buffer dump"
#define SHELL_CMD_trace "trace"
#define SHELL_CMD_trace_PARAM "<cpumask> <ms>"
#define SHELL_CMD_trace_HELP "Dump cpus recent events within <ms> millisecond"
#define SHELL_CMD_GET_LOG_LVL "get_loglevel"
#define SHELL_CMD_GET_LOG_LVL_PARAM NULL
#define SHELL_CMD_GET_LOG_LVL_HELP "Get the loglevel"
#define SHELL_CMD_SET_LOG_LVL "set_loglevel"
#define SHELL_CMD_SET_LOG_LVL_PARAM "<console_loglevel> [mem_loglevel]"
#define SHELL_CMD_SET_LOG_LVL_HELP "Set loglevel [0-6]"
/* Global function prototypes */
int shell_show_req_info(struct shell *p_shell, int argc, char **argv);
int shell_construct(struct shell **p_shell);
int shell_cmd_help(struct shell *p_shell, int argc, char **argv);
int shell_reset_cmd(struct shell *p_shell, int argc, char **argv);
int shell_list_vm(struct shell *p_shell, int argc, char **argv);
int shell_list_vcpu(struct shell *p_shell, int argc, char **argv);
int shell_pause_vcpu(struct shell *p_shell, int argc, char **argv);
int shell_resume_vcpu(struct shell *p_shell, int argc, char **argv);
int shell_vcpu_dumpreg(struct shell *p_shell, int argc, char **argv);
int shell_vcpu_dumpmem(struct shell *p_shell, int argc, char **argv);
int shell_boot_vm(struct shell *p_shell, int argc, char **argv);
int shell_trace_cmd(struct shell *p_shell, int argc, char **argv);
int shell_to_sos_console(struct shell *p_shell, int argc, char **argv);
int shell_show_cpu_int(struct shell *p_shell, int argc, char **argv);
int shell_show_ptdev_info(struct shell *p_shell, int argc, char **argv);
int shell_show_vioapic_info(struct shell *p_shell, int argc, char **argv);
int shell_show_ioapic_info(struct shell *p_shell, int argc, char **argv);
int shell_show_vmexit_profile(struct shell *p_shell, int argc, char **argv);
int shell_dump_logbuf(struct shell *p_shell, int argc, char **argv);
int shell_get_loglevel(struct shell *p_shell, int argc, char **argv);
int shell_set_loglevel(struct shell *p_shell, int argc, char **argv);
struct shell_cmd *shell_find_cmd(struct shell *p_shell, const char *cmd);
int shell_process_cmd(struct shell *p_shell, char *p_input_line);
int shell_terminate_serial(struct shell *p_shell);
int shell_init_serial(struct shell *p_shell);
void shell_puts_serial(struct shell *p_shell, char *string_ptr);
uint8_t shell_getc_serial(struct shell *p_shell);
void shell_special_serial(struct shell *p_shell, uint8_t ch);
void kick_shell(struct shell *p_shell);
int shell_puts(struct shell *p_shell, char *str_ptr);
int shell_set_name(struct shell *p_shell, char *name);
#endif /* SHELL_INTER_H */

421
debug/shell_public.c Normal file
View File

@@ -0,0 +1,421 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "shell_internal.h"
/* Shell that uses serial I/O */
static struct shell *serial_session;
static int shell_register_cmd(struct shell *p_shell,
const char *cmd,
const char *cmd_param,
const char *cmd_help_str,
int (*cmd_fcn)(struct shell *, int, char **))
{
int status = 0;
struct shell_cmd *p_cmd;
uint32_t cmd_mem_size;
if ((p_shell == NULL) || (cmd == NULL) ||
(cmd_help_str == NULL) || (cmd_fcn == NULL)) {
return -EINVAL;
}
/* Check if a duplicate command exists */
p_cmd = shell_find_cmd(p_shell, cmd);
if (p_cmd != NULL) {
/* Requested command is already registered */
pr_err("Error: Command %s is already registered.", cmd);
status = -EINVAL;
goto exit;
}
/* Requested command is not already registered. So allocate enough
* memory for the command structure and the command, parameter and the
* help text strings along with the corresponding null terminating
* character/s.
*/
cmd_mem_size = sizeof(struct shell_cmd)
+ (strnlen_s(cmd, SHELL_CMD_MAX_LEN) + 1);
/* If command takes any parameters, need to allocate memory for storing
* parameter string.
*/
if (cmd_param)
cmd_mem_size += strnlen_s(cmd_param, SHELL_PARA_MAX_LEN) + 1;
/* If help text is provided for command, need to allocate memory for
* storing help string.
*/
if (cmd_help_str)
cmd_mem_size += strnlen_s(cmd_help_str, SHELL_HELP_MAX_LEN) + 1;
p_cmd = (struct shell_cmd *) calloc(1, cmd_mem_size);
if (p_cmd == NULL) {
status = -ENOMEM;
goto exit;
}
/* The command structure, command string, it's parameter string and
* the associated help string are all stored in contiguous memory
* locations. So the cmd string immediately follows the command
* structure..
*/
p_cmd->str = (char *)p_cmd + sizeof(struct shell_cmd);
strncpy_s(p_cmd->str, SHELL_CMD_MAX_LEN, cmd, SHELL_CMD_MAX_LEN);
/* Check if this command does take any parameters... */
if (cmd_param) {
/* The command parameter string immediately follows the command
* string in memory.
*/
p_cmd->cmd_param = p_cmd->str
+ (strnlen_s(cmd, SHELL_CMD_MAX_LEN) + 1);
strcpy_s(p_cmd->cmd_param, SHELL_PARA_MAX_LEN, cmd_param);
}
/* Check if help string is provided for the command.. */
if (cmd_help_str) {
if (cmd_param) {
/* The command help string immediately follows the
* parameter string in memory | cmd_structure |
* cmd_str | param_str | help_str |
*/
p_cmd->help_str = p_cmd->cmd_param +
(strnlen_s(cmd_param, SHELL_PARA_MAX_LEN) + 1);
strcpy_s(p_cmd->help_str,
SHELL_HELP_MAX_LEN, cmd_help_str);
} else {
/* No command parameter/s. Help string immediately
* follows the cmd string | cmd_structure | cmd_str |
* help_str |
*/
p_cmd->help_str = p_cmd->str +
(strnlen_s(cmd, SHELL_CMD_MAX_LEN) + 1);
strcpy_s(p_cmd->help_str,
SHELL_HELP_MAX_LEN, cmd_help_str);
}
}
/* Set the command function. */
p_cmd->fcn = cmd_fcn;
INIT_LIST_HEAD(&p_cmd->node);
list_add(&p_cmd->node, &p_shell->cmd_list);
/* Update command count. */
p_shell->cmd_count++;
status = 0;
exit:
return status;
}
int shell_init(void)
{
int status;
status = shell_construct(&serial_session);
if (status != 0)
return status;
/* Set the function pointers for the shell i/p and o/p functions */
serial_session->session_io.io_init = shell_init_serial;
serial_session->session_io.io_deinit = shell_terminate_serial;
serial_session->session_io.io_puts = shell_puts_serial;
serial_session->session_io.io_getc = shell_getc_serial;
serial_session->session_io.io_special = shell_special_serial;
serial_session->session_io.io_echo_on = (bool)true;
/* Initialize the handler for the serial port that will be used
* for shell i/p and o/p
*/
status = serial_session->session_io.io_init(serial_session);
/* Register command handlers for the shell commands that are available
* by default
*/
if (status == 0) {
status = shell_register_cmd(serial_session,
SHELL_CMD_HELP,
SHELL_CMD_HELP_PARAM,
SHELL_CMD_HELP_HELP,
shell_cmd_help);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_HELP);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VM_LIST,
SHELL_CMD_VM_LIST_PARAM,
SHELL_CMD_VM_LIST_HELP,
shell_list_vm);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VM_LIST);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_LIST,
SHELL_CMD_VCPU_LIST_PARAM,
SHELL_CMD_VCPU_LIST_HELP,
shell_list_vcpu);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_LIST);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_PAUSE,
SHELL_CMD_VCPU_PAUSE_PARAM,
SHELL_CMD_VCPU_PAUSE_HELP,
shell_pause_vcpu);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_PAUSE);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_RESUME,
SHELL_CMD_VCPU_RESUME_PARAM,
SHELL_CMD_VCPU_RESUME_HELP,
shell_resume_vcpu);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_RESUME);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_DUMPREG,
SHELL_CMD_VCPU_DUMPREG_PARAM,
SHELL_CMD_VCPU_DUMPREG_HELP,
shell_vcpu_dumpreg);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_DUMPREG);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_DUMPMEM,
SHELL_CMD_VCPU_DUMPMEM_PARAM,
SHELL_CMD_VCPU_DUMPMEM_HELP,
shell_vcpu_dumpmem);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_DUMPMEM);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VM_CONSOLE,
SHELL_CMD_VM_CONSOLE_PARAM,
SHELL_CMD_VM_CONSOLE_HELP,
shell_to_sos_console);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VM_CONSOLE);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_INTERRUPT,
SHELL_CMD_INTERRUPT_PARAM,
SHELL_CMD_INTERRUPT_HELP,
shell_show_cpu_int);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_INTERRUPT);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_PTDEV,
SHELL_CMD_PTDEV_PARAM,
SHELL_CMD_PTDEV_HELP,
shell_show_ptdev_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_PTDEV);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_REQ,
SHELL_CMD_REQ_PARAM,
SHELL_CMD_REQ_HELP,
shell_show_req_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_REQ);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VIOAPIC,
SHELL_CMD_VIOAPIC_PARAM,
SHELL_CMD_VIOAPIC_HELP,
shell_show_vioapic_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VIOAPIC);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_IOAPIC,
SHELL_CMD_IOAPIC_PARAM,
SHELL_CMD_IOAPIC_HELP,
shell_show_ioapic_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_IOAPIC);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VMEXIT,
SHELL_CMD_VMEXIT_PARAM,
SHELL_CMD_VMEXIT_HELP,
shell_show_vmexit_profile);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VMEXIT);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_LOGDUMP,
SHELL_CMD_LOGDUMP_PARAM,
SHELL_CMD_LOGDUMP_HELP,
shell_dump_logbuf);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_LOGDUMP);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_GET_LOG_LVL,
SHELL_CMD_GET_LOG_LVL_PARAM,
SHELL_CMD_GET_LOG_LVL_HELP,
shell_get_loglevel);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_GET_LOG_LVL);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_SET_LOG_LVL,
SHELL_CMD_SET_LOG_LVL_PARAM,
SHELL_CMD_SET_LOG_LVL_HELP,
shell_set_loglevel);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_SET_LOG_LVL);
}
}
return status;
}
int shell_puts(struct shell *p_shell, char *str_ptr)
{
int status;
if ((p_shell != NULL) && (p_shell->session_io.io_puts != NULL) &&
(str_ptr != NULL)) {
/* Transmit data using this shell session's 'puts' function */
p_shell->session_io.io_puts(p_shell, str_ptr);
status = 0;
} else {
/* Error: Invalid request */
status = -EINVAL;
}
return status;
}
int shell_set_name(struct shell *p_shell, char *name)
{
int status;
if ((p_shell != NULL) && (name != NULL)) {
strncpy_s((void *) p_shell->name, SHELL_NAME_MAX_LEN,
(void *) name, SHELL_NAME_MAX_LEN - 1);
/* Ensure null terminated string */
p_shell->name[SHELL_NAME_MAX_LEN - 1] = 0;
status = 0;
} else {
status = -EINVAL;
}
return status;
}
void shell_kick_session(void)
{
/* Kick the shell */
kick_shell(serial_session);
}
int shell_switch_console(void)
{
struct vuart *vuart;
vuart = vuart_console_active();
if (vuart == NULL)
return -EINVAL;
vuart->active = false;
/* Output that switching to ACRN shell */
shell_puts(serial_session,
"\r\n\r\n----- Entering ACRN Shell -----\r\n");
return 0;
}

347
debug/uart16550.c Normal file
View File

@@ -0,0 +1,347 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include "uart16550.h"
#include "serial_internal.h"
/* Mapping of 16c550 write-only registers to appropriate structure members */
#define THR_IDX RBR_IDX
#define IIR_IDX FCR_IDX
#define DLL_IDX RBR_IDX
#define DLM_IDX IER_IDX
#if defined(CONFIG_SERIAL_PIO_BASE)
static int serial_port_mapped = 1;
static int uart_enabled = 1;
#define UART_BASE_ADDRESS CONFIG_SERIAL_PIO_BASE
#elif defined(CONFIG_SERIAL_MMIO_BASE)
static int serial_port_mapped;
static int uart_enabled = 1;
#define UART_BASE_ADDRESS CONFIG_SERIAL_MMIO_BASE
#else
#warning "no uart base configure, please check!"
static int serial_port_mapped;
static int uart_enabled;
#define UART_BASE_ADDRESS 0
#endif
typedef uint32_t uart_reg_t;
enum UART_REG_IDX{
RBR_IDX, /* 0 */
IER_IDX, /* 1 */
FCR_IDX, /* 2 */
LCR_IDX, /* 3 */
MCR_IDX, /* 4 */
ISR_IDX, /* 5 */
MSR_IDX, /* 6 */
SPR_IDX, /* 7 */
MDR1_IDX, /* 8 */
REG9_IDX, /* 9 */
REGA_IDX, /* A */
REGB_IDX, /* B */
REGC_IDX, /* C */
REGD_IDX, /* D */
REGE_IDX, /* E */
UASR_IDX, /* F */
SCR_IDX, /* 10*/
SSR_IDX, /* 11*/
REG12_IDX, /* 12*/
OSC_12M_SEL_IDX, /* 13*/
};
/* CPU oscillator clock */
#define CPU_OSC_CLOCK 1843200 /* 1.8432 MHz */
/* UART hardware definitions */
#define UART_CLOCK_RATE CPU_OSC_CLOCK
#define UART_BUFFER_SIZE 2048
static inline uint32_t uart16550_read_reg(uint32_t base, uint32_t reg_idx)
{
if (serial_port_mapped) {
return io_read_byte((ioport_t)
((uint8_t *)(uint64_t)base + reg_idx));
} else {
return mmio_read_long((mmio_addr_t)
((uint32_t *)(uint64_t)base + reg_idx));
}
}
static inline void uart16550_write_reg(uint32_t base,
uint32_t val, uint32_t reg_idx)
{
if (serial_port_mapped) {
io_write_byte(val, (ioport_t)
((uint8_t *)(uint64_t)base + reg_idx));
} else {
mmio_write_long(val, (mmio_addr_t)
((uint32_t *)(uint64_t)base + reg_idx));
}
}
static void uart16550_enable(__unused struct tgt_uart *tgt_uart)
{
}
static int uart16550_calc_baud_div(__unused struct tgt_uart *tgt_uart,
uint32_t ref_freq, uint32_t *baud_div_ptr, uint32_t baud_rate)
{
uint32_t baud_multiplier = baud_rate < BAUD_460800 ? 16 : 13;
*baud_div_ptr = ref_freq / (baud_multiplier * baud_rate);
return 0;
}
static int uart16550_set_baud_rate(struct tgt_uart *tgt_uart,
uint32_t baud_rate)
{
int status;
uint32_t baud_div, duart_clock = CPU_OSC_CLOCK;
uart_reg_t temp_reg;
/* Calculate baud divisor */
status = uart16550_calc_baud_div(
tgt_uart, duart_clock, &baud_div, baud_rate);
if (status == 0) {
/* Enable DLL and DLM registers for setting the Divisor */
temp_reg = uart16550_read_reg(tgt_uart->base_address, LCR_IDX);
temp_reg |= LCR_DLAB;
uart16550_write_reg(tgt_uart->base_address, temp_reg, LCR_IDX);
/* Write the appropriate divisor value */
uart16550_write_reg(tgt_uart->base_address,
((baud_div >> 8) & 0xFF), DLM_IDX);
uart16550_write_reg(tgt_uart->base_address,
(baud_div & 0xFF), DLL_IDX);
/* Disable DLL and DLM registers */
temp_reg &= ~LCR_DLAB;
uart16550_write_reg(tgt_uart->base_address, temp_reg, LCR_IDX);
}
return status;
}
static int uart16550_init(struct tgt_uart *tgt_uart)
{
int status = 0;
if (!uart_enabled) {
/*uart will not be used */
status = -ENODEV;
} else {
if (strcmp(tgt_uart->uart_id, "STDIO") == 0) {
atomic_set_int(&tgt_uart->open_count, 0);
} else {
/* set open count to 1 to prevent open */
atomic_set_int(&tgt_uart->open_count, 1);
status = -EINVAL;
}
}
return status;
}
static int uart16550_open(struct tgt_uart *tgt_uart,
struct uart_config *config)
{
uint32_t temp32;
int status = 0;
if (strcmp(tgt_uart->uart_id, "STDIO") == 0) {
if (atomic_cmpxchg_int(&tgt_uart->open_count, 0, 1) != 0)
return -EBUSY;
/* Call UART setup function */
/* Enable TX and RX FIFOs */
uart16550_write_reg(tgt_uart->base_address,
FCR_FIFOE | FCR_RFR | FCR_TFR, FCR_IDX);
/* Set parity value */
if (config->parity_bits == PARITY_ODD) {
/* Odd parity */
temp32 = LCR_PARITY_ODD;
} else if (config->parity_bits == PARITY_EVEN) {
/* Even parity */
temp32 = LCR_PARITY_EVEN;
} else {
/* No parity */
temp32 = LCR_PARITY_NONE;
}
/* Set Data length */
if (config->data_bits == DATA_7) {
/* Set bits for 7 data bits */
temp32 |= LCR_WL7;
} else {
/* Set bits for 8 data bits */
temp32 |= LCR_WL8;
}
/* Check for 1 stop bit */
if (config->stop_bits == STOP_1) {
/* Set bits for 1 stop bit */
temp32 |= LCR_NB_STOP_BITS_1;
} else {
/* Set bits for 2 stop bits */
temp32 |= LCR_NB_STOP_BITS_2;
}
/* Set-up data bits / parity / stop bits. */
uart16550_write_reg(tgt_uart->base_address,
temp32, LCR_IDX);
/* Disable interrupts (we use polling) */
uart16550_write_reg(tgt_uart->base_address,
UART_IER_DISABLE_ALL, IER_IDX);
/* Set baud rate */
uart16550_set_baud_rate(tgt_uart, config->baud_rate);
/* Data terminal ready + Request to send */
uart16550_write_reg(tgt_uart->base_address,
MCR_RTS | MCR_DTR, MCR_IDX);
/* Enable the UART hardware */
uart16550_enable(tgt_uart);
} else {
status = -ENODEV;
}
return status;
}
static int uart16550_get_rx_err(uint32_t rx_data)
{
int rx_status = SD_RX_NO_ERROR;
/* Check for RX overrun error */
if ((rx_data & LSR_OE))
rx_status |= SD_RX_OVERRUN_ERROR;
/* Check for RX parity error */
if ((rx_data & LSR_PE))
rx_status |= SD_RX_PARITY_ERROR;
/* Check for RX frame error */
if ((rx_data & LSR_FE))
rx_status |= SD_RX_FRAME_ERROR;
/* Return the rx status */
return rx_status;
}
static void uart16550_close(struct tgt_uart *tgt_uart)
{
if (tgt_uart != NULL) {
if (atomic_cmpxchg_int(&tgt_uart->open_count, 1, 0) == 1) {
/* TODO: Add logic to disable the UART */
}
}
}
static void uart16550_read(struct tgt_uart *tgt_uart, void *buffer,
uint32_t *bytes_read)
{
/* If a character has been received, read it */
if ((uart16550_read_reg(tgt_uart->base_address, ISR_IDX) & LSR_DR)
== LSR_DR) {
/* Read a character */
*(uint8_t *)buffer =
uart16550_read_reg(tgt_uart->base_address, RBR_IDX);
/* Read 1 byte */
*bytes_read = 1;
} else {
*bytes_read = 0;
}
}
static void uart16550_write(struct tgt_uart *tgt_uart,
const void *buffer, uint32_t *bytes_written)
{
/* Ensure there are no further Transmit buffer write requests */
do {
} while (!(uart16550_read_reg(tgt_uart->base_address,
ISR_IDX) & LSR_THRE));
/* Transmit the character. */
uart16550_write_reg(tgt_uart->base_address,
*(uint8_t *)buffer, THR_IDX);
if (bytes_written != NULL)
*bytes_written = 1;
}
static bool uart16550_tx_is_busy(struct tgt_uart *tgt_uart)
{
return ((uart16550_read_reg(tgt_uart->base_address, ISR_IDX) &
(LSR_TEMT)) == 0) ? true : false;
}
static bool uart16550_rx_data_is_avail(struct tgt_uart *tgt_uart,
uint32_t *lsr_reg)
{
*(uart_reg_t *)lsr_reg =
uart16550_read_reg(tgt_uart->base_address, ISR_IDX);
return ((*(uart_reg_t *)lsr_reg & LSR_DR) == LSR_DR) ? true : false;
}
struct tgt_uart Tgt_Uarts[SERIAL_MAX_DEVS] = {
{
.uart_id = "STDIO",
.base_address = UART_BASE_ADDRESS,
.clock_frequency = UART_CLOCK_RATE,
.buffer_size = UART_BUFFER_SIZE,
.init = uart16550_init,
.open = uart16550_open,
.close = uart16550_close,
.read = uart16550_read,
.write = uart16550_write,
.tx_is_busy = uart16550_tx_is_busy,
.rx_data_is_avail = uart16550_rx_data_is_avail,
.get_rx_err = uart16550_get_rx_err,
}
};
void uart16550_set_property(int enabled, int port_mapped, uint64_t base_addr)
{
uart_enabled = enabled;
serial_port_mapped = port_mapped;
Tgt_Uarts[0].base_address = (uint32_t) base_addr;
}

130
debug/uart16550.h Normal file
View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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
* OWNER 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.
*/
#ifndef UART16550_H
#define UART16550_H
/* Register / bit definitions for 16c550 uart */
#define UART16550_RBR 0x00
/*receive buffer register | base+00h, dlab=0b r*/
#define UART16550_THR 0x00
/*transmit holding register | base+00h, dlab=0b w*/
#define UART16550_DLL 0x00
/*divisor least significant byte | base+00h, dlab=1b rw*/
#define UART16550_IER 0x01
/*interrupt enable register | base+01h, dlab=0b rw*/
#define UART16550_DLM 0x01
/*divisor most significant byte | base+01h, dlab=1b rw*/
#define UART16550_IIR 0x02
/*interrupt identification register | base+02h, dlab=0b r*/
#define UART16550_FCR 0x02
/*fifo control register | base+02h, dlab=0b w*/
#define UART16550_LCR 0x03
/*line control register | base+03h, dlab=xb rw*/
#define UART16550_MCR 0x04
/*modem control register, only uart0 | base+04h, dlab=xb rw*/
#define UART16550_LSR 0x05
/*line status register | base+05h, dlab=xb r*/
#define UART16550_MSR 0x06
/*modem status register, only uart0 | base+06h, dlab=xb r*/
#define UART16550_SCR 0x07
/*scratch pad register | base+07h, dlab=xb rw*/
#define UART16550_MDR1 0x08
#define UARTML7213_BRCSR 0x0e
/*baud rate reference clock select register dlab xb*/
#define UARTML7213_SRST 0x0f /*Soft Reset Register dlab xb*/
/* value definitions for IIR */
#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
#define IIR_RXTOUT 0x0c
#define IIR_RLS 0x06
#define IIR_RXRDY 0x04
#define IIR_TXRDY 0x02
#define IIR_NOPEND 0x01
#define IIR_MLSC 0x00
#define IER_EDSSI (0x0008)
/*enable/disable modem status interrupt*/
#define IER_ELSI (0x0004)
/*enable/disable receive data error interrupt*/
#define IER_ETBEI (0x0002)
/*enable/disable transmit data write request interrupt*/
#define IER_ERBFI (0x0001)
/*enable/disable receive data read request interrupt*/
/* definition for LCR */
#define LCR_DLAB (1 << 7) /*DLAB THR/RBR&IER or DLL&DLM= Bit 7*/
#define LCR_SB (1 << 6) /*break control on/off= Bit 6*/
#define LCR_SP (1 << 5) /*Specifies the operation of parity bit*/
#define LCR_EPS (1 << 4) /*Specifies the logic of a parity bit*/
#define LCR_PEN (1 << 3) /*Specifies whether to add a parity bit*/
#define LCR_STB (1 << 2) /*stop bit length*/
#define LCR_WL8 (0x03) /*number of bits of serial data*/
#define LCR_WL7 (0x02) /*number of bits of serial data*/
#define LCR_WL6 (0x01) /*number of bits of serial data*/
#define LCR_WL5 (0x00) /*number of bits of serial data*/
#define LCR_PARITY_ODD (LCR_PEN)
#define LCR_PARITY_NONE 0x0
#define LCR_PARITY_EVEN (LCR_PEN | LCR_EPS)
#define LCR_NB_STOP_BITS_1 0x0
#define LCR_NB_STOP_BITS_2 (LCR_STB)
/* bit definitions for LSR */
/* at least one error in data within fifo */
#define LSR_ERR (1 << 7)
/* Transmit data Present */
#define LSR_TEMT (1 << 6)
/* Transmit data write request present */
#define LSR_THRE (1 << 5)
/* Break interrupt data Present */
#define LSR_BI (1 << 4)
/* Framing Error Occurred */
#define LSR_FE (1 << 3)
/* Parity Error Occurred */
#define LSR_PE (1 << 2)
/* Overrun error */
#define LSR_OE (1 << 1)
/* Readable received data is present */
#define LSR_DR (1 << 0)
/* definition for MCR */
#define MCR_RTS (1 << 1) /* Request to Send */
#define MCR_DTR (1 << 0) /* Data Terminal Ready */
/* definition for FCR */
#define FCR_RX_MASK 0xc0
#define FCR_DMA (1 << 3)
#define FCR_TFR (1 << 2) /* Reset Transmit Fifo */
#define FCR_RFR (1 << 1) /* Reset Receive Fifo */
#define FCR_FIFOE (1 << 0) /* Fifo Enable */
#define UART_IER_DISABLE_ALL 0x00000000
#endif /* !UART16550_H */

398
debug/vuart.c Normal file
View File

@@ -0,0 +1,398 @@
/*-
* Copyright (c) 2012 NetApp, Inc.
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2018 Intel Corporation
* 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 NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#include <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "uart16550.h"
#include "serial_internal.h"
#define COM1_BASE 0x3F8
#define COM1_IRQ 4
#define DEFAULT_RCLK 1843200
#define DEFAULT_BAUD 9600
#define RX_SIZE 256
#define TX_SIZE 65536
#define vuart_lock_init(vu) spinlock_init(&((vu)->lock))
#define vuart_lock(vu) spinlock_obtain(&((vu)->lock))
#define vuart_unlock(vu) spinlock_release(&((vu)->lock))
#define vm_vuart(vm) (vm->vuart)
static void fifo_reset(struct fifo *fifo)
{
fifo->rindex = 0;
fifo->windex = 0;
fifo->num = 0;
}
static void fifo_init(struct fifo *fifo, int sz)
{
fifo->buf = calloc(1, sz);
ASSERT(fifo->buf != NULL, "");
fifo->size = sz;
fifo_reset(fifo);
}
static char fifo_putchar(struct fifo *fifo, char ch)
{
fifo->buf[fifo->windex] = ch;
if (fifo->num < fifo->size) {
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
} else {
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->windex = (fifo->windex + 1) % fifo->size;
}
return 0;
}
static char fifo_getchar(struct fifo *fifo)
{
char c;
if (fifo->num > 0) {
c = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
return c;
} else
return -1;
}
static int fifo_numchars(struct fifo *fifo)
{
return fifo->num;
}
/*
* The IIR returns a prioritized interrupt reason:
* - receive data available
* - transmit holding register empty
*
* Return an interrupt reason if one is available.
*/
static int uart_intr_reason(struct vuart *vu)
{
if ((vu->lsr & LSR_OE) != 0 && (vu->ier & IER_ELSI) != 0)
return IIR_RLS;
else if (fifo_numchars(&vu->rxfifo) > 0 && (vu->ier & IER_ERBFI) != 0)
return IIR_RXTOUT;
else if (vu->thre_int_pending && (vu->ier & IER_ETBEI) != 0)
return IIR_TXRDY;
else
return IIR_NOPEND;
}
static void uart_init(struct vuart *vu)
{
uint16_t divisor;
divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
vu->dll = divisor;
vu->dlh = divisor >> 16;
vu->active = false;
vu->base = COM1_BASE;
fifo_init(&vu->rxfifo, RX_SIZE);
fifo_init(&vu->txfifo, TX_SIZE);
vuart_lock_init(vu);
}
/*
* Toggle the COM port's intr pin depending on whether or not we have an
* interrupt condition to report to the processor.
*/
static void uart_toggle_intr(struct vuart *vu)
{
char intr_reason;
intr_reason = uart_intr_reason(vu);
if (intr_reason != IIR_NOPEND) {
if (vu->vm->vpic)
vpic_assert_irq(vu->vm, COM1_IRQ);
vioapic_assert_irq(vu->vm, COM1_IRQ);
if (vu->vm->vpic)
vpic_deassert_irq(vu->vm, COM1_IRQ);
vioapic_deassert_irq(vu->vm, COM1_IRQ);
}
}
static void uart_write(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t offset,
__unused size_t width, uint32_t value)
{
struct vuart *vu = vm_vuart(vm);
offset -= vu->base;
vuart_lock(vu);
/*
* Take care of the special case DLAB accesses first
*/
if ((vu->lcr & LCR_DLAB) != 0) {
if (offset == UART16550_DLL) {
vu->dll = value;
goto done;
}
if (offset == UART16550_DLM) {
vu->dlh = value;
goto done;
}
}
switch (offset) {
case UART16550_THR:
fifo_putchar(&vu->txfifo, value);
vu->thre_int_pending = true;
break;
case UART16550_IER:
/*
* Apply mask so that bits 4-7 are 0
* Also enables bits 0-3 only if they're 1
*/
vu->ier = value & 0x0F;
break;
case UART16550_FCR:
/*
* The FCR_ENABLE bit must be '1' for the programming
* of other FCR bits to be effective.
*/
if ((value & FCR_FIFOE) == 0) {
vu->fcr = 0;
} else {
if ((value & FCR_RFR) != 0)
fifo_reset(&vu->rxfifo);
vu->fcr = value &
(FCR_FIFOE | FCR_DMA | FCR_RX_MASK);
}
break;
case UART16550_LCR:
vu->lcr = value;
break;
case UART16550_MCR:
/* ignore modem */
break;
case UART16550_LSR:
/*
* Line status register is not meant to be written to
* during normal operation.
*/
break;
case UART16550_MSR:
/*
* As far as I can tell MSR is a read-only register.
*/
break;
case UART16550_SCR:
vu->scr = value;
break;
default:
break;
}
done:
uart_toggle_intr(vu);
vuart_unlock(vu);
}
static uint32_t uart_read(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t offset,
__unused size_t width)
{
char iir, intr_reason, reg;
struct vuart *vu = vm_vuart(vm);
offset -= vu->base;
vuart_lock(vu);
/*
* Take care of the special case DLAB accesses first
*/
if ((vu->lcr & LCR_DLAB) != 0) {
if (offset == UART16550_DLL) {
reg = vu->dll;
goto done;
}
if (offset == UART16550_DLM) {
reg = vu->dlh;
goto done;
}
}
switch (offset) {
case UART16550_RBR:
vu->lsr &= ~LSR_OE;
reg = fifo_getchar(&vu->rxfifo);
break;
case UART16550_IER:
reg = vu->ier;
break;
case UART16550_IIR:
iir = (vu->fcr & FCR_FIFOE) ? IIR_FIFO_MASK : 0;
intr_reason = uart_intr_reason(vu);
/*
* Deal with side effects of reading the IIR register
*/
if (intr_reason == IIR_TXRDY)
vu->thre_int_pending = false;
iir |= intr_reason;
reg = iir;
break;
case UART16550_LCR:
reg = vu->lcr;
break;
case UART16550_MCR:
reg = vu->mcr;
break;
case UART16550_LSR:
/* Transmitter is always ready for more data */
vu->lsr |= LSR_TEMT | LSR_THRE;
/* Check for new receive data */
if (fifo_numchars(&vu->rxfifo) > 0)
vu->lsr |= LSR_DR;
else
vu->lsr &= ~LSR_DR;
reg = vu->lsr;
/* The LSR_OE bit is cleared on LSR read */
vu->lsr &= ~LSR_OE;
break;
case UART16550_MSR:
/* ignore modem I*/
reg = 0;
break;
case UART16550_SCR:
reg = vu->scr;
break;
default:
reg = 0xFF;
break;
}
done:
uart_toggle_intr(vu);
vuart_unlock(vu);
return reg;
}
void vuart_register_io_handler(struct vm *vm)
{
struct vm_io_range range = {
.flags = IO_ATTR_RW,
.base = 0x3f8,
.len = 8
};
register_io_emulation_handler(vm, &range, uart_read, uart_write);
}
void vuart_console_tx_chars(void)
{
struct vuart *vu;
vu = vuart_console_active();
if (vu == NULL)
return;
vuart_lock(vu);
while (fifo_numchars(&vu->txfifo) > 0)
printf("%c", fifo_getchar(&vu->txfifo));
vuart_unlock(vu);
}
void vuart_console_rx_chars(uint32_t serial_handle)
{
struct vuart *vu;
uint32_t vbuf_len;
char buffer[100];
uint32_t buf_idx = 0;
if (serial_handle == SERIAL_INVALID_HANDLE) {
pr_err("%s: invalid serial handle 0x%llx\n",
__func__, serial_handle);
return;
}
vu = vuart_console_active();
if (vu == NULL)
return;
vuart_lock(vu);
/* Get data from serial */
vbuf_len = serial_gets(serial_handle, buffer, 100);
if (vbuf_len) {
while (buf_idx < vbuf_len) {
if (buffer[buf_idx] == GUEST_CONSOLE_TO_HV_SWITCH_KEY) {
/* Switch the console */
shell_switch_console();
break;
}
buf_idx++;
}
if (vu->active != false) {
buf_idx = 0;
while (buf_idx < vbuf_len)
fifo_putchar(&vu->rxfifo, buffer[buf_idx++]);
uart_toggle_intr(vu);
}
}
vuart_unlock(vu);
}
struct vuart *vuart_console_active(void)
{
struct vm *vm = get_vm_from_vmid(0);
if (vm && vm->vuart) {
struct vuart *vu = vm->vuart;
if (vu->active)
return vm->vuart;
}
return NULL;
}
void *vuart_init(struct vm *vm)
{
struct vuart *vu;
vu = calloc(1, sizeof(struct vuart));
ASSERT(vu != NULL, "");
uart_init(vu);
vu->vm = vm;
vuart_register_io_handler(vm);
return vu;
}