From 9d2f95033a3d8f078787f5d5d82775dd36299d9b Mon Sep 17 00:00:00 2001 From: Pawel Wieczorkiewicz Date: Wed, 6 Sep 2023 13:40:13 +0200 Subject: [PATCH 1/4] drivers,uart: verify if uart port is ready (exists) Register serial console only when UART port really exists and is ready. Otherwise, the serial writing functions trigger a BUG_ON. Signed-off-by: Pawel Wieczorkiewicz --- common/setup.c | 4 ++-- drivers/serial.c | 26 +++++++++++++++++++++----- include/drivers/serial.h | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/common/setup.c b/common/setup.c index 97645fea..c69be47a 100644 --- a/common/setup.c +++ b/common/setup.c @@ -79,8 +79,8 @@ static void __text_init init_console(void) { 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 (init_uart(&cfg) == 0) + register_console_callback(serial_console_write, _ptr(cfg.port)); if (opt_qemu_console) { register_console_callback(qemu_console_write, _ptr(QEMU_CONSOLE_PORT)); diff --git a/drivers/serial.c b/drivers/serial.c index 31cf0f28..1f10d672 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -23,6 +23,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include #include @@ -124,7 +125,20 @@ static inline bool receiver_ready(io_port_t port) { return msr.dsr && msr.cts; } -void __text_init init_uart(uart_config_t *cfg) { +#define PORT_TIMEOUT 100 /* ~100ms */ +static inline int uart_port_ready(io_port_t port) { + unsigned retries = PORT_TIMEOUT; + + do { + if (receiver_ready(port)) + return 0; + io_delay(); + } while (retries--); + + return -ENODEV; +} + +int __text_init init_uart(uart_config_t *cfg) { mcr_t mcr = {0}; fcr_t fcr = {0}; ier_t ier = {0}; @@ -153,6 +167,8 @@ void __text_init init_uart(uart_config_t *cfg) { if (com_ports[0] == NO_COM_PORT) com_ports[0] = cfg->port; + + return uart_port_ready(cfg->port); } void __text_init init_uart_input(const cpu_t *cpu) { @@ -168,11 +184,11 @@ void __text_init init_uart_input(const cpu_t *cpu) { static inline int uart_port_status(io_port_t port) { if (!receiver_ready(port)) - return -1; /* ENODEV */ + return -ENODEV; if (!thr_empty(port)) { io_delay(); - return 1; /* EAGAIN */ + return -EAGAIN; } return 0; @@ -204,7 +220,7 @@ int serial_putchar(io_port_t port, char c) { do { rc = uart_putc(port, c); BUG_ON(rc < 0); - } while (rc > 0 && retries--); + } while (rc == -EAGAIN && retries--); return rc; } @@ -216,7 +232,7 @@ int serial_write(io_port_t port, const char *buf, size_t len) { do { rc = uart_puts(port, buf, len); BUG_ON(rc < 0); - } while (rc > 0 && retries--); + } while (rc == -EAGAIN && retries--); return rc; } diff --git a/include/drivers/serial.h b/include/drivers/serial.h index f631e07d..ce22cc24 100644 --- a/include/drivers/serial.h +++ b/include/drivers/serial.h @@ -199,7 +199,7 @@ typedef enum com_irq com_irq_t; 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 int init_uart(uart_config_t *cfg); extern void init_uart_input(const cpu_t *cpu); extern void uart_interrupt_handler(void); extern int serial_putchar(io_port_t port, char c); From 88e78967864946877655a0b7030544b2eed82606 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorkiewicz Date: Wed, 6 Sep 2023 14:53:11 +0200 Subject: [PATCH 2/4] console: do not register same console callbacks twice Signed-off-by: Pawel Wieczorkiewicz --- common/console.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/console.c b/common/console.c index 8c67ed33..05aa898b 100644 --- a/common/console.c +++ b/common/console.c @@ -105,7 +105,19 @@ void fb_console_write(void *fb_memory, const char *buf, size_t len) { fb_write(fb_memory, buf, len, FB_WHITE); } +static inline bool is_console_callback_registered(console_callback_t cb, void *arg) { + for (unsigned int i = 0; i < num_console_callbacks; i++) { + if (console_callbacks[i].cb == cb && console_callbacks[i].arg == arg) + return true; + } + + return false; +} + void register_console_callback(console_callback_t cb, void *arg) { + if (is_console_callback_registered(cb, arg)) + return; + console_callbacks[num_console_callbacks].cb = cb; console_callbacks[num_console_callbacks++].arg = arg; } From bf221cd22293685911e1fe2f84b297aa7eb90b16 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorkiewicz Date: Wed, 6 Sep 2023 15:21:49 +0200 Subject: [PATCH 3/4] serial,console: fix early and cmdline serial consoles Initialize early serial console with a default config: com1,115200,8,n,1. This is useful to get multiboot and cmdline parsing output. When cmdline has been parsed initialize all specified UART ports and assign serial console to them. This may overwrite the early config for UART com1. Always assign configured UART devices to the global com_ports[] and do not hard code COM identifiers to actual COM ports. Signed-off-by: Pawel Wieczorkiewicz --- common/setup.c | 52 +++++++++++++++++++++++++++------------- drivers/serial.c | 23 +++++++++--------- include/drivers/serial.h | 5 ++-- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/common/setup.c b/common/setup.c index c69be47a..cdf4afe9 100644 --- a/common/setup.c +++ b/common/setup.c @@ -68,27 +68,45 @@ unsigned long cpu_frequency; #define QEMU_CONSOLE_PORT 0x0e9 -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; - } - if (init_uart(&cfg) == 0) - register_console_callback(serial_console_write, _ptr(cfg.port)); +static inline int __text_init init_uart_console(com_idx_t com, const uart_config_t *cfg) { + int rc = init_uart(com, cfg); + + if (rc != 0) + return rc; + + register_console_callback(serial_console_write, _ptr(cfg->port)); + printk("Serial console at: "); + display_uart_config(com, cfg); + + return rc; +} + +static void __text_init init_early_console(void) { + uart_config_t 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_console(COM1, &cfg); 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 void __text_init init_serial_consoles(void) { + uart_config_t cfg; + + for (com_idx_t com = COM1; com < MAX_COM; com++) { + memset(&cfg, 0, sizeof(cfg)); + if (parse_com_port(com, &cfg)) + init_uart_console(com, &cfg); + } } static __always_inline void zero_bss(void) { @@ -166,7 +184,7 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic, unsigned long zero_bss(); /* Initialize console early */ - init_console(); + init_early_console(); if (multiboot_magic == MULTIBOOT2_BOOTLOADER_MAGIC) { /* Indentity mapping is still on, so fill in multiboot structures */ @@ -178,6 +196,8 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic, unsigned long if (!string_empty(kernel_cmdline)) printk("Command line: %s\n", kernel_cmdline); + init_serial_consoles(); + init_boot_traps(); init_real_mode(); diff --git a/drivers/serial.c b/drivers/serial.c index 1f10d672..2ca1c990 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -40,15 +40,15 @@ static struct { io_port_t __data_rmode com_ports[4]; -static inline const char *com_port_name(com_port_t port) { - switch (port) { - case COM1_PORT: +static inline const char *com_name(com_idx_t com) { + switch (com) { + case COM1: return "COM1"; - case COM2_PORT: + case COM2: return "COM2"; - case COM3_PORT: + case COM3: return "COM3"; - case COM4_PORT: + case COM4: return "COM4"; default: BUG(); @@ -69,8 +69,8 @@ static const char com_parity_names[] = { #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, +void display_uart_config(com_idx_t com, const uart_config_t *cfg) { + printk("[%s] 0x%x %u,%u%c%u\n", com_name(com), cfg->port, cfg->baud, com_frame_size_values[cfg->frame_size], com_parity_names[cfg->parity], COM_STOP_BIT_VALUE(cfg)); } @@ -95,7 +95,7 @@ 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) { +static inline void set_port_mode(const uart_config_t *cfg) { lcr_t lcr = {0}; /* Set baud speed by applying divisor to DLL+DLH */ @@ -138,7 +138,7 @@ static inline int uart_port_ready(io_port_t port) { return -ENODEV; } -int __text_init init_uart(uart_config_t *cfg) { +int __text_init init_uart(com_idx_t com, const uart_config_t *cfg) { mcr_t mcr = {0}; fcr_t fcr = {0}; ier_t ier = {0}; @@ -165,8 +165,7 @@ int __text_init init_uart(uart_config_t *cfg) { mcr.aux = 2; outb(cfg->port + UART_MCR_REG_OFFSET, mcr.reg); - if (com_ports[0] == NO_COM_PORT) - com_ports[0] = cfg->port; + com_ports[com] = cfg->port; return uart_port_ready(cfg->port); } diff --git a/include/drivers/serial.h b/include/drivers/serial.h index ce22cc24..326ea8fa 100644 --- a/include/drivers/serial.h +++ b/include/drivers/serial.h @@ -34,6 +34,7 @@ enum com_idx { COM2 = 1, COM3 = 2, COM4 = 3, + MAX_COM, }; typedef enum com_idx com_idx_t; @@ -199,12 +200,12 @@ typedef enum com_irq com_irq_t; extern io_port_t com_ports[4]; extern io_port_t get_first_com_port(void); -extern int init_uart(uart_config_t *cfg); +extern int init_uart(com_idx_t com, const uart_config_t *cfg); extern void init_uart_input(const cpu_t *cpu); 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); +extern void display_uart_config(com_idx_t com, const uart_config_t *cfg); #endif /* KTF_DRV_SERIAL_H */ From 3f9393050506f0b910c2269f698a488373ed70bc Mon Sep 17 00:00:00 2001 From: Pawel Wieczorkiewicz Date: Thu, 7 Sep 2023 10:14:59 +0200 Subject: [PATCH 4/4] driver,uart: do not panic when UART port not ready Signed-off-by: Pawel Wieczorkiewicz --- drivers/serial.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/serial.c b/drivers/serial.c index 2ca1c990..2e87077a 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -218,7 +218,6 @@ int serial_putchar(io_port_t port, char c) { do { rc = uart_putc(port, c); - BUG_ON(rc < 0); } while (rc == -EAGAIN && retries--); return rc; @@ -230,7 +229,6 @@ int serial_write(io_port_t port, const char *buf, size_t len) { do { rc = uart_puts(port, buf, len); - BUG_ON(rc < 0); } while (rc == -EAGAIN && retries--); return rc;