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; } diff --git a/common/setup.c b/common/setup.c index 97645fea..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; - } - init_uart(&cfg); - 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 31cf0f28..2e87077a 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 @@ -39,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(); @@ -68,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)); } @@ -94,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 */ @@ -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(com_idx_t com, const uart_config_t *cfg) { mcr_t mcr = {0}; fcr_t fcr = {0}; ier_t ier = {0}; @@ -151,8 +165,9 @@ void __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); } void __text_init init_uart_input(const cpu_t *cpu) { @@ -168,11 +183,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; @@ -203,8 +218,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; } @@ -215,8 +229,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..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 void 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 */