Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pastaboy12345/NKLegacy/llms.txt
Use this file to discover all available pages before exploring further.
kmain() is the C entry point called by the Multiboot assembly stub in ntoskrnl/arch/i386/boot.asm. By the time it runs, the CPU is in 32-bit protected mode, a 16 KB kernel stack has been set up at stack_top, and the flat memory model is active. kmain then executes five sequential initialization phases before handing off to the shell loop — none of these phases allocates heap memory or calls any standard library function; every subsystem uses static storage.
The function signature is
void kmain(uint32_t magic, void *mboot_info). Both parameters are provided by the Multiboot ABI — magic should equal 0x2BADB002 and mboot_info points to the Multiboot information structure populated by GRUB. NKLegacy currently casts both to (void) and does not use them, but they must appear in the signature to match what the boot.asm stub pushes onto the stack.Full Source — kmain.c
Phase-by-Phase Walkthrough
Phase 1 — Early Hardware Init
The very first call is
serial_init(COM1), which programs the UART for COM1 at 115200 baud, 8-N-1. Because this happens before the GDT or IDT have been loaded, it gives a reliable out-of-band debug channel: even a fault during GDT loading would still produce readable output on the serial port. The immediately following serial_print confirms the port is live.vga_set_color(0x0F) writes the attribute byte (foreground = white, background = black) that subsequent vga_print calls will use. vga_clear() then fills all 2000 character cells of the 80×25 VGA text buffer at 0xB8000 with spaces using that attribute, producing a clean black screen.Phase 2 — CPU Tables
gdt_init() builds the Global Descriptor Table in a static array, fills the null descriptor, a flat ring-0 code segment (base 0, limit 4 GB, 32-bit), and a flat ring-0 data segment, then executes lgdt and reloads CS via a far jump and all data segment registers with the data selector.idt_init() fills all 256 IDT entries with ISR stubs (assembled in ntoskrnl/arch/i386/isr.asm), wires up the exception handlers, and executes lidt. Both tables are in static storage — no dynamic allocation is involved.These two steps must be completed before any interrupt can be safely fielded, which is why Phase 3 (which ultimately calls sti) comes strictly after.Phase 3 — Interrupt Hardware
pic_init() sends Initialization Command Words to both the master (port 0x20) and slave (port 0xA0) 8259A PICs to remap IRQ0–IRQ7 to interrupt vectors 0x20–0x27 and IRQ8–IRQ15 to 0x28–0x2F. This moves hardware interrupts above the 32 CPU-reserved exception vectors.pit_init() programs channel 0 of the 8253/8254 Programmable Interval Timer in mode 3 (square-wave) with a divisor that produces a 1000 Hz IRQ0 rate, giving a 1 ms tick resolution. The tick counter is later read by pit_get_ticks() for the boot banner.keyboard_init() unmasks the keyboard IRQ line in the PIC and installs the PS/2 keyboard ISR so that keypresses begin generating scancode interrupts.Finally, __asm__ volatile("sti") sets EFLAGS.IF, enabling CPU interrupt delivery. From this point on the PIT fires every millisecond and keystrokes are captured.Phase 4 — Boot Banner and NKFS Init
The banner sequence uses
vga_set_color() extensively to reproduce the NT-style colored status output. The header line combines the compile-time string macro NK_VERSION "0.1.0" with the C preprocessor’s __DATE__ and __TIME__ tokens to embed the exact build timestamp.Six [OK] status lines (light green 0x0A for the tag, gray 0x07 for the description) confirm each subsystem that was initialized in Phases 1–3.kprintf("NKLegacy kernel initialized. %u ticks elapsed.\n\n", pit_get_ticks()) prints the number of PIT ticks that have accumulated since pit_init() ran, demonstrating that the timer ISR has been firing live.nkfs_init() zeroes and initializes the NKFS in-RAM filesystem: it clears the 256-node node_pool and the 512 KB data_pool (both static arrays in .bss), sets the allocation counters to zero, and creates the root directory node named "C:".Phase 5 — Shell Loop
cmd_run() is the terminal call in kmain. It implements an infinite read-dispatch loop: it reads characters from the PS/2 keyboard driver, echoes them to the VGA console, and on each newline parses and dispatches the command. It never returns. If it did, the boot.asm entry stub would execute cli / hlt to halt the CPU indefinitely.