From 6cde9b4c1984141d6ee0fc7dee753dd1e339a5d0 Mon Sep 17 00:00:00 2001 From: Kevin Alavik Date: Wed, 14 May 2025 13:33:38 +0200 Subject: [PATCH] feat/kernel: Added built-in UART support --- .vscode/settings.json | 6 +++ GNUmakefile | 2 +- kernel/src/arch/cpu.h | 19 ++++++++ kernel/src/arch/io.c | 42 +++++++++++++++++ kernel/src/arch/io.h | 14 ++++++ kernel/src/dev/serial.c | 82 ++++++++++++++++++++++++++++++++++ kernel/src/dev/serial.h | 45 +++++++++++++++++++ kernel/src/emk.c | 99 ++++------------------------------------- kernel/src/lib/string.c | 65 +++++++++++++++++++++++++++ kernel/src/lib/string.h | 12 +++++ 10 files changed, 295 insertions(+), 91 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 kernel/src/arch/cpu.h create mode 100644 kernel/src/arch/io.c create mode 100644 kernel/src/arch/io.h create mode 100644 kernel/src/dev/serial.c create mode 100644 kernel/src/dev/serial.h create mode 100644 kernel/src/lib/string.c create mode 100644 kernel/src/lib/string.h diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..98d0744 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "stdnoreturn.h": "c", + "serial.h": "c" + } +} \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index 593f83c..dde581a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -2,7 +2,7 @@ MAKEFLAGS += -rR .SUFFIXES: -QEMUFLAGS := -m 2G -debugcon stdio +QEMUFLAGS := -m 2G -serial stdio IMAGE_NAME := release/emk HOST_CC := cc diff --git a/kernel/src/arch/cpu.h b/kernel/src/arch/cpu.h new file mode 100644 index 0000000..5f08074 --- /dev/null +++ b/kernel/src/arch/cpu.h @@ -0,0 +1,19 @@ +#ifndef CPU_H +#define CPU_H + +#include + +static inline noreturn void hlt() +{ + for (;;) + __asm__ volatile("hlt"); +} + +static inline noreturn void hcf() +{ + __asm__ volatile("cli"); + for (;;) + __asm__ volatile("hlt"); +} + +#endif // CPU_H \ No newline at end of file diff --git a/kernel/src/arch/io.c b/kernel/src/arch/io.c new file mode 100644 index 0000000..e43d424 --- /dev/null +++ b/kernel/src/arch/io.c @@ -0,0 +1,42 @@ +#include + +void outb(uint16_t port, uint8_t value) +{ + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port) : "memory"); +} + +uint8_t inb(uint16_t port) +{ + uint8_t result; + __asm__ volatile("inb %1, %0" : "=a"(result) : "Nd"(port)); + return result; +} + +void outw(uint16_t port, uint16_t value) +{ + __asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port) : "memory"); +} + +uint16_t inw(uint16_t port) +{ + uint16_t result; + __asm__ volatile("inw %1, %0" : "=a"(result) : "Nd"(port)); + return result; +} + +void outl(uint16_t port, uint32_t value) +{ + __asm__ volatile("outl %0, %1" : : "a"(value), "Nd"(port) : "memory"); +} + +uint32_t inl(uint16_t port) +{ + uint32_t result; + __asm__ volatile("inl %1, %0" : "=a"(result) : "Nd"(port)); + return result; +} + +void io_wait(void) +{ + __asm__ volatile("outb %%al, $0x80" : : "a"(0) : "memory"); +} \ No newline at end of file diff --git a/kernel/src/arch/io.h b/kernel/src/arch/io.h new file mode 100644 index 0000000..f9fc4bd --- /dev/null +++ b/kernel/src/arch/io.h @@ -0,0 +1,14 @@ +#ifndef IO_H +#define IO_H + +#include + +void outb(uint16_t port, uint8_t value); +uint8_t inb(uint16_t port); +void outw(uint16_t port, uint16_t value); +uint16_t inw(uint16_t port); +void outl(uint16_t port, uint32_t value); +uint32_t inl(uint16_t port); +void io_wait(void); + +#endif // IO_H \ No newline at end of file diff --git a/kernel/src/dev/serial.c b/kernel/src/dev/serial.c new file mode 100644 index 0000000..96048e7 --- /dev/null +++ b/kernel/src/dev/serial.c @@ -0,0 +1,82 @@ +#include +#include + +int serial_init(uint16_t port) +{ + uint16_t base = (uint16_t)port; + + // Disable all interrupts + outb(base + UART_IER, 0x00); + io_wait(); + + // Enable DLAB to set baud rate + outb(base + UART_LCR, UART_LCR_DLAB); + io_wait(); + outb(base + UART_DLL, UART_BAUD_38400 & 0xFF); + io_wait(); + outb(base + UART_DLH, (UART_BAUD_38400 >> 8) & 0xFF); + io_wait(); + + // Disable DLAB and set 8N1 (8 bits, no parity, 1 stop bit) + outb(base + UART_LCR, UART_LCR_8N1); + io_wait(); + + // Enable FIFO, clear them, with 14-byte threshold + outb(base + UART_FCR, UART_FCR_ENABLE | UART_FCR_CLEAR_RX | UART_FCR_CLEAR_TX | UART_FCR_14BYTE); + io_wait(); + + // Set DTR, RTS, and OUT2 + outb(base + UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + io_wait(); + + // Test serial chip (enable loopback mode) + outb(base + UART_MCR, UART_MCR_LOOP | UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + io_wait(); + outb(base + UART_DATA, 0xAE); // Send test byte + io_wait(); + if (inb(base + UART_DATA) != 0xAE) + { + return -1; + } + + outb(base + UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + io_wait(); + return 0; +} + +int serial_write(uint16_t port, const uint8_t *data, uint32_t length) +{ + uint16_t base = port; + uint32_t i; + + for (i = 0; i < length; i++) + { + // Wait for transmitter holding register to be empty + while (!(inb(base + UART_LSR) & UART_LSR_THRE)) + { + io_wait(); + } + outb(base + UART_DATA, data[i]); + } + return i; +} + +int serial_read(uint16_t port, uint8_t *buffer, uint32_t length) +{ + uint16_t base = port; + uint32_t i = 0; + + while (i < length) + { + if (inb(base + UART_LSR) & UART_LSR_DR) + { + buffer[i] = inb(base + UART_DATA); + i++; + } + else + { + break; + } + } + return i; +} \ No newline at end of file diff --git a/kernel/src/dev/serial.h b/kernel/src/dev/serial.h new file mode 100644 index 0000000..b1e26c6 --- /dev/null +++ b/kernel/src/dev/serial.h @@ -0,0 +1,45 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include + +#define COM1 0x3f8 + +// UART register offsets +#define UART_DATA 0x00 // Data register (R/W) +#define UART_IER 0x01 // Interrupt Enable Register (R/W) +#define UART_DLL 0x00 // Divisor Latch Low (R/W, when DLAB=1) +#define UART_DLH 0x01 // Divisor Latch High (R/W, when DLAB=1) +#define UART_FCR 0x02 // FIFO Control Register (W) +#define UART_LCR 0x03 // Line Control Register (R/W) +#define UART_MCR 0x04 // Modem Control Register (R/W) +#define UART_LSR 0x05 // Line Status Register (R) + +// Line Control Register bits +#define UART_LCR_DLAB 0x80 // Divisor Latch Access Bit +#define UART_LCR_8N1 0x03 // 8 bits, no parity, 1 stop bit + +// FIFO Control Register bits +#define UART_FCR_ENABLE 0x01 // Enable FIFO +#define UART_FCR_CLEAR_RX 0x02 // Clear receive FIFO +#define UART_FCR_CLEAR_TX 0x04 // Clear transmit FIFO +#define UART_FCR_14BYTE 0xC0 // 14-byte trigger level + +// Modem Control Register bits +#define UART_MCR_DTR 0x01 // Data Terminal Ready +#define UART_MCR_RTS 0x02 // Request to Send +#define UART_MCR_OUT2 0x08 // OUT2 (used for interrupts) +#define UART_MCR_LOOP 0x10 // Loopback mode + +// Line Status Register bits +#define UART_LSR_DR 0x01 // Data Ready +#define UART_LSR_THRE 0x20 // Transmitter Holding Register Empty + +// Baud rate divisor for 38400 baud (115200 / 3) +#define UART_BAUD_38400 3 + +int serial_init(uint16_t port); +int serial_write(uint16_t port, const uint8_t *data, uint32_t length); +int serial_read(uint16_t port, uint8_t *buffer, uint32_t length); + +#endif // SERIAL_H \ No newline at end of file diff --git a/kernel/src/emk.c b/kernel/src/emk.c index 3f3fe2d..1c9facf 100644 --- a/kernel/src/emk.c +++ b/kernel/src/emk.c @@ -1,107 +1,26 @@ -#include -#include -#include #include +#include +#include +#include __attribute__((used, section(".limine_requests"))) static volatile LIMINE_BASE_REVISION(3); -__attribute__((used, section(".limine_requests"))) static volatile struct limine_framebuffer_request framebuffer_request = { - .id = LIMINE_FRAMEBUFFER_REQUEST, - .revision = 0}; - __attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER; __attribute__((used, section(".limine_requests_end"))) static volatile LIMINE_REQUESTS_END_MARKER; -void *memcpy(void *restrict dest, const void *restrict src, size_t n) -{ - uint8_t *restrict pdest = (uint8_t *restrict)dest; - const uint8_t *restrict psrc = (const uint8_t *restrict)src; - - for (size_t i = 0; i < n; i++) - { - pdest[i] = psrc[i]; - } - - return dest; -} - -void *memset(void *s, int c, size_t n) -{ - uint8_t *p = (uint8_t *)s; - - for (size_t i = 0; i < n; i++) - { - p[i] = (uint8_t)c; - } - - return s; -} - -void *memmove(void *dest, const void *src, size_t n) -{ - uint8_t *pdest = (uint8_t *)dest; - const uint8_t *psrc = (const uint8_t *)src; - - if (src > dest) - { - for (size_t i = 0; i < n; i++) - { - pdest[i] = psrc[i]; - } - } - else if (src < dest) - { - for (size_t i = n; i > 0; i--) - { - pdest[i - 1] = psrc[i - 1]; - } - } - - return dest; -} - -int memcmp(const void *s1, const void *s2, size_t n) -{ - const uint8_t *p1 = (const uint8_t *)s1; - const uint8_t *p2 = (const uint8_t *)s2; - - for (size_t i = 0; i < n; i++) - { - if (p1[i] != p2[i]) - { - return p1[i] < p2[i] ? -1 : 1; - } - } - - return 0; -} - -static void hcf(void) -{ - asm("cli"); - for (;;) - { - asm("hlt"); - } -} - void emk_entry(void) { - if (LIMINE_BASE_REVISION_SUPPORTED == false) + if (serial_init(COM1) != 0) { + /* Just halt and say nothing */ hcf(); } - if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) + if (!LIMINE_BASE_REVISION_SUPPORTED) { + serial_write(COM1, (uint8_t *)"ERROR: Limine base revision is not supported\n", 45); hcf(); } - struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0]; - for (size_t i = 0; i < 100; i++) - { - volatile uint32_t *fb_ptr = framebuffer->address; - fb_ptr[i * (framebuffer->pitch / 4) + i] = 0xffffff; - } - - hcf(); + serial_write(COM1, (uint8_t *)"Hello, World!\n", 14); + hlt(); } \ No newline at end of file diff --git a/kernel/src/lib/string.c b/kernel/src/lib/string.c new file mode 100644 index 0000000..41fd8e2 --- /dev/null +++ b/kernel/src/lib/string.c @@ -0,0 +1,65 @@ +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) +{ + uint8_t *restrict pdest = (uint8_t *restrict)dest; + const uint8_t *restrict psrc = (const uint8_t *restrict)src; + + for (size_t i = 0; i < n; i++) + { + pdest[i] = psrc[i]; + } + + return dest; +} + +void *memset(void *s, int c, size_t n) +{ + uint8_t *p = (uint8_t *)s; + + for (size_t i = 0; i < n; i++) + { + p[i] = (uint8_t)c; + } + + return s; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + uint8_t *pdest = (uint8_t *)dest; + const uint8_t *psrc = (const uint8_t *)src; + + if (src > dest) + { + for (size_t i = 0; i < n; i++) + { + pdest[i] = psrc[i]; + } + } + else if (src < dest) + { + for (size_t i = n; i > 0; i--) + { + pdest[i - 1] = psrc[i - 1]; + } + } + + return dest; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const uint8_t *p1 = (const uint8_t *)s1; + const uint8_t *p2 = (const uint8_t *)s2; + + for (size_t i = 0; i < n; i++) + { + if (p1[i] != p2[i]) + { + return p1[i] < p2[i] ? -1 : 1; + } + } + + return 0; +} \ No newline at end of file diff --git a/kernel/src/lib/string.h b/kernel/src/lib/string.h new file mode 100644 index 0000000..fa44564 --- /dev/null +++ b/kernel/src/lib/string.h @@ -0,0 +1,12 @@ +#ifndef STRING_H +#define STRING_H + +#include +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n); +void *memset(void *s, int c, size_t n); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +#endif // STRING_H \ No newline at end of file