Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions common/cmdline.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021 Amazon.com, Inc. or its affiliates.
* Copyright (c) 2022 Open Source Security, Inc.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand All @@ -24,6 +25,7 @@
*/

#include <cmdline.h>
#include <drivers/serial.h>
#include <ktf.h>
#include <mm/regions.h>
#include <string.h>
Expand Down Expand Up @@ -56,6 +58,18 @@ bool_cmd("qemu_console", opt_qemu_console);
bool opt_poweroff = true;
bool_cmd("poweroff", opt_poweroff);

static char opt_com1[20];
string_cmd("com1", opt_com1);

static char opt_com2[20];
string_cmd("com2", opt_com2);

static char opt_com3[20];
string_cmd("com3", opt_com3);

static char opt_com4[20];
string_cmd("com4", opt_com4);

const char *kernel_cmdline;

void __text_init cmdline_parse(const char *cmdline) {
Expand Down Expand Up @@ -116,3 +130,182 @@ void __text_init cmdline_parse(const char *cmdline) {
}
}
}

static __text_init bool _parse_com_port(char *port_str, io_port_t *port) {
unsigned long tmp;

if (string_empty(port_str))
return false;

tmp = strtoul(port_str, NULL, 16);
switch (tmp) {
case COM1_PORT:
case COM2_PORT:
case COM3_PORT:
case COM4_PORT:
*port = tmp;
break;
default:
return false;
}

return true;
}

static __text_init bool _parse_com_baud(char *baud_str, com_baud_t *baud) {
unsigned long tmp;

if (string_empty(baud_str))
return false;

tmp = strtoul(baud_str, NULL, 0);
switch (tmp) {
case COM_BAUD_300:
case COM_BAUD_1200:
case COM_BAUD_2400:
case COM_BAUD_4800:
case COM_BAUD_9600:
case COM_BAUD_19200:
case COM_BAUD_38400:
case COM_BAUD_57600:
case COM_BAUD_115200:
*baud = tmp;
break;
default:
return false;
}

return true;
}

static __text_init bool _parse_com_width(char *frame_size_str,
com_frame_size_t *frame_size) {
unsigned long tmp;

if (string_empty(frame_size_str))
return false;

tmp = strtoul(frame_size_str, NULL, 0);
switch (tmp) {
case 8:
*frame_size = COM_FRAME_SIZE_8_BITS;
break;
case 7:
*frame_size = COM_FRAME_SIZE_7_BITS;
break;
case 6:
*frame_size = COM_FRAME_SIZE_6_BITS;
break;
case 5:
*frame_size = COM_FRAME_SIZE_5_BITS;
break;
default:
return false;
}

return true;
}

static __text_init bool _parse_com_parity(char *parity_str, com_parity_t *parity) {
if (string_empty(parity_str) || strlen(parity_str) > 1)
return false;

switch (parity_str[0]) {
case 'n':
*parity = COM_NO_PARITY;
break;
case 'o':
*parity = COM_ODD_PARITY;
break;
case 'e':
*parity = COM_EVEN_PARITY;
break;
case 'h':
*parity = COM_HIGH_PARITY;
break;
case 'l':
*parity = COM_LOW_PARITY;
break;
default:
return false;
}

return true;
}

static __text_init bool _parse_com_stop_bit(char *stop_bit_str,
com_stop_bit_t *stop_bit) {
unsigned long tmp;

if (string_empty(stop_bit_str))
return false;

tmp = strtoul(stop_bit_str, NULL, 0);
switch (tmp) {
case 1:
*stop_bit = COM_STOP_BIT_1;
break;
case 2:
*stop_bit = COM_STOP_BIT_2;
break;
default:
return false;
}

return true;
}

