summaryrefslogtreecommitdiff
path: root/kernel/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers')
-rw-r--r--kernel/drivers/char/pl011.cpp83
-rw-r--r--kernel/drivers/char/uart_16550.cpp85
-rw-r--r--kernel/drivers/console/framebuffer.cpp328
-rw-r--r--kernel/drivers/driver_registry.cpp55
4 files changed, 551 insertions, 0 deletions
diff --git a/kernel/drivers/char/pl011.cpp b/kernel/drivers/char/pl011.cpp
new file mode 100644
index 0000000..7615370
--- /dev/null
+++ b/kernel/drivers/char/pl011.cpp
@@ -0,0 +1,83 @@
+// ============================================================================
+// drivers/char/pl011.cpp — PL011 UART console driver + device (aarch64)
+//
+// Stub implementation that compiles but does no real I/O yet.
+// TODO: Program IBRD, FBRD, LCR_H, CR registers for actual UART output.
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/early_driver.h>
+#include <kernel/early_device.h>
+#include <kernel/platform_bus.h>
+
+// ── PL011Device ───────────────────────────────────────────────────────────
+
+class PL011Device : public Device, public EarlyDevice<PL011Device> {
+public:
+ explicit PL011Device(uintptr_t base_addr) : base_(base_addr) {}
+
+ const char* name() const override { return "uart0"; }
+ uintptr_t base() const { return base_; }
+
+private:
+ uintptr_t base_;
+};
+
+// ── PL011Console driver ──────────────────────────────────────────────────
+
+class PL011Console : public ConsoleDriver,
+ public EarlyDriver<PL011Console> {
+public:
+ PL011Console() = default;
+
+ const char* name() const override { return "pl011-uart"; }
+
+ bool init() override {
+ auto* dev = static_cast<PL011Device*>(device());
+ base_ = dev->base();
+
+ // TODO: Configure UART registers at base_:
+ // UARTIBRD (offset 0x24) — integer baud rate divisor
+ // UARTFBRD (offset 0x28) — fractional baud rate divisor
+ // UARTLCR_H (offset 0x2C) — line control (8N1, FIFO enable)
+ // UARTCR (offset 0x30) — control register (TX enable, UART enable)
+ initialized_ = true;
+ return true;
+ }
+
+ void shutdown() override {
+ initialized_ = false;
+ }
+
+ void putchar(char c) override {
+ if (!initialized_) return;
+ // TODO: while (*(volatile uint32_t*)(base_ + 0x18) & (1 << 5)) {}
+ // *(volatile uint32_t*)(base_ + 0x00) = c;
+ (void)c;
+ }
+
+ void clear() override {
+ // UART has no screen to clear
+ }
+
+private:
+ uintptr_t base_ = 0;
+ bool initialized_ = false;
+};
+
+// ── Driver init function ──────────────────────────────────────────────────
+
+namespace console {
+
+void init_pl011(uintptr_t base_addr) {
+ auto* dev = PL011Device::create(base_addr);
+ platform::bus().add_device(dev);
+
+ auto* drv = PL011Console::create();
+ drv->bind(dev);
+ drv->init();
+
+ console::register_driver(drv);
+}
+
+} // namespace console
diff --git a/kernel/drivers/char/uart_16550.cpp b/kernel/drivers/char/uart_16550.cpp
new file mode 100644
index 0000000..a85b5e7
--- /dev/null
+++ b/kernel/drivers/char/uart_16550.cpp
@@ -0,0 +1,85 @@
+// ============================================================================
+// drivers/char/uart_16550.cpp — 16550 UART console driver + device (x86_64)
+//
+// Stub implementation that compiles but does no real I/O yet.
+// TODO: Program divisor latch, 8N1, FIFO, implement actual port I/O.
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/early_driver.h>
+#include <kernel/early_device.h>
+#include <kernel/platform_bus.h>
+
+// ── UART16550Device ───────────────────────────────────────────────────────
+
+class UART16550Device : public Device, public EarlyDevice<UART16550Device> {
+public:
+ explicit UART16550Device(uint16_t port) : port_(port) {}
+
+ const char* name() const override { return "uart0"; }
+ uint16_t port() const { return port_; }
+
+private:
+ uint16_t port_;
+};
+
+// ── UART16550Console driver ───────────────────────────────────────────────
+
+class UART16550Console : public ConsoleDriver,
+ public EarlyDriver<UART16550Console> {
+public:
+ UART16550Console() = default;
+
+ const char* name() const override { return "uart-16550"; }
+
+ bool init() override {
+ auto* dev = static_cast<UART16550Device*>(device());
+ port_ = dev->port();
+
+ // TODO: outb(port_ + 1, 0x00) — disable interrupts
+ // TODO: outb(port_ + 3, 0x80) — enable DLAB
+ // TODO: outb(port_ + 0, 0x03) — set divisor (lo) 38400 baud
+ // TODO: outb(port_ + 1, 0x00) — set divisor (hi)
+ // TODO: outb(port_ + 3, 0x03) — 8 bits, no parity, one stop
+ // TODO: outb(port_ + 2, 0xC7) — enable FIFO, clear, 14-byte threshold
+ // TODO: outb(port_ + 4, 0x0B) — IRQs enabled, RTS/DSR set
+ initialized_ = true;
+ return true;
+ }
+
+ void shutdown() override {
+ initialized_ = false;
+ }
+
+ void putchar(char c) override {
+ if (!initialized_) return;
+ // TODO: while (!(inb(port_ + 5) & 0x20)) {} — wait for THR empty
+ // TODO: outb(port_, c)
+ (void)c;
+ }
+
+ void clear() override {
+ // UART has no screen to clear
+ }
+
+private:
+ uint16_t port_ = 0;
+ bool initialized_ = false;
+};
+
+// ── Driver init function ──────────────────────────────────────────────────
+
+namespace console {
+
+void init_uart_16550(uint16_t port) {
+ auto* dev = UART16550Device::create(port);
+ platform::bus().add_device(dev);
+
+ auto* drv = UART16550Console::create();
+ drv->bind(dev);
+ drv->init();
+
+ console::register_driver(drv);
+}
+
+} // namespace console
diff --git a/kernel/drivers/console/framebuffer.cpp b/kernel/drivers/console/framebuffer.cpp
new file mode 100644
index 0000000..e7f0577
--- /dev/null
+++ b/kernel/drivers/console/framebuffer.cpp
@@ -0,0 +1,328 @@
+// ============================================================================
+// drivers/console/framebuffer.cpp — Framebuffer console driver + device
+//
+// FramebufferDevice describes the GOP framebuffer hardware (base address,
+// resolution, pixel format). FramebufferConsole is the driver that renders
+// text to it using a built-in 8x16 VGA bitmap font.
+//
+// Both are early-allocated (static storage, no heap).
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/early_driver.h>
+#include <kernel/early_device.h>
+#include <kernel/platform_bus.h>
+#include <boot/boot_info.h>
+
+// ── Minimal 8x16 bitmap font (ASCII 32-126) ──────────────────────────────
+
+static constexpr int FONT_FIRST = 32;
+static constexpr int FONT_LAST = 126;
+
+// clang-format off
+static const uint8_t g_font[][16] = {
+ // 32: space
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 33: !
+ {0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 34: "
+ {0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 35: #
+ {0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00},
+ // 36: $
+ {0x00,0x10,0x10,0x7C,0xD6,0xD0,0x7C,0x16,0xD6,0x7C,0x10,0x10,0x00,0x00,0x00,0x00},
+ // 37: %
+ {0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00},
+ // 38: &
+ {0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ // 39: '
+ {0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 40: (
+ {0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
+ // 41: )
+ {0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00},
+ // 42: *
+ {0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 43: +
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 44: ,
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00},
+ // 45: -
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 46: .
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 47: /
+ {0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00},
+ // 48-57: 0-9
+ {0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
+ // 58: :
+ {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
+ // 59: ;
+ {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00},
+ // 60: <
+ {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00},
+ // 61: =
+ {0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 62: >
+ {0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00},
+ // 63: ?
+ {0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 64: @
+ {0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0xC0,0x7C,0x00,0x00,0x00,0x00},
+ // 65-90: A-Z
+ {0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00},
+ {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00},
+ // 91: [
+ {0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00},
+ // 92: backslash
+ {0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00,0x00,0x00},
+ // 93: ]
+ {0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00},
+ // 94: ^
+ {0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 95: _
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},
+ // 96: `
+ {0x00,0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 97-122: a-z
+ {0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xE0,0x60,0x60,0x78,0x6C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x1C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x38,0x6C,0x64,0x60,0xF0,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,0x00},
+ {0x00,0x00,0xE0,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00},
+ {0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0xD6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xDC,0x76,0x66,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0x60,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x10,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00},
+ // 123: {
+ {0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00},
+ // 124: |
+ {0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 125: }
+ {0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00},
+ // 126: ~
+ {0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+};
+// clang-format on
+
+// ── FramebufferDevice ─────────────────────────────────────────────────────
+
+class FramebufferDevice : public Device, public EarlyDevice<FramebufferDevice> {
+public:
+ explicit FramebufferDevice(const FramebufferInfo& fb)
+ : base_(fb.base), width_(fb.width), height_(fb.height)
+ , pitch_(fb.pitch), format_(fb.format)
+ {}
+
+ const char* name() const override { return "framebuffer0"; }
+
+ uint64_t base() const { return base_; }
+ uint32_t width() const { return width_; }
+ uint32_t height() const { return height_; }
+ uint32_t pitch() const { return pitch_; }
+ PixelFormat format() const { return format_; }
+
+private:
+ uint64_t base_;
+ uint32_t width_;
+ uint32_t height_;
+ uint32_t pitch_;
+ PixelFormat format_;
+};
+
+// ── FramebufferConsole driver ─────────────────────────────────────────────
+
+class FramebufferConsole : public ConsoleDriver,
+ public EarlyDriver<FramebufferConsole> {
+public:
+ FramebufferConsole() = default;
+
+ const char* name() const override { return "framebuffer-console"; }
+
+ bool init() override {
+ auto* dev = static_cast<FramebufferDevice*>(device());
+ fb_ = reinterpret_cast<uint32_t*>(dev->base());
+ width_ = dev->width();
+ height_ = dev->height();
+ pitch_ = dev->pitch();
+ is_bgr_ = (dev->format() == PixelFormat::BGR);
+ clear();
+ return true;
+ }
+
+ void shutdown() override {}
+
+ void putchar(char c) override {
+ uint32_t max_cols = width_ / CHAR_W;
+ uint32_t max_rows = height_ / CHAR_H;
+
+ if (c == '\n') {
+ col_ = 0;
+ row_++;
+ } else if (c == '\r') {
+ col_ = 0;
+ } else if (c == '\t') {
+ col_ = (col_ + 4) & ~3u;
+ } else {
+ render_char(col_, row_, c);
+ col_++;
+ }
+
+ if (col_ >= max_cols) {
+ col_ = 0;
+ row_++;
+ }
+
+ if (row_ >= max_rows) {
+ scroll();
+ row_ = max_rows - 1;
+ }
+ }
+
+ void clear() override {
+ for (uint32_t y = 0; y < height_; y++) {
+ for (uint32_t x = 0; x < width_; x++) {
+ put_pixel(x, y, BG_COLOR);
+ }
+ }
+ col_ = 0;
+ row_ = 0;
+ }
+
+private:
+ uint32_t* fb_ = nullptr;
+ uint32_t width_ = 0;
+ uint32_t height_ = 0;
+ uint32_t pitch_ = 0;
+ bool is_bgr_ = false;
+ uint32_t col_ = 0;
+ uint32_t row_ = 0;
+
+ static constexpr uint32_t CHAR_W = 8;
+ static constexpr uint32_t CHAR_H = 16;
+ static constexpr uint32_t FG_COLOR = CONFIG_FBCON_FG_COLOR;
+ static constexpr uint32_t BG_COLOR = CONFIG_FBCON_BG_COLOR;
+
+ void put_pixel(uint32_t x, uint32_t y, uint32_t rgb) {
+ if (x >= width_ || y >= height_) return;
+
+ uint32_t color;
+ if (is_bgr_) {
+ color = ((rgb & 0xFF) << 16) | (rgb & 0xFF00) | ((rgb >> 16) & 0xFF);
+ } else {
+ color = rgb;
+ }
+
+ auto* row = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<uint8_t*>(fb_) + y * pitch_);
+ row[x] = color;
+ }
+
+ void scroll() {
+ uint32_t row_bytes = pitch_;
+ uint32_t shift = CHAR_H * row_bytes;
+ uint32_t total = height_ * row_bytes;
+
+ auto* fb = reinterpret_cast<uint8_t*>(fb_);
+
+ for (uint32_t i = 0; i < total - shift; i++) {
+ fb[i] = fb[i + shift];
+ }
+ for (uint32_t i = total - shift; i < total; i++) {
+ fb[i] = 0;
+ }
+ uint32_t start_y = height_ - CHAR_H;
+ for (uint32_t y = start_y; y < height_; y++) {
+ for (uint32_t x = 0; x < width_; x++) {
+ put_pixel(x, y, BG_COLOR);
+ }
+ }
+ }
+
+ void render_char(uint32_t col, uint32_t row, char c) {
+ int idx = static_cast<int>(c) - FONT_FIRST;
+ if (idx < 0 || idx > (FONT_LAST - FONT_FIRST)) idx = 0;
+
+ uint32_t px = col * CHAR_W;
+ uint32_t py = row * CHAR_H;
+
+ for (uint32_t y = 0; y < CHAR_H; y++) {
+ uint8_t bits = g_font[idx][y];
+ for (uint32_t x = 0; x < CHAR_W; x++) {
+ uint32_t color = (bits & (0x80 >> x)) ? FG_COLOR : BG_COLOR;
+ put_pixel(px + x, py + y, color);
+ }
+ }
+ }
+};
+
+// ── Driver init function ──────────────────────────────────────────────────
+
+namespace console {
+
+void init_framebuffer(const FramebufferInfo& fb) {
+ // Create device on the platform bus
+ auto* dev = FramebufferDevice::create(fb);
+ platform::bus().add_device(dev);
+
+ // Create driver and bind to device
+ auto* drv = FramebufferConsole::create();
+ drv->bind(dev);
+ drv->init();
+
+ // Register as active console
+ console::register_driver(drv);
+ console::set_active(drv);
+}
+
+} // namespace console
diff --git a/kernel/drivers/driver_registry.cpp b/kernel/drivers/driver_registry.cpp
new file mode 100644
index 0000000..43b2b5a
--- /dev/null
+++ b/kernel/drivers/driver_registry.cpp
@@ -0,0 +1,55 @@
+// ============================================================================
+// drivers/driver_registry.cpp — Driver/device registry globals
+//
+// Provides storage for per-category driver lists, the active console
+// pointer, and the platform bus singleton. Always compiled.
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/platform_bus.h>
+
+// ── ConsoleDriver default write implementation ────────────────────────────
+
+void ConsoleDriver::write(const char* s, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ putchar(s[i]);
+ }
+}
+
+// ── Console registry ──────────────────────────────────────────────────────
+
+static DriverList<ConsoleDriver> g_console_drivers;
+static ConsoleDriver* g_active_console = nullptr;
+
+namespace console {
+
+void register_driver(ConsoleDriver* driver) {
+ g_console_drivers.add(driver);
+}
+
+void set_active(ConsoleDriver* driver) {
+ g_active_console = driver;
+}
+
+ConsoleDriver* active() {
+ return g_active_console;
+}
+
+DriverList<ConsoleDriver>& drivers() {
+ return g_console_drivers;
+}
+
+} // namespace console
+
+// ── Platform bus (lazy singleton) ─────────────────────────────────────────
+
+namespace platform {
+
+Bus& bus() {
+ if (!PlatformBus::instance()) {
+ PlatformBus::create();
+ }
+ return *PlatformBus::instance();
+}
+
+} // namespace platform