NKLegacy initializes two fundamental x86 protected mode data structures — the Global Descriptor Table (GDT) and the Interrupt Descriptor Table (IDT) — during the early kernel boot phase. The GDT tells the CPU how the address space is segmented and at what privilege level, while the IDT maps each of the 256 interrupt vectors to an assembly stub that transfers control into the kernel’s C interrupt dispatcher.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.
Both tables are loaded into the CPU via inline assembly instructions:
lgdt for the GDT and lidt for the IDT. NKLegacy performs these loads inside dedicated gdt_flush and idt_flush routines written in isr.asm, which are called at the end of gdt_init() and idt_init() respectively.GDT — Global Descriptor Table
Source:ntoskrnl/arch/i386/gdt.h / ntoskrnl/arch/i386/gdt.c
The GDT defines the memory segments the CPU may access in 32-bit protected mode. NKLegacy installs five entries: a mandatory null descriptor followed by separate code and data segments for ring 0 (kernel) and ring 3 (user).
Data Structures
__attribute__((packed)) so the compiler emits no padding bytes — the CPU interprets them at the exact binary layout the x86 ABI requires.
Segment Selectors
NKLegacy defines four non-null selectors. A selector value encodes the GDT index (bits 15–3) plus the RPL (requested privilege level) in bits 1–0.Kernel code segment — index 1 in the GDT. Execute/read, base 0, limit 4 GB, DPL 0 (ring 0). Loaded into
CS after the far jump in gdt_flush.Kernel data segment — index 2 in the GDT. Read/write, base 0, limit 4 GB, DPL 0 (ring 0). Loaded into
DS, ES, FS, GS, and SS immediately after the GDT is installed.User code segment — index 3 in the GDT. Execute/read, base 0, limit 4 GB, DPL 3 (ring 3). Reserved for future user-mode process support.
User data segment — index 4 in the GDT. Read/write, base 0, limit 4 GB, DPL 3 (ring 3). Reserved for future user-mode process support.
gdt_init
gdt_ptr with the table’s address and byte-length limit, then calls gdt_flush to execute lgdt and reload all segment registers.
IDT — Interrupt Descriptor Table
Source:ntoskrnl/arch/i386/idt.h / ntoskrnl/arch/i386/idt.c
The IDT maps all 256 x86 interrupt vectors to handler stubs. Vectors 0–31 are reserved for CPU exceptions, and vectors 32–47 are mapped to hardware IRQs after the 8259 PIC is remapped by pic_init.
Data Structures
registers_t field reference
| Field | Source | Description |
|---|---|---|
ds | ISR stub | Data segment at time of interrupt |
edi…eax | pusha | General-purpose registers saved by the stub |
int_no | ISR stub | Interrupt vector number (0–255) |
err_code | CPU / stub | CPU error code, or 0 if the exception has none |
eip, cs, eflags | CPU | Interrupted instruction pointer, code segment, flags |
esp, ss | CPU | Stack pointer and segment (saved only on privilege change) |
idt_init
0x08 (kernel code segment) and flags 0x8E (present, ring 0, 32-bit interrupt gate). Finally it calls idt_flush to execute lidt.
idt_register_handler
n. When an interrupt fires, the assembly stub saves CPU state into a registers_t and calls either isr_handler (exceptions) or irq_handler (hardware IRQs), both of which look up the registered callback by regs->int_no and invoke it.
Interrupt vector number (0–255). Use values 32–47 for hardware IRQ lines after PIC remapping.
A function pointer with signature
void handler(registers_t *). Receives a pointer to the full CPU state at the time of the interrupt.Usage Example — Registering a Custom IRQ Handler
The example below mirrors the real keyboard driver (keyboard.c) and shows the complete pattern for registering an IRQ handler.
Initialize the PIC
Call
pic_init() before any IRQ handler is registered so that the 8259 PIC is remapped to vectors 32–47.Register your handler
Call
idt_register_handler(vector, callback) with the vector number and your C function pointer.