summaryrefslogtreecommitdiff
path: root/na/lib/klog.cc
diff options
context:
space:
mode:
Diffstat (limited to 'na/lib/klog.cc')
-rw-r--r--na/lib/klog.cc574
1 files changed, 0 insertions, 574 deletions
diff --git a/na/lib/klog.cc b/na/lib/klog.cc
deleted file mode 100644
index 86c67f8..0000000
--- a/na/lib/klog.cc
+++ /dev/null
@@ -1,574 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * klog.cc — Kernel logging subsystem implementation
- *
- * Implements the ring buffer, spinlock-protected writes, log level
- * filtering, and synchronous console output.
- *
- * Phase 2 TODO: async flush via kernel thread (see docs/specs/klog.md)
- */
-
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/console.h>
-#include <lume/atomic.h>
-#include <kernel/sync/spinlock.h>
-#include <arch/cpu.h>
-
-/* Forward declaration — mpaland/printf */
-extern "C" int vsnprintf_(char* buffer, size_t count,
- const char* format, __builtin_va_list va);
-
-/* ========================================================================
- * Ring Buffer State
- * ======================================================================== */
-
-struct KlogEntryHdr {
- uint32 seq;
- uint32 len;
- uint64 ts;
- uint16 cpu;
- uint8 level;
- uint8 subsys;
- uint16 reserved;
-};
-
-static struct {
- char data[KLOG_BUF_SIZE];
- uint32 head; /* Next write position */
- uint32 tail; /* Next read position (for /proc/kmsg) */
- uint32 console_tail; /* Next position to flush to console */
- uint32 dropped;
- lume::atomic<uint32> seq;
- uint64 subsys_mask;
- Spinlock lock;
- KlogLevel console_level; /* Messages <= this level go to UART */
- bool initialized;
- bool panic_mode;
-} g_klog;
-
-/* ========================================================================
- * Level name table
- * ======================================================================== */
-
-static const char* klog_level_name(KlogLevel level)
-{
- static const char* names[] = {
- "EMERG", "ALERT", "CRIT", "ERR",
- "WARN", "NOTICE", "INFO", "DEBUG"
- };
- if (level <= KLOG_DEBUG) {
- return names[level];
- }
- return "?";
-}
-
-static const char* klog_subsys_name(KlogSubsys subsys)
-{
- static const char* names[] = {
- "GEN",
- "SCHED",
- "TRAP",
- "IRQ",
- "TIMER",
- "VMM",
- "PMM",
- "SLAB",
- "FS",
- "DRV",
- "TEST",
- };
- if (subsys < KLOG_SUBSYS_MAX) {
- return names[subsys];
- }
- return "?";
-}
-
-static const char* parse_kern_level(const char* fmt, KlogLevel* level)
-{
- if (!fmt || !level) {
- return fmt;
- }
-
- /* Linux-style "\001n" prefix (KERN_SOH) */
- if (fmt[0] == '\001' && fmt[1] >= '0' && fmt[1] <= '7') {
- *level = static_cast<KlogLevel>(fmt[1] - '0');
- return fmt + 2;
- }
-
- /* Alternate "<n>" prefix */
- if (fmt[0] == '<' && fmt[1] >= '0' && fmt[1] <= '7' && fmt[2] == '>') {
- *level = static_cast<KlogLevel>(fmt[1] - '0');
- return fmt + 3;
- }
-
- return fmt;
-}
-
-/* ========================================================================
- * Ring Buffer Operations
- * ======================================================================== */
-
-/* Write a byte to the ring buffer. Overwrites oldest data on overflow. */
-static inline uint32 min_u32(uint32 a, uint32 b)
-{
- return (a < b) ? a : b;
-}
-
-static void ring_write_bytes(const void* src, uint32 len)
-{
- const char* p = reinterpret_cast<const char*>(src);
- for (uint32 i = 0; i < len; ++i) {
- g_klog.data[(g_klog.head + i) & (KLOG_BUF_SIZE - 1)] = p[i];
- }
- g_klog.head += len;
-}
-
-static void ring_read_bytes(uint32 pos, void* dst, uint32 len)
-{
- char* out = reinterpret_cast<char*>(dst);
- for (uint32 i = 0; i < len; ++i) {
- out[i] = g_klog.data[(pos + i) & (KLOG_BUF_SIZE - 1)];
- }
-}
-
-static void ring_drop_oldest(uint32 needed)
-{
- uint32 min_tail = min_u32(g_klog.tail, g_klog.console_tail);
-
- while (KLOG_BUF_SIZE - (g_klog.head - min_tail) < needed) {
- KlogEntryHdr hdr;
- ring_read_bytes(min_tail, &hdr, sizeof(hdr));
-
- uint32 entry = sizeof(hdr) + hdr.len;
- if (entry == 0 || entry > KLOG_BUF_SIZE) {
- min_tail++;
- g_klog.dropped++;
- continue;
- }
- min_tail += entry;
- g_klog.dropped++;
- }
-
- if (g_klog.tail < min_tail) {
- g_klog.tail = min_tail;
- }
- if (g_klog.console_tail < min_tail) {
- g_klog.console_tail = min_tail;
- }
-}
-
-/* ========================================================================
- * Console Output (synchronous)
- * ======================================================================== */
-
-static void console_emit(const char* s, int len)
-{
- console_write_all(s, len);
-}
-
-static int klog_format_line(char* out, int out_size,
- const KlogEntryHdr& hdr,
- const char* msg, int msg_len)
-{
- const char* lvl = klog_level_name(static_cast<KlogLevel>(hdr.level));
- const char* subsys = klog_subsys_name(static_cast<KlogSubsys>(hdr.subsys));
-
- int prefix = ksnprintf(out, out_size,
- "[%s][cpu%u][%llu][%s] ",
- lvl, (uint32)hdr.cpu,
- (unsigned long long)hdr.ts, subsys);
- if (prefix < 0) {
- return 0;
- }
-
- int total = prefix;
- int avail = out_size - 1 - total;
- if (avail > 0) {
- int to_copy = (msg_len < avail) ? msg_len : avail;
- for (int i = 0; i < to_copy; ++i) {
- out[total + i] = msg[i];
- }
- total += to_copy;
- }
-
- if (total > 0 && out[total - 1] != '\n' && total < out_size - 1) {
- out[total++] = '\n';
- }
-
- out[total] = '\0';
- return total;
-}
-
-static int klog_read_internal(char* buf, int size, uint32* tail_ptr, bool consume)
-{
- if (size <= 0) {
- return 0;
- }
-
- int written = 0;
- uint32 pos = *tail_ptr;
-
- while (pos != g_klog.head) {
- KlogEntryHdr hdr;
- ring_read_bytes(pos, &hdr, sizeof(hdr));
- uint32 raw_len = hdr.len;
- if (raw_len > KLOG_BUF_SIZE) {
- break;
- }
- uint32 copy_len = raw_len;
- if (copy_len > KLOG_LINE_MAX) {
- copy_len = KLOG_LINE_MAX;
- }
-
- char msg[KLOG_LINE_MAX + 1];
- ring_read_bytes(pos + sizeof(hdr), msg, copy_len);
- msg[copy_len] = '\0';
-
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line), hdr, msg,
- static_cast<int>(copy_len));
-
- if (line_len <= 0 || written + line_len > size) {
- break;
- }
-
- for (int i = 0; i < line_len; ++i) {
- buf[written + i] = line[i];
- }
- written += line_len;
-
- pos += sizeof(hdr) + raw_len;
- }
-
- if (consume) {
- *tail_ptr = pos;
- }
-
- return written;
-}
-
-static void klog_vex(KlogLevel level, KlogSubsys subsys,
- const char* fmt, __builtin_va_list va);
-
-/* ========================================================================
- * Public API
- * ======================================================================== */
-
-void klog_init(void)
-{
- g_klog.head = 0;
- g_klog.tail = 0;
- g_klog.console_tail = 0;
- g_klog.dropped = 0;
- g_klog.seq.store(0, __ATOMIC_RELAXED);
- g_klog.subsys_mask = (1ULL << KLOG_SUBSYS_MAX) - 1;
- g_klog.console_level = static_cast<KlogLevel>(KLOG_DEFAULT_LEVEL);
- g_klog.initialized = true;
- g_klog.panic_mode = false;
- g_klog.lock.init("klog");
-}
-
-void klog(KlogLevel level, const char* fmt, ...)
-{
- __builtin_va_list va;
- __builtin_va_start(va, fmt);
- klog_vex(level, KLOG_SUBSYS_GENERIC, fmt, va);
- __builtin_va_end(va);
-}
-
-void klog_ex(KlogLevel level, KlogSubsys subsys, const char* fmt, ...)
-{
- __builtin_va_list va;
- __builtin_va_start(va, fmt);
- klog_vex(level, subsys, fmt, va);
- __builtin_va_end(va);
-}
-
-static void klog_vex(KlogLevel level, KlogSubsys subsys,
- const char* fmt, __builtin_va_list va)
-{
- if (subsys >= KLOG_SUBSYS_MAX) {
- subsys = KLOG_SUBSYS_GENERIC;
- }
-
- if (g_klog.initialized && ((g_klog.subsys_mask & (1ULL << subsys)) == 0)) {
- return;
- }
-
- char msg[KLOG_LINE_MAX + 1];
- int msg_len = vsnprintf_(msg, KLOG_LINE_MAX, fmt, va);
-
- if (msg_len < 0) {
- return;
- }
- if (msg_len > KLOG_LINE_MAX) {
- msg_len = KLOG_LINE_MAX;
- }
- msg[msg_len] = '\0';
-
- KlogEntryHdr hdr;
- hdr.seq = g_klog.seq.fetch_add(1, __ATOMIC_RELAXED);
- hdr.len = static_cast<uint32>(msg_len);
- hdr.ts = arch::cpu::read_time();
- hdr.cpu = static_cast<uint16>(arch::cpu::id());
- hdr.level = static_cast<uint8>(level);
- hdr.subsys = static_cast<uint8>(subsys);
- hdr.reserved = 0;
-
- bool do_console = (!g_klog.initialized || level <= g_klog.console_level);
-
- if (g_klog.initialized && !g_klog.panic_mode) {
- LockGuard guard(g_klog.lock);
- ring_drop_oldest(sizeof(hdr) + hdr.len);
- ring_write_bytes(&hdr, sizeof(hdr));
- ring_write_bytes(msg, hdr.len);
- if (do_console) {
- g_klog.console_tail = g_klog.head;
- /* Emit to UART while holding the lock to prevent
- * interleaved output from concurrent CPUs. */
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line),
- hdr, msg, hdr.len);
- if (line_len > 0) {
- console_emit(line, line_len);
- }
- }
- } else if (do_console) {
- /* Pre-init or panic mode: emit without lock */
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line),
- hdr, msg, hdr.len);
- if (line_len > 0) {
- console_emit(line, line_len);
- }
- }
-}
-
-int vprintk(const char* fmt, __builtin_va_list va)
-{
- KlogLevel level = KLOG_INFO;
- const char* real_fmt = parse_kern_level(fmt, &level);
-
- __builtin_va_list va_copy;
- __builtin_va_copy(va_copy, va);
-
- char tmp[KLOG_LINE_MAX + 1];
- int len = vsnprintf_(tmp, KLOG_LINE_MAX, real_fmt, va_copy);
- __builtin_va_end(va_copy);
-
- klog_vex(level, KLOG_SUBSYS_GENERIC, real_fmt, va);
-
- if (len < 0) {
- return 0;
- }
- return len;
-}
-
-int printk(const char* fmt, ...)
-{
- __builtin_va_list va;
- __builtin_va_start(va, fmt);
- int ret = vprintk(fmt, va);
- __builtin_va_end(va);
- return ret;
-}
-
-void klog_set_level(KlogLevel level)
-{
- g_klog.console_level = level;
-}
-
-void klog_set_subsys_mask(uint64 mask)
-{
- g_klog.subsys_mask = mask;
-}
-
-void klog_enable_subsys(KlogSubsys subsys)
-{
- if (subsys < KLOG_SUBSYS_MAX) {
- g_klog.subsys_mask |= (1ULL << subsys);
- }
-}
-
-void klog_disable_subsys(KlogSubsys subsys)
-{
- if (subsys < KLOG_SUBSYS_MAX) {
- g_klog.subsys_mask &= ~(1ULL << subsys);
- }
-}
-
-int klog_read(char* buf, int size)
-{
- if (!g_klog.initialized || size <= 0) {
- return 0;
- }
-
- LockGuard guard(g_klog.lock);
- return klog_read_internal(buf, size, &g_klog.tail, false);
-}
-
-int klog_read_consume(char* buf, int size)
-{
- if (!g_klog.initialized || size <= 0) {
- return 0;
- }
-
- LockGuard guard(g_klog.lock);
- return klog_read_internal(buf, size, &g_klog.tail, true);
-}
-
-void klog_flush(void)
-{
- if (!g_klog.initialized) {
- return;
- }
-
- LockGuard guard(g_klog.lock);
-
- uint32 pos = g_klog.console_tail;
- while (pos != g_klog.head) {
- KlogEntryHdr hdr;
- ring_read_bytes(pos, &hdr, sizeof(hdr));
- uint32 raw_len = hdr.len;
- if (raw_len > KLOG_BUF_SIZE) {
- break;
- }
- uint32 copy_len = raw_len;
- if (copy_len > KLOG_LINE_MAX) {
- copy_len = KLOG_LINE_MAX;
- }
-
- char msg[KLOG_LINE_MAX + 1];
- ring_read_bytes(pos + sizeof(hdr), msg, copy_len);
- msg[copy_len] = '\0';
-
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line), hdr, msg,
- static_cast<int>(copy_len));
- if (line_len > 0) {
- console_emit(line, line_len);
- }
-
- pos += sizeof(hdr) + raw_len;
- }
-
- g_klog.console_tail = pos;
-}
-
-bool klog_ratelimit(KlogRateLimit* rl, uint64 now)
-{
- if (!rl) {
- return true;
- }
-
- if (rl->interval == 0 || rl->burst == 0) {
- return true;
- }
-
- if (now - rl->last_ts >= rl->interval) {
- rl->last_ts = now;
- rl->printed = 0;
- rl->suppressed = 0;
- }
-
- if (rl->printed < rl->burst) {
- rl->printed++;
- return true;
- }
-
- rl->suppressed++;
- return false;
-}
-
-void klog_stats(KlogStats* out)
-{
- if (!out) {
- return;
- }
-
- out->head = g_klog.head;
- out->tail = g_klog.tail;
- out->console_tail = g_klog.console_tail;
- out->dropped = g_klog.dropped;
- out->seq = g_klog.seq.load(__ATOMIC_RELAXED);
-}
-
-/* ========================================================================
- * Kernel Panic — Fatal Error Handler
- *
- * Dumps all critical S-mode CSRs and the stack pointer to the console,
- * then halts the CPU. Uses kprintf (NOT klog) to avoid deadlocking
- * if the panic was triggered while holding the klog spinlock.
- * ======================================================================== */
-
-extern "C" [[noreturn]] void kernel_panic(const char* msg, const char* detail) {
- /* Disable interrupts immediately */
- arch::cpu::intr_off();
-
- g_klog.panic_mode = true;
-
- /* ---- Banner ---- */
- kprintf("\n\n");
- kprintf("========================================\n");
- kprintf(" *** KERNEL PANIC ***\n");
- kprintf("========================================\n");
-
- if (msg != nullptr) {
- kprintf(" Reason : %s", msg);
- if (detail != nullptr) {
- kprintf(": %s", detail);
- }
- kprintf("\n");
- }
-
- /* ---- Read S-mode CSRs ---- */
- uint64 sepc, scause, stval, sstatus, satp, stvec, sscratch, sp_val;
-
- __asm__ volatile("csrr %0, sepc" : "=r"(sepc));
- __asm__ volatile("csrr %0, scause" : "=r"(scause));
- __asm__ volatile("csrr %0, stval" : "=r"(stval));
- __asm__ volatile("csrr %0, sstatus" : "=r"(sstatus));
- __asm__ volatile("csrr %0, satp" : "=r"(satp));
- __asm__ volatile("csrr %0, stvec" : "=r"(stvec));
- __asm__ volatile("csrr %0, sscratch" : "=r"(sscratch));
- __asm__ volatile("mv %0, sp" : "=r"(sp_val));
-
- kprintf("\n--- S-mode CSR Dump ---\n");
- kprintf(" sepc = 0x%016llx\n", sepc);
- kprintf(" scause = 0x%016llx", scause);
-
- /* Decode scause for convenience */
- bool is_interrupt = (scause >> 63) != 0;
- uint64 cause_code = scause & 0x7FFFFFFFFFFFFFFFULL;
- if (is_interrupt) {
- kprintf(" (interrupt, code=%llu)\n", cause_code);
- } else {
- kprintf(" (exception, code=%llu)\n", cause_code);
- }
-
- kprintf(" stval = 0x%016llx\n", stval);
- kprintf(" sstatus = 0x%016llx", sstatus);
- kprintf(" [SIE=%llu SPP=%llu SPIE=%llu]\n",
- (sstatus >> 1) & 1, /* SIE */
- (sstatus >> 8) & 1, /* SPP */
- (sstatus >> 5) & 1); /* SPIE */
- kprintf(" satp = 0x%016llx", satp);
- uint64 satp_mode = satp >> 60;
- kprintf(" [mode=%llu]\n", satp_mode);
- kprintf(" stvec = 0x%016llx\n", stvec);
- kprintf(" sscratch = 0x%016llx\n", sscratch);
- kprintf(" sp = 0x%016llx\n", sp_val);
-
- kprintf(" cpu_id = %llu\n", arch::cpu::id());
- kprintf(" time = %llu\n", arch::cpu::read_time());
-
- kprintf("========================================\n\n");
-
- /* Halt — spin forever with WFI to save power */
- while (true) {
- arch::cpu::halt_until_interrupt();
- }
-}