diff --git a/include/klog.h b/include/klog.h new file mode 100644 index 0000000..d2ae44c --- /dev/null +++ b/include/klog.h @@ -0,0 +1,48 @@ +/* Nadir boot logger: timestamped `[ 0.000000] sub: msg` lines. + * + * Runs in 64-bit long mode at Ring 0 (CPL 0) on the VGA console via + * console_putchar, which is polling-only and IRQ-safe by construction. + * + * Timestamp source: pit_ticks() / PIT_HZ (100Hz, 10ms per tick), integer + * math only. At 100Hz one + * tick is 10ms, so the 6-digit fraction only changes in steps of 10000 + * (e.g. 0.010000); the last 4 digits are always zero by construction. + * Early boot note: IF=0 until kmain runs `sti`, so the PIT IRQ cannot fire + * and ticks stay frozen at 0. + * + * IF/IRQ safety: mainline only, never IRQ context. The PIT IRQ handler + * (pit_on_tick) must never call this. It only bumps the tick counter. + * Single-core kernel, so no locking is needed. + * + * Preconditions: console is usable (identity-mapped VGA); pit_ticks() is + * linked in (BSS-zeroed counter reads 0 before pit_init). + * Failure modes: none + */ + +#ifndef NADIR_KLOG_H +#define NADIR_KLOG_H + +#include + +/* Print one full line: `[ 0.000000] subsystem: msg` + '\n'. */ +void klog(const char *subsystem, const char *msg); + +/* Piecewise line builder for values (PMM stats, uptime): prints the + * `[ 0.000000] subsystem: ` prefix with no newline; append with + * klog_str/dec/hex64, then close with klog_end() ('\n'). + * Preconditions: calls nest as begin -> (str|dec|hex64)* -> end. */ +void klog_begin(const char *subsystem); + +/* Append a NUL-terminated string to the open line. */ +void klog_str(const char *s); + +/* Append an unsigned decimal value (no libc printf). */ +void klog_dec(uint64_t v); + +/* Append a 64-bit value as 0x-prefixed, zero-padded hexadecimal. */ +void klog_hex64(uint64_t v); + +/* Terminate the open line with '\n'. */ +void klog_end(void); + +#endif /* NADIR_KLOG_H */ diff --git a/kernel/core/klog.c b/kernel/core/klog.c new file mode 100644 index 0000000..29953cb --- /dev/null +++ b/kernel/core/klog.c @@ -0,0 +1,117 @@ +/* Nadir boot logger (see klog.h). + * + * The only backends are console_putchar (VGA polling) + * and pit_ticks (monotonic u64) — no hardware addresses, no port I/O, no + * serial dependency live here. Serial mirroring stays in kmain, which + * prints the same text without the timestamp prefix (kept simple). + * + * Timestamp math: secs = ticks / PIT_HZ, frac = (ticks % PIT_HZ) * 10000 + * (1000000us/s divided by 100Hz). The field is 12 wide, space-padded on + * the left (`[ 0.000000]`); past 99999s it overflows the + * pad and just prints long. + */ + +#include + +#include "console.h" +#include "klog.h" +#include "pit.h" + +/* Width of the `secs.frac` field between `[` and `]`. */ +#define KLOG_TIME_WIDTH 12U + +/* Print the `[ 0.000000] subsystem: ` prefix. Reads pit_ticks() live + * so piecewise lines and one-shot lines share one code path. */ +static void klog_prefix(const char *subsystem) +{ + uint64_t ticks = pit_ticks(); + uint64_t secs = ticks / (uint64_t)PIT_HZ; + uint64_t frac = (ticks % (uint64_t)PIT_HZ) * 10000ULL; + char num[22]; + int n = 0; + uint64_t v; + uint64_t div; + int digits; + uint64_t len; + + /* Decimal seconds into num (reversed then flipped below). */ + v = secs; + if (v == 0U) { + num[n++] = '0'; + } else { + while (v != 0U && n < 20) { + num[n++] = (char)('0' + (v % 10U)); + v /= 10U; + } + } + /* Field length is digits + '.' + 6 frac digits; pad left with spaces + * to KLOG_TIME_WIDTH so short uptimes align */ + digits = n; + len = (uint64_t)digits + 7U; + console_putchar('['); + while (len < (uint64_t)KLOG_TIME_WIDTH) { + console_putchar(' '); + len++; + } + while (n > 0) { + n--; + console_putchar(num[n]); + } + console_putchar('.'); + /* Exactly 6 fraction digits, zero-padded (10ms ticks leave the last + * 4 as zeros — resolution limit, not a bug). */ + div = 100000ULL; + while (div > 0U) { + console_putchar((char)('0' + ((frac / div) % 10U))); + div /= 10U; + } + console_puts("] "); + console_puts(subsystem); + console_puts(": "); +} + +void klog(const char *subsystem, const char *msg) +{ + klog_begin(subsystem); + klog_str(msg); + klog_end(); +} + +void klog_begin(const char *subsystem) +{ + klog_prefix(subsystem); +} + +void klog_str(const char *s) +{ + console_puts(s); +} + +void klog_dec(uint64_t v) +{ + char buf[20]; + int n = 0; + + if (v == 0U) { + console_putchar('0'); + return; + } + while (v != 0U && n < (int)sizeof(buf)) { + buf[n++] = (char)('0' + (v % 10U)); + v /= 10U; + } + while (n > 0) { + n--; + console_putchar(buf[n]); + } +} + +void klog_hex64(uint64_t v) +{ + console_puthex64(v); +} + +void klog_end(void) +{ + console_putchar('\n'); +}