# NT-Lite -- an x86-64 kernel Boots. Runs processes. Mimics Windows to some extent. ## what is it? NT-Lite is a 64-bit operating system kernel written from scratch. It includes a UEFI bootloader, physical and virtual memory managers, a preemptive scheduler, a HAL, a VFS, a PE loader, and partial Win32 and POSIX layers. It boots into a shell. The codebase is roughly 20k lines of C and assembly. ## features - UEFI bootloader - physical memory manager - virtual memory manager with kernel pool - HAL for screen, keyboard, mouse, timer, disk, PCI, and APIC - preemptive scheduler with priority levels - processes and threads - VFS with IRPs - drive-letter paths such as `0:\apps\shell.exe` - INI-based boot and driver configuration - PE import resolution and dynamic driver loading - partial Win32 personality - partial POSIX personality ## architecture notes ### memory layout NT-Lite uses a single virtual address space per booted system rather than a fully separate kernel address space and user address space. The distinction between kernel and user mode comes from page permissions and CPU privilege checks, not from having a different page table for the kernel. The kernel is mapped into the higher half of virtual memory and user processes only receive the regions they are explicitly granted. Kernel pages are marked supervisor-only, so user-mode code cannot read or write them directly. User pages are mapped with user permissions and are isolated from one another by the kernel’s allocator, VMM, and process creation path. ### boot chain The bootloader reads configuration, loads the core images, maps the higher-half layout, and hands control to `KernelMain`. After that, the kernel takes over interrupt setup, paging, scheduler bring-up, runtime driver loading, and user process launch. ### drivers Drivers are PE32+ `.sys` files with a conventional `DriverEntry` export that accepts a pointer to `HAL_API`. Drivers are split into two groups: - essential boot drivers, which are loaded before the kernel starts doing real work - runtime drivers, which are loaded later from disk once storage and the VFS are available The driver model is intentionally small. Disk drivers expose a `DISK_DRIVER` vtable and register with `IoRegisterDisk(index, &driver)`. Filesystem drivers expose an `IO_FILESYSTEM` vtable and register with `IoRegisterFilesystem(index, &fs, &vol)`. Currently shipping: AHCI (interrupts), IDE (PIO/DMA), and FAT32. ### I/O and filesystems All file access flows through the VFS layer. That keeps disk access, filesystem parsing, and path translation separate instead of spreading storage logic across the rest of the kernel. Paths are not interpreted as Windows paths by default. A drive such as `0:\` is a mounted namespace, while the Win32 and POSIX personalities translate their own path styles into the same underlying VFS. ### processes, threads, and syscalls User processes are launched from PE images, imports are resolved before the first thread is queued, and the syscall layer provides the boundary between user code and kernel services. `syscall.dll` is the shared entry point for the supported personalities. The scheduler is preemptive and priority-based, so threads do not depend on cooperative yielding to keep the system responsive. Process state is tracked separately from thread state, which allows a process to exit, be reaped, or be restarted by `init` without bringing down the rest of the system. ### personalities The Win32 personality supports programs that expect drive letters, familiar process APIs, and a Windows-style runtime. The POSIX personality maps the same kernel primitives into a tree-shaped namespace and a different userspace contract. ## how it boots 1. UEFI loads `boot.ini` and reads `drivers.ini` 2. the bootloader maps the kernel, HAL, and boot drivers into the upper half 3. a direct physical window is mapped at `0xFFFF880000000000` 4. control passes to `KernelMain` 5. PMM, VMM, GDT, IDT, HAL, IRQs, and scheduler initialization run 6. runtime drivers are loaded from disk 7. `shell.exe` is launched ## how to build - open the VS x64 build tools as administrator - change into this directory - run `build.bat` - use one of `run.bat` (IDE), `rund.bat` (IDE, debug), `runa.bat` (AHCI), or `runad.bat` (AHCI, debug) ## paths ```text "apps\shell.exe" -> current working directory "0:\apps\shell.exe" -> drive 0 "2:\data\config.ini" -> drive 2 ``` Drives 0-7 are available. A drive only works if a filesystem is registered on it. The Win32 personality translates drive letters to drive numbers, while the POSIX personality maps drives onto a tree. ## screenshots ![video.exe running bink video 2](image.png)
Boot logs ```text BdsDxe: loading Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x4,0x0)/Sata(0x0,0xFFFF,0x0) BdsDxe: starting Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x4,0x0)/Sata(0x0,0xFFFF,0x0) 1 2 3 4 Loading colonel.exe... Loading hal.dll... 5 6 7 8 Found RSDP at: 0xF77E014 9 HAL: screen 1280x800 HAL: FADT PM1a_CNT=0xb004 PM1b_CNT=0x0000 DSDT=0x0f77a000 HAL: _S5_ SLP_TYP_A=0x00 SLP_TYP_B=0x00 HAL: LAPIC=00000000fee00000 IOAPIC=00000000fec00000 HPET=00000000fed00000 HAL: lapicPhys=0xFEE00000, lapicVirt=0xFFFF8800FEE00000, hpetPhys=0xFED00000 HAL: Attempting to calibrate APIC timer via HPET... HAL: APIC timer: 62981 ticks/ms HAL: initialisation complete. KernelMain: Handing over to scheduler... SystemInitThread: Thread started. Igniting the system... InitializeDrivers: Initializing drivers... InitializeDrivers: Executing DriverEntry for ide.sys IDE: Init IDE: No drive on primary channel InitializeDrivers: Driver ide.sys failed to init! InitializeDrivers: Executing DriverEntry for ahci.sys AHCI: driver loading... AHCI: controller at PCI 00:04.0 AHCI: PCI IRQ line = 11 AHCI: ABAR phys=0x81060000 version=0x00010000 AHCI: cap=0xc0141f05 ports=6 slots=32 64bit=1 AHCI: port 0: link up, initializing... AHCI: port COMRESET done: sig=0x00000101 ssts=0x00000113 AHCI: drive 0: 524288 sectors (256 MB) AHCI: drive 0 registered AHCI: port 1: no link (ssts=0x00000000) AHCI: port 2: no link (ssts=0x00000000) AHCI: port 3: no link (ssts=0x00000000) AHCI: port 4: no link (ssts=0x00000000) AHCI: port 5: no link (ssts=0x00000000) AHCI: ISR registered on IRQ 11; initialised 1 drive(s) InitializeDrivers: Executing DriverEntry for fat32.sys FAT32: Init FAT32: Init complete (status=0, clusters=128480). Found runtime driver: usb.sys Test runtime driver SystemInitThread: Loading system DLLs... Memory Status: -> Free (PMM): 151.71 MB -> Kernel Pool: 4.00 MB -> User Heap: 0.55 MB LaunchUserProcess: IoOpenFile: 0 LaunchUserProcess: IoGetFileSize: 5120 LaunchUserProcess: MmAllocateUserMemory: 0x8E020 LaunchUserProcess: IoReadFile: 0 LaunchUserProcess: IoCloseFile Created process (PID 2) with command line: init.exe LaunchUserProcess: PE Image mapped! Base 0x90020, Entry 0x91600 LaunchUserProcess: Thread created and queued! [init] starting [init] waiting shell: ready > help Available commands: help - Show this list dir - List files cd - Change directory type - Display file contents del - Delete a file write - Create a file with text exit - Close the shell > dir System Volume Information drivers efi colonel.exe hal.dll syscall.dll win32.dll posix.dll init.exe shell_win32.exe shell_posix.exe shell_native.exe doom.exe video.exe boot.ini bink2w64.dll video.bk2 doom1.wad > shell_win32.exe shell: ready shell_win32.exe > doom.exe doom: starting getenv: Unknown variable DOOMWADDIR DOOM Shareware Startup v1.10 V_Init: allocate screens. M_LoadDefaults: Load system defaults. Z_Init: Init zone memory allocation daemon. W_Init: Init WADfiles. adding ./doom1.wad =========================================================================== Shareware! =========================================================================== M_Init: Init miscellaneous info. R_Init: Init Doom refresh daemon - [.. ] InitTextures InitFlats........ InitSprites InitColormaps R_InitData R_InitPointToAngle R_InitTables R_InitPlanes R_InitLightTables R_InitSkyMap R_InitTranslationsTables P_Init: Init Playloop state. I_Init: Setting up machine state. I_InitSound: I_InitSound: pre-cached all sound data I_InitSound: sound module ready D_CheckNetGame: Checking network game status. startskill 2 deathmatch: 0 startmap: 1 startepisode: 1 player 1 of 1 (1 nodes) S_Init: Setting up sound. S_Init: default sfx volume 8 HU_Init: Setting up heads up display. ST_Init: Init status bar. doom: entering game loop I_ShutdownSound: NOT finishing pending sounds > shell_posix.exe shell: ready > help Available commands: help - Show this list dir [path] - List directory contents cd - Change directory type - Display file contents del - Delete a file write - Create a file with text exit - Close the shell > dir System Volume Information drivers efi colonel.exe hal.dll syscall.dll win32.dll posix.dll init.exe shell_win32.exe shell_posix.exe shell_native.exe doom.exe video.exe boot.ini bink2w64.dll video.bk2 doom1.wad .doo > video.exe video: starting video.exe video: video.bk2 loaded shell: goodbye > exit shell: goodbye > exit shell: goodbye > exit shell: goodbye [init] done ProcessWatcherThread: Process "init.exe" (PID 2) exited with code 0. ProcessWatcherThread: Shutting down. ```
## faq Q: Why? A: Learning. Fun. Q: Does it run Doom? A: `doom.exe` Q: Is the Win32 layer real? A: Real enough to load binaries that expect it. Not real enough to run Notepad. Q: Are processes isolated? A: No. But IPC is free. Q: Is this finished? A: No. Will it corrupt my data? A: It shouldn't. Q: Why is the path syntax so weird? A: It makes sense to me. Q: Why are the logs so verbose? A: How else do I debug it? Q: What should I read first? A: This document. Q: Can I use this in production? A: Don't.