acrn-hypervisor/hypervisor/debug/serial.c

366 lines
8.1 KiB
C

/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <hypervisor.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(const char *uart_id, uint32_t *index)
{
/* Initialize the index to the start of array. */
*index = 0U;
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. */
(*index)++;
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] == NULL) {
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(const 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] != 0U &&
(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;
}
uint32_t serial_get_rx_data(uint32_t uart_handle)
{
uint32_t index;
struct uart *uart;
int data_avail;
uint32_t rx_byte_status;
uint32_t lsr_reg, bytes_read;
uint8_t ch;
uint32_t total_bytes_read = 0U;
if (!SERIAL_VALIDATE_HANDLE(uart_handle)) {
return 0U;
}
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS) {
return 0U;
}
uart = sio_ports[index];
if (uart == NULL) {
return 0U;
}
/* Place all the data available in RX FIFO, in circular buffer */
data_avail = uart->tgt_uart->rx_data_is_avail(
uart->tgt_uart, &lsr_reg);
while (data_avail != 0) {
/* 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 */
(void)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;
data_avail = uart->tgt_uart->rx_data_is_avail(
uart->tgt_uart, &lsr_reg);
}
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 == 0U)) {
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 > 0U; 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;
data_read++;
}
}
/* 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 = 0U;
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 != 0);
/* Transmit character */
uart->tgt_uart->write(uart->tgt_uart, &(c), &bytes_written);
/* Return character written or EOF for error */
return ((bytes_written > 0U) ? 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 == 0U)) {
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 > 0U && 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);
}