core: add timestamped klog logger on VGA console

This commit is contained in:
Pierre-Emmanuel Gob. 2026-10-05 18:12:19 +02:00
parent d14465458d
commit b0e7856e3f
2 changed files with 165 additions and 0 deletions

48
include/klog.h Normal file
View file

@ -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 <stdint.h>
/* 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 */

117
kernel/core/klog.c Normal file
View file

@ -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 <stdint.h>
#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');
}