From eb15d2ffa3c1db16d72facf7d4672466a1de484b Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sat, 6 Jun 2026 17:51:01 +0100 Subject: [PATCH] x86_64: update serial driver to support kernel debugging --- arch/x86_64/include/arch/serial.h | 28 ++++- arch/x86_64/init.c | 3 +- arch/x86_64/serial.c | 187 +++++++++++++++++++++++------- 3 files changed, 170 insertions(+), 48 deletions(-) diff --git a/arch/x86_64/include/arch/serial.h b/arch/x86_64/include/arch/serial.h index 2c5da92..7e6e332 100644 --- a/arch/x86_64/include/arch/serial.h +++ b/arch/x86_64/include/arch/serial.h @@ -1,14 +1,21 @@ #ifndef ARCH_SERIAL_H_ #define ARCH_SERIAL_H_ +#include +#include + #ifdef __cplusplus extern "C" { #endif -#define SERIAL_PORT_A 0x3F8 -#define SERIAL_PORT_B 0x2F8 -#define SERIAL_PORT_C 0x3E8 -#define SERIAL_PORT_D 0x2E8 +#define COM(x) (x) + +#define COM1 COM(1) +#define COM2 COM(2) +#define COM3 COM(3) +#define COM4 COM(4) + +extern void serial_init(int baud); extern void serial_putchar(int port, char ch); @@ -18,7 +25,18 @@ extern char serial_recv_byte(int device); extern int serial_rcvd(int device); -extern void early_serialcon_init(int baud); +extern kern_status_t serial_read( + int device, + void *buf, + size_t max, + size_t *nr_read); +extern kern_status_t serial_write( + int device, + const void *buf, + size_t count, + size_t *nr_written); + +extern void early_serialcon_init(void); #ifdef __cplusplus } diff --git a/arch/x86_64/init.c b/arch/x86_64/init.c index b6f123f..bcf67e6 100644 --- a/arch/x86_64/init.c +++ b/arch/x86_64/init.c @@ -78,7 +78,7 @@ void early_console_init(void) vgacon_init(); } else if (!strcmp(dest, "ttyS0")) { /* write log messages to serial port */ - early_serialcon_init(115200); + early_serialcon_init(); } } @@ -109,6 +109,7 @@ int ml_init(uintptr_t arg) parse_cmdline(PTR32(mb->cmdline)); + serial_init(115200); early_console_init(); bootstrap_cpu_init(); diff --git a/arch/x86_64/serial.c b/arch/x86_64/serial.c index c48f596..81818e8 100644 --- a/arch/x86_64/serial.c +++ b/arch/x86_64/serial.c @@ -3,50 +3,78 @@ #include #include #include +#include -#define COM1 0x3F8 -#define COM2 0x2F8 -#define COM3 0x3E8 -#define COM4 0x2E8 +#define SERIAL_BUFFER_SIZE 1024 -static int transmit_empty(int device) +RINGBUF_DECLARE(com1_queue, SERIAL_BUFFER_SIZE); +RINGBUF_DECLARE(com2_queue, SERIAL_BUFFER_SIZE); +RINGBUF_DECLARE(com3_queue, SERIAL_BUFFER_SIZE); +RINGBUF_DECLARE(com4_queue, SERIAL_BUFFER_SIZE); + +static const uint16_t port_addresses[] = { + [COM1] = 0x3F8, + [COM2] = 0x2F8, + [COM3] = 0x3E8, + [COM4] = 0x2E8, +}; + +static int port_active[] = { + [COM1] = 0, + [COM2] = 0, + [COM3] = 0, + [COM4] = 0, +}; + +static struct ringbuf *port_buffer[] = { + [COM1] = &com1_queue, + [COM2] = &com2_queue, + [COM3] = &com3_queue, + [COM4] = &com4_queue, +}; + +static int transmit_empty(int port) { - return inportb(device + 5) & 0x20; + return inportb(port_addresses[port] + 5) & 0x20; } -static int serial_received(int device) +static int serial_received(int port) { - return inportb(device + 5) & 0x1; + return inportb(port_addresses[port] + 5) & 0x1; } -void serial_send_byte(int device, char out) +void serial_send_byte(int port, char out) { + uint16_t address = port_addresses[port]; volatile unsigned int _count = 0; - while (!transmit_empty(device)) { + while (!transmit_empty(port)) { _count++; } + (void)_count; - outportb(device, out); + outportb(address, out); - if (device == COM1) { + if (port == COM1) { outportb(0xe9, out); } - while (!transmit_empty(device)) { + while (!transmit_empty(port)) { _count++; } } -char serial_recv_byte(int device) +char serial_recv_byte(int port) { + uint16_t address = port_addresses[port]; volatile unsigned int _count = 0; - while (!serial_received(device)) { + while (!serial_received(port)) { _count++; } + (void)_count; - char c = inportb(device); + char c = inportb(address); - outportb(device + 5, inportb(device + 5) & ~0x1); + outportb(address + 5, inportb(address + 5) & ~0x1); return c; } @@ -100,37 +128,75 @@ static int get_baud_divisor(int baud) static void init_serial_port(int port, int baud) { + uint16_t address = port_addresses[port]; int baud_div = get_baud_divisor(baud); - outportb(port + 1, 0x00); // Disable all interrupts - outportb(port + 3, 0x80); // Enable DLAB (set baud rate divisor) - outportb(port + 0, baud_div); // Set divisor - outportb(port + 1, 0x00); - outportb(port + 3, 0x03); // 8 bits, no parity, one stop bit + outportb(address + 1, 0x00); // Disable all interrupts + outportb(address + 3, 0x80); // Enable DLAB (set baud rate divisor) + outportb(address + 0, baud_div); // Set divisor + outportb(address + 1, 0x00); + outportb(address + 3, 0x03); // 8 bits, no parity, one stop bit outportb( - port + 2, + address + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold - outportb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set + outportb(address + 4, 0x0B); // IRQs enabled, RTS/DSR set outportb( - port + 4, + address + 4, 0x1E); // Set in loopback mode, test the serial chip - outportb(port + 0, 0xAE); // Test serial chip (send byte 0xAE and - // check if serial returns same byte) + outportb(address + 0, 0xAE); // Test serial chip (send byte 0xAE and + // check if serial returns same byte) volatile unsigned int q = 0; while (!serial_received(port)) { q++; } + (void)q; // Check if serial is faulty (i.e: not same byte as sent) - if (inportb(port + 0) != 0xAE) { + if (inportb(address + 0) != 0xAE) { + printk("serial: COM%d is faulty", port); return; } // If serial is not faulty set it in normal operation mode // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) - outportb(port + 1, 0x01); - outportb(port + 4, 0x0F); - printk("serial: port %x initialised", port); + outportb(address + 1, 0x01); + outportb(address + 4, 0x0F); + port_active[port] = 1; + printk("serial: COM%d initialised", port); +} + +kern_status_t serial_read(int device, void *buf, size_t max, size_t *nr_read) +{ + struct ringbuf *src = port_buffer[device]; + + unsigned long flags; + ringbuf_lock(src, &flags); + kern_status_t status = ringbuf_read(src, buf, max, nr_read, &flags); + ringbuf_unlock(src, flags); + + return status; +} + +kern_status_t serial_write( + int device, + const void *buf, + size_t count, + size_t *nr_written) +{ + if (!port_active[device]) { + return KERN_BAD_STATE; + } + + const unsigned char *p = buf; + + size_t w = 0; + for (size_t i = 0; i < count; i++) { + serial_send_byte(device, p[i]); + w++; + } + + *nr_written = w; + return KERN_OK; } static struct console serialcon = { @@ -140,30 +206,67 @@ static struct console serialcon = { .c_lock = SPIN_LOCK_INIT, }; -static int serial_irq1(void) +static int serial_irq_com1_com3(void) { -#if 0 - if (serial_received(COM1)) { + unsigned long flags; + + if (port_active[COM1] && serial_received(COM1)) { unsigned char c = serial_recv_byte(COM1); - printk("serial: COM1 received %c", c); + ringbuf_lock(&com1_queue, &flags); + ringbuf_write(&com1_queue, &c, 1, NULL, &flags); + ringbuf_unlock(&com1_queue, flags); } - if (serial_received(COM3)) { + if (port_active[COM3] && serial_received(COM3)) { unsigned char c = serial_recv_byte(COM3); - printk("serial: COM3 received %c", c); + ringbuf_lock(&com3_queue, &flags); + ringbuf_write(&com3_queue, &c, 1, NULL, &flags); + ringbuf_unlock(&com3_queue, flags); } -#endif return 0; } -static struct irq_hook irq1_hook = { - .irq_callback = serial_irq1, +static int serial_irq_com2_com4(void) +{ + unsigned long flags; + + if (port_active[COM2] && serial_received(COM2)) { + unsigned char c = serial_recv_byte(COM2); + ringbuf_lock(&com2_queue, &flags); + ringbuf_write(&com2_queue, &c, 1, NULL, &flags); + ringbuf_unlock(&com2_queue, flags); + } + + if (port_active[COM4] && serial_received(COM4)) { + unsigned char c = serial_recv_byte(COM4); + ringbuf_lock(&com4_queue, &flags); + ringbuf_write(&com4_queue, &c, 1, NULL, &flags); + ringbuf_unlock(&com4_queue, flags); + } + + return 0; +} + +static struct irq_hook irq_hook_com1_com3 = { + .irq_callback = serial_irq_com1_com3, }; -void early_serialcon_init(int baud) +static struct irq_hook irq_hook_com2_com4 = { + .irq_callback = serial_irq_com2_com4, +}; + +void serial_init(int baud) { - hook_irq(IRQ4, &irq1_hook); + hook_irq(IRQ4, &irq_hook_com1_com3); + hook_irq(IRQ3, &irq_hook_com2_com4); init_serial_port(COM1, baud); + init_serial_port(COM2, baud); + init_serial_port(COM3, baud); + init_serial_port(COM4, baud); +} + +void early_serialcon_init(void) +{ console_register(&serialcon); }