diff --git a/kernel/core/kmain.c b/kernel/core/kmain.c index 3152165..9d984e5 100644 --- a/kernel/core/kmain.c +++ b/kernel/core/kmain.c @@ -2,30 +2,157 @@ * * Called once from the stage-2 entry stub after the CPU is in 64-bit long * mode with a valid stack, zeroed BSS and identity-mapped memory. Runs at - * Ring 0 (CPL 0) interrupts disabled. Installs the exception IDT first - * so from then on a fault prints diagnostics instead of triple-faulting. - * It never returns; on return the caller halts the CPU. + * Ring 0 (CPL 0) with IF=0 on entry under the stage-2 GDT (CS 0x18); + * `-mno-red-zone` keeps IRQ pushes off the red zone so interrupt frames + * are safe. Installs the exception IDT first so from then on a fault + * prints diagnostics instead of triple-faulting. `sti` runs exactly once + * after every device is programmed; the main loop keeps IF=1 and sleeps + * in `hlt` (which wakes on each IRQ) — never `cli`-parks, or timer ticks + * and keystrokes would stop. It never returns; on return the caller halts. */ +#include + #include "console.h" #include "idt.h" +#include "keyboard.h" +#include "pic.h" +#include "pit.h" +#include "serial.h" /* A value that only comes out right if the CPU truly executes 64-bit * arithmetic: every nibble survives the shifts above 32 bits. */ #define PROOF_VALUE 0x0123456789ABCDEFULL +/* Print an unsigned decimal via console_putchar (no libc printf). */ +static void print_dec(uint64_t v) +{ + char buf[20]; + int n = 0; + + if (v == 0) { + console_putchar('0'); + return; + } + while (v != 0 && n < (int)sizeof(buf)) { + buf[n++] = (char)('0' + (v % 10U)); + v /= 10U; + } + while (n > 0) { + n--; + console_putchar(buf[n]); + } +} + +/* Mirror one echoed keystroke to COM1 when the UART looks present. */ +static void serial_echo(char c, int ok) +{ + char tmp[2]; + + if (ok == 0) { + return; + } + if (c == '\b') { + /* Erase on a serial terminal: back, blank, back. */ + serial_puts("\b \b"); + } else if (c == '\n') { + serial_puts("\r\n"); + } else { + tmp[0] = c; + tmp[1] = '\0'; + serial_puts(tmp); + } +} + void kmain(void) { + int serial_present; + uint64_t last_uptime; + console_clear(); console_puts("Hello World from Nadir Kernel !\n"); console_puts("Running C code in 64-bit long mode (Ring 0).\n"); + + /* WHY this order: pic_remap must precede idt_install_irqs so IRQ + * gates land on valid remapped vectors 32-47 (pre-remap they would + * overlap CPU exceptions 0-15 and a tick would look like a fault); + * devices (serial/PIT/keyboard) are programmed before sti so no IRQ + * fires into a half-wired handler; pic_remap masks everything and + * each device opts in via pic_unmask, so stray lines stay silent. */ idt_init(); console_puts("IDT installed (vectors 0-31).\n"); + + pic_remap(); + console_puts("PIC remapped (IRQ 0-15 -> vectors 32-47, masked).\n"); + + idt_install_irqs(); + console_puts("IRQ gates installed (vectors 32-47).\n"); + + serial_init(); + serial_present = serial_ok(); + console_puts("Serial COM1 38400 8N1 ready.\n"); + if (serial_present != 0) { + serial_puts("Hello World from Nadir Kernel !\n"); + serial_puts("Serial COM1 38400 8N1 ready.\n"); + } + + pit_init(); + console_puts("PIT 100Hz on IRQ0 ready.\n"); + if (serial_present != 0) { + serial_puts("PIT 100Hz on IRQ0 ready.\n"); + } + + keyboard_init(); + console_puts("Keyboard PS/2 IRQ1 ready.\n"); + if (serial_present != 0) { + serial_puts("Keyboard PS/2 IRQ1 ready.\n"); + } + + /* Explicit opt-in for the two live lines; idempotent even though + * keyboard_init already unmasked IRQ1. */ + pic_unmask(0); + pic_unmask(1); + + __asm__ volatile("sti"); + console_puts("Interrupts enabled (IRQs 0-1).\n"); + if (serial_present != 0) { + serial_puts("Interrupts enabled (IRQs 0-1).\n"); + } + console_puts("64-bit proof value: "); console_puthex64(PROOF_VALUE); - console_puts("\nHalting.\n"); + console_puts("\nHalting in hlt loop (timer + keyboard live).\n"); + /* Main loop: hlt sleeps until the next IRQ (IF=1 throughout), then + * one poll pass prints uptime ~1/s and drains echoed keys. */ + last_uptime = 0; for (;;) { + uint64_t now; + __asm__ volatile("hlt"); + now = pit_ticks(); + if ((now - last_uptime) >= 100U) { + uint64_t secs = now / 100U; + uint64_t tenths = (now / 10U) % 10U; + + last_uptime = now; + console_puts("uptime "); + print_dec(secs); + console_puts("."); + print_dec(tenths); + console_puts("s (ticks "); + console_puthex64(now); + console_puts(")\n"); + if (serial_present != 0) { + serial_puts("uptime (see VGA for value)\n"); + } + } + while (keyboard_has_key() != 0) { + char c = keyboard_dequeue(); + + /* console_putchar erases on '\b' and wraps on '\n'. */ + console_putchar(c); + serial_echo(c, serial_present); + } } }