summaryrefslogtreecommitdiff
path: root/na/drivers/uart.cc
diff options
context:
space:
mode:
Diffstat (limited to 'na/drivers/uart.cc')
-rw-r--r--na/drivers/uart.cc113
1 files changed, 111 insertions, 2 deletions
diff --git a/na/drivers/uart.cc b/na/drivers/uart.cc
index 264ec6a..467b496 100644
--- a/na/drivers/uart.cc
+++ b/na/drivers/uart.cc
@@ -2,9 +2,18 @@
/* Copyright (C) 2026 Richard Qin */
/*
- * uart.cc — 16550a UART driver (early console)
+ * uart.cc — 16550a UART driver + console registry
*
- * Implements the early UART output mapping required by panic and kernel_main.
+ * Two roles:
+ * 1. Early console: early_putc() for pre-init output (panic, kprintf)
+ * 2. Runtime console: struct console registered with register_console().
+ * klog/printk route through console_write_all() → uart_console_write().
+ *
+ * The console registry is a simple linked list of struct console.
+ * console_write_all() iterates the list and calls each console's write().
+ * If no console is registered, it falls back to early_putc().
+ *
+ * Reference: Linux kernel/printk/printk.c, drivers/tty/serial/8250/
*/
#include <lume/console.h>
@@ -13,11 +22,19 @@
#include <lume/addr.h>
#include <arch/config.h>
+/* ================================================================== */
+/* UART state */
+/* ================================================================== */
+
static uint64 g_uart_pa = 0;
static uint64 g_uart_size = 0;
static bool g_runtime_mapping_ready = false;
static bool g_mmu_enabled = false;
+/* ================================================================== */
+/* Early console (Phase 1) */
+/* ================================================================== */
+
extern "C" void console_set_mmu_enabled() {
g_mmu_enabled = true;
}
@@ -70,3 +87,95 @@ void console_init() {
void console_use_runtime_mapping() {
g_runtime_mapping_ready = true;
}
+
+/* ================================================================== */
+/* Console registry (Phase 2) — linked list of struct console */
+/* ================================================================== */
+
+static list_node g_console_list;
+static bool g_console_list_init = false;
+
+void register_console(console* con)
+{
+ if (!con) return;
+
+ if (!g_console_list_init) {
+ g_console_list.init();
+ g_console_list_init = true;
+ }
+
+ con->flags |= CON_ENABLED;
+ con->list.init();
+
+ /* Insert at tail */
+ list_add_tail(&con->list, &g_console_list);
+}
+
+void unregister_console(console* con)
+{
+ if (!con) return;
+
+ list_del_init(&con->list);
+ con->flags &= ~CON_ENABLED;
+}
+
+bool console_drivers_registered()
+{
+ if (!g_console_list_init) return false;
+ return !list_empty(&g_console_list);
+}
+
+void console_write_all(const char* s, int count)
+{
+ if (g_console_list_init && !list_empty(&g_console_list)) {
+ list_node* pos;
+ list_for_each(pos, &g_console_list) {
+ auto* con = container_of(pos, console, list);
+ if ((con->flags & CON_ENABLED) && con->write) {
+ con->write(con, s, static_cast<unsigned int>(count));
+ }
+ }
+ } else {
+ /* Fallback: direct early_putc (pre-registration) */
+ for (int i = 0; i < count; i++) {
+ early_putc(s[i]);
+ }
+ }
+}
+
+/* ================================================================== */
+/* UART console driver */
+/* ================================================================== */
+
+static void uart_console_write(console* /*con*/, const char* s, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ if (s[i] == '\n') {
+ early_putc('\r');
+ }
+ early_putc(s[i]);
+ }
+}
+
+/*
+ * uart_console must be initialized at RUNTIME, not with static
+ * aggregate initializers. Reason: KASLR self-relocation processes
+ * .rela.dyn entries for simple pointer variables, but may not cover
+ * function pointer fields inside static aggregate initializers.
+ * A static `{ .write = uart_console_write }` would store the
+ * pre-relocation address, causing a crash when called.
+ */
+static console uart_console;
+
+void uart_register_console()
+{
+ uart_console.name = "ttyS";
+ uart_console.write = uart_console_write;
+ uart_console.setup = nullptr;
+ uart_console.flags = CON_ENABLED | CON_BOOT;
+ uart_console.index = 0;
+ uart_console.data = nullptr;
+ uart_console.list.init();
+ register_console(&uart_console);
+}
+