mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-09-23 17:58:30 +00:00
initial import
internal commit: 14ac2bc2299032fa6714d1fefa7cf0987b3e3085 Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
236
debug/console.c
Normal file
236
debug/console.c
Normal 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
368
debug/dump.c
Normal 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
161
debug/logmsg.c
Normal 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
824
debug/printf.c
Normal 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(¶m, 0, sizeof(param));
|
||||
param.emit = charout;
|
||||
param.data = &nchars;
|
||||
|
||||
/* execute the printf() */
|
||||
res = do_print(fmt, ¶m, 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(¶m, 0, sizeof(param));
|
||||
param.emit = charmem;
|
||||
param.data = &snparam;
|
||||
|
||||
/* execute the printf() */
|
||||
if (do_print(fmt, ¶m, 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
194
debug/sbuf.c
Normal 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
365
debug/serial.c
Normal 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
208
debug/serial_internal.h
Normal 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
1099
debug/shell_internal.c
Normal file
File diff suppressed because it is too large
Load Diff
187
debug/shell_internal.h
Normal file
187
debug/shell_internal.h
Normal 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
421
debug/shell_public.c
Normal 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
347
debug/uart16550.c
Normal 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
130
debug/uart16550.h
Normal 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
398
debug/vuart.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user