__text_init bool parse_com_port(com_idx_t com, uart_config_t *cfg) {
char *tmp_str = NULL;
char *opt_com;

switch (com) {
case COM1:
opt_com = opt_com1;
break;
case COM2:
opt_com = opt_com2;
break;
case COM3:
opt_com = opt_com3;
break;
case COM4:
opt_com = opt_com4;
break;
default:
return false;
}

if (string_empty(opt_com))
return false;

tmp_str = strtok(opt_com, ",");
if (!_parse_com_port(tmp_str, &cfg->port))
return false;

tmp_str = strtok(NULL, ",");
if (string_empty(tmp_str))
cfg->baud = DEFAULT_BAUD_SPEED;
else if (!_parse_com_baud(tmp_str, &cfg->baud))
return false;

tmp_str = strtok(NULL, ",");
if (string_empty(tmp_str))
cfg->frame_size = COM_FRAME_SIZE_8_BITS;
else if (!_parse_com_width(tmp_str, &cfg->frame_size))
return false;

tmp_str = strtok(NULL, ",");
if (string_empty(tmp_str))
cfg->parity = COM_NO_PARITY;
else if (!_parse_com_parity(tmp_str, &cfg->parity))
return false;

tmp_str = strtok(NULL, ",");
if (string_empty(tmp_str))
cfg->stop_bit = COM_STOP_BIT_1;
else if (!_parse_com_stop_bit(tmp_str, &cfg->stop_bit))
return false;

return true;
}
26 changes: 17 additions & 9 deletions common/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@
#include <perfmon/pfmlib.h>
#endif

io_port_t __data_rmode com_ports[2] = {COM1_PORT, COM2_PORT};

boot_flags_t boot_flags;

static unsigned bsp_cpu_id = 0;
Expand All @@ -70,18 +68,28 @@ unsigned get_bsp_cpu_id(void) { return bsp_cpu_id; }
void set_bsp_cpu_id(unsigned cpu_id) { bsp_cpu_id = cpu_id; }

#define QEMU_CONSOLE_PORT 0x0e9
static void __text_init init_console(void) {
get_com_ports();

uart_init(com_ports[0], DEFAULT_BAUD_SPEED);
register_console_callback(serial_console_write, _ptr(com_ports[0]));

printk("COM1: %x, COM2: %x\n", com_ports[0], com_ports[1]);
static void __text_init init_console(void) {
uart_config_t cfg = {0};

if (!parse_com_port(COM1, &cfg)) {
/* Use first COM port indicated by BIOS (if none, use COM1) */
cfg.port = get_first_com_port();
cfg.baud = DEFAULT_BAUD_SPEED;
cfg.frame_size = COM_FRAME_SIZE_8_BITS;
cfg.parity = COM_NO_PARITY;
cfg.stop_bit = COM_STOP_BIT_1;
}
init_uart(&cfg);
register_console_callback(serial_console_write, _ptr(cfg.port));

if (opt_qemu_console) {
register_console_callback(qemu_console_write, _ptr(QEMU_CONSOLE_PORT));
printk("Initialized QEMU console at port 0x%x", QEMU_CONSOLE_PORT);
}

printk("Serial console at: ");
display_uart_config(&cfg);
}

static __always_inline void zero_bss(void) {
Expand Down Expand Up @@ -218,7 +226,7 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic,
init_pci();

/* Initialize console input */
uart_input_init(get_bsp_cpu_id());
init_uart_input(get_bsp_cpu_id());

/* Initialize timers */
bool hpet_initialized = false;
Expand Down
83 changes: 66 additions & 17 deletions drivers/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,53 @@ static struct {
unsigned init;
} input_state;

extern io_port_t com_ports[2];
io_port_t __data_rmode com_ports[4];

static inline const char *com_port_name(com_port_t port) {
switch (port) {
case COM1_PORT:
return "COM1";
case COM2_PORT:
return "COM2";
case COM3_PORT:
return "COM3";
case COM4_PORT:
return "COM4";
default:
BUG();
}
}

static inline void set_port_mode(io_port_t port, bool stop_bit, uint8_t width) {
lcr_t lcr = {0};
static const uint8_t com_frame_size_values[] = {
[COM_FRAME_SIZE_5_BITS] = 5,
[COM_FRAME_SIZE_6_BITS] = 6,
[COM_FRAME_SIZE_7_BITS] = 7,
[COM_FRAME_SIZE_8_BITS] = 8,
};

lcr.stop_bit = stop_bit;
lcr.width = width;
outb(port + UART_LCR_REG_OFFSET, lcr.reg);
static const char com_parity_names[] = {
[COM_NO_PARITY] = 'n', [COM_ODD_PARITY] = 'o', [COM_EVEN_PARITY] = 'e',
[COM_HIGH_PARITY] = 'h', [COM_LOW_PARITY] = 'l',
};

#define COM_STOP_BIT_VALUE(cfg) ((cfg)->stop_bit + 1)

void display_uart_config(const uart_config_t *cfg) {
printk("[%s] 0x%x %u,%u%c%u\n", com_port_name(cfg->port), cfg->port, cfg->baud,
com_frame_size_values[cfg->frame_size], com_parity_names[cfg->parity],
COM_STOP_BIT_VALUE(cfg));
}

io_port_t get_first_com_port(void) {
memcpy((void *) com_ports, _ptr(BDA_COM_PORTS_ENTRY), sizeof(com_ports));

for (unsigned int i = 0; i < ARRAY_SIZE(com_ports); i++) {
if (com_ports[i] != 0x0)
return com_ports[i];
}

/* Fallback to COM1 */
return COM1_PORT;
}

static inline void set_dlab(io_port_t port, bool dlab) {
Expand All @@ -55,6 +94,21 @@ static inline void set_dlab(io_port_t port, bool dlab) {
outb(port + UART_LCR_REG_OFFSET, lcr.reg);
}

static inline void set_port_mode(uart_config_t *cfg) {
lcr_t lcr = {0};

lcr.stop_bit = cfg->stop_bit;
lcr.width = cfg->frame_size;
lcr.parity = cfg->parity;

outb(cfg->port + UART_LCR_REG_OFFSET, lcr.reg);

/* Set baud speed by applying divisor to DLL+DLH */
set_dlab(cfg->port, true);
outw(cfg->port + UART_DLL_REG_OFFSET, DEFAULT_BAUD_SPEED / cfg->baud);
set_dlab(cfg->port, false);
}

static inline bool thr_empty(io_port_t port) {
lsr_t lsr;

Expand All @@ -71,30 +125,25 @@ static inline bool receiver_ready(io_port_t port) {
return msr.dsr && msr.cts;
}

void uart_init(io_port_t port, unsigned baud) {
void __text_init init_uart(uart_config_t *cfg) {
mcr_t mcr = {0};

/* Enable interrupts for received data available */
outb(port + UART_IER_REG_OFFSET, 0x01);
outb(cfg->port + UART_IER_REG_OFFSET, 0x01);

/* Disable FIFO control */
outb(port + UART_FCR_REG_OFFSET, 0x00);
outb(cfg->port + UART_FCR_REG_OFFSET, 0x00);

/* Set 8n1 mode */
set_port_mode(port, STOP_BIT_1, FRAME_SIZE_8_BITS);

/* Set baud speed by applying divisor to DLL+DLH */
set_dlab(port, true);
outw(port + UART_DLL_REG_OFFSET, DEFAULT_BAUD_SPEED / baud);
set_dlab(port, false);
set_port_mode(cfg);

/* Set tx/rx ready state */
mcr.dtr = 1;
mcr.rts = 1;
outb(port + UART_MCR_REG_OFFSET, mcr.reg);
outb(cfg->port + UART_MCR_REG_OFFSET, mcr.reg);
}

void uart_input_init(uint8_t dst_cpus) {
void __text_init init_uart_input(uint8_t dst_cpus) {
/* Initialize input state */
memset(&input_state, 0, sizeof(input_state));

Expand Down
2 changes: 1 addition & 1 deletion grub/boot/grub/grub.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ terminal_input --append serial
terminal_output --append serial

menuentry "kernel64" {
multiboot /boot/kernel64.bin poweroff=1
multiboot /boot/kernel64.bin poweroff=1 com1=0x3f8,115200,8,n,1
boot
}
Loading