diff --git a/common/cmdline.c b/common/cmdline.c index 446feda1..8cd9487c 100644 --- a/common/cmdline.c +++ b/common/cmdline.c @@ -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 @@ -24,6 +25,7 @@ */ #include +#include #include #include #include @@ -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) { @@ -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; +} diff --git a/common/setup.c b/common/setup.c index 022f0575..6a13239b 100644 --- a/common/setup.c +++ b/common/setup.c @@ -60,8 +60,6 @@ #include #endif -io_port_t __data_rmode com_ports[2] = {COM1_PORT, COM2_PORT}; - boot_flags_t boot_flags; static unsigned bsp_cpu_id = 0; @@ -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) { @@ -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; diff --git a/drivers/serial.c b/drivers/serial.c index 7937355f..ec921962 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -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) { @@ -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; @@ -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)); diff --git a/grub/boot/grub/grub.cfg b/grub/boot/grub/grub.cfg index b3438746..5b06d569 100644 --- a/grub/boot/grub/grub.cfg +++ b/grub/boot/grub/grub.cfg @@ -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 } diff --git a/include/cmdline.h b/include/cmdline.h index 85e6a354..49640f20 100644 --- a/include/cmdline.h +++ b/include/cmdline.h @@ -30,6 +30,8 @@ #include #include +#include + #define PARAM_MAX_LENGTH 32 struct __packed ktf_param { @@ -47,7 +49,7 @@ struct __packed ktf_param { #define cmd_param(_name, _var, _type) \ __param_size_check(_var, _name); \ - __ktfparam __cmd_##_var = {_name, _type, &_var, sizeof(_var)}; + __ktfparam __cmd_##_var = {_name, _type, &(_var), sizeof(_var)}; #define bool_cmd(_cmdname, _varname) cmd_param(_cmdname, _varname, BOOL) #define ulong_cmd(_cmdname, _varname) cmd_param(_cmdname, _varname, ULONG) @@ -65,10 +67,13 @@ extern bool opt_hpet; extern bool opt_fpu; extern bool opt_qemu_console; extern bool opt_poweroff; + extern const char *kernel_cmdline; extern void cmdline_parse(const char *cmdline); +extern bool parse_com_port(com_idx_t com, uart_config_t *cfg); + /* Static declarations */ static inline int parse_bool(const char *s) { diff --git a/include/drivers/serial.h b/include/drivers/serial.h index f15d1503..a8d5906f 100644 --- a/include/drivers/serial.h +++ b/include/drivers/serial.h @@ -36,19 +36,68 @@ union line_control_register { }; typedef union line_control_register lcr_t; -#define FRAME_SIZE_8_BITS 0x03 -#define FRAME_SIZE_7_BITS 0x02 -#define FRAME_SIZE_6_BITS 0x01 -#define FRAME_SIZE_5_BITS 0x00 +enum com_idx { + COM1 = 0, + COM2 = 1, + COM3 = 2, + COM4 = 3, +}; +typedef enum com_idx com_idx_t; + +enum com_port { + COM1_PORT = 0x3f8, + COM2_PORT = 0x2f8, + COM3_PORT = 0x3e8, + COM4_PORT = 0x2e8, +}; +typedef enum com_port com_port_t; + +enum com_baud { + COM_BAUD_300 = 300, + COM_BAUD_1200 = 1200, + COM_BAUD_2400 = 2400, + COM_BAUD_4800 = 4800, + COM_BAUD_9600 = 9600, + COM_BAUD_19200 = 19200, + COM_BAUD_38400 = 38400, + COM_BAUD_57600 = 57600, + COM_BAUD_115200 = 115200, +}; +typedef enum com_baud com_baud_t; -#define STOP_BIT_1 0x00 -#define STOP_BIT_2 0x01 +#define DEFAULT_BAUD_SPEED COM_BAUD_115200 -#define NO_PARITY 0x00 -#define ODD_PARITY 0x01 -#define EVEN_PARITY 0x03 -#define HIGH_PARITY 0x05 -#define LOW_PARITY 0x07 +enum com_frame_size { + COM_FRAME_SIZE_8_BITS = 0x03, + COM_FRAME_SIZE_7_BITS = 0x02, + COM_FRAME_SIZE_6_BITS = 0x01, + COM_FRAME_SIZE_5_BITS = 0x00, +}; +typedef enum com_frame_size com_frame_size_t; + +enum com_stop_bit { + COM_STOP_BIT_1 = 0x00, + COM_STOP_BIT_2 = 0x01, +}; +typedef enum com_stop_bit com_stop_bit_t; + +enum com_parity { + COM_NO_PARITY = 0x00, + COM_ODD_PARITY = 0x01, + COM_EVEN_PARITY = 0x03, + COM_HIGH_PARITY = 0x05, + COM_LOW_PARITY = 0x07, +}; +typedef enum com_parity com_parity_t; + +struct uart_config { + io_port_t port; + com_baud_t baud; + com_frame_size_t frame_size; + com_parity_t parity; + com_stop_bit_t stop_bit; +}; +typedef struct uart_config uart_config_t; union modem_control_register { uint8_t reg; @@ -99,8 +148,14 @@ union interrupt_enable_register { }; typedef union interrupt_enable_register ier_t; -#define COM1_IRQ 4 /* IRQ 4 */ -#define COM2_IRQ 3 /* IRQ 3 */ +enum com_irq { + COM1_IRQ = 4, /* IRQ 4 */ + COM2_IRQ = 3, /* IRQ 3 */ + COM3_IRQ = COM1_IRQ, + COM4_IRQ = COM2_IRQ, +}; +typedef enum com_irq com_irq_t; + #define COM1_IRQ0_OFFSET (PIC_IRQ0_OFFSET + COM1_IRQ) #define COM2_IRQ0_OFFSET (PIC_IRQ0_OFFSET + COM2_IRQ) @@ -117,17 +172,20 @@ typedef union interrupt_enable_register ier_t; #define UART_MSR_REG_OFFSET 0x06 #define UART_SCR_REG_OFFSET 0x07 -#define DEFAULT_BAUD_SPEED 115200 - #define UART_IIR_STATUS_MASK 0x0E /* bits 3, 2, 1 */ #define UART_IIR_RBR_READY 0x04 /* External declarations */ -extern void uart_init(io_port_t port, unsigned baud); -extern void uart_input_init(uint8_t dst_cpus); +extern io_port_t com_ports[4]; + +extern io_port_t get_first_com_port(void); +extern void init_uart(uart_config_t *cfg); +extern void init_uart_input(uint8_t dst_cpus); extern void uart_interrupt_handler(void); extern int serial_putchar(io_port_t port, char c); extern int serial_write(io_port_t port, const char *buf, size_t len); +extern void display_uart_config(const uart_config_t *cfg); + #endif /* KTF_DRV_SERIAL_H */ diff --git a/include/setup.h b/include/setup.h index 23b480cf..1f93a65a 100644 --- a/include/setup.h +++ b/include/setup.h @@ -25,9 +25,6 @@ #ifndef KTF_SETUP_H #define KTF_SETUP_H -#define COM1_PORT 0x3f8 -#define COM2_PORT 0x2f8 - #ifndef __ASSEMBLY__ #include #include @@ -41,23 +38,11 @@ struct boot_flags { }; typedef struct boot_flags boot_flags_t; -extern io_port_t com_ports[2]; - extern char cpu_identifier[49]; extern boot_flags_t boot_flags; /* Static declarations */ -static inline void get_com_ports(void) { - memcpy((void *) com_ports, (void *) (BDA_COM_PORTS_ENTRY), sizeof(com_ports)); - - if (com_ports[0] == 0x0) - com_ports[0] = COM1_PORT; - - if (com_ports[1] == 0x0) - com_ports[1] = COM2_PORT; -} - /* External declarations */ extern unsigned get_bsp_cpu_id(void);