CIndy-OS is a small 32-bit x86 hobby operating system built from scratch with NASM and freestanding C. It is intentionally explicit and educational: the boot path, interrupt handling, memory management, paging, filesystems, user-mode execution, system calls, and early process support are implemented by hand.
Status: early kernel / learning OS. The current tree boots with GRUB, provides an interactive shell, accesses a FAT16 disk and initrd, loads flat binaries and simple ELF executables, enters ring 3, and contains the first pieces of a cooperative process model.
- GRUB Multiboot boot path for a 32-bit kernel ISO.
- Protected-mode startup with a GDT, TSS, and kernel stack.
- VGA text-mode output with cursor control, colored output, scrolling, and serial initialization.
- QEMU run targets for normal, curses, and debug-console operation.
- IDT setup with CPU exception handlers.
- PIC remapping.
- IRQ0 timer support through the PIT.
- IRQ1 PS/2 keyboard support.
int 0x80syscall entry from user mode.- Basic keyboard-controller reboot support.
The shell supports simple whitespace-separated argc/argv parsing and currently includes:
help,clear,echoabout,version,whoami,pwdtimer,uptime,meminfols,catfor the initrd filesystemfat-ls,fat-cat,fat-writefor the FAT16 diskread-test,write-testfor raw ATA sector testingrun <file>to load a flat binary or ELF imagespawn <file.elf>to create and start an ELF-backed processreboot
- Kernel heap allocation through
kmalloc_a. - Physical memory manager using a bitmap of page frames.
- Multiboot memory-map based memory initialization.
- Paging enabled with identity-mapped physical memory sized from detected RAM, with a fallback mapping when detection is unavailable.
- Dynamic page mapping through
map_pageand page-table lookup throughget_pte. - User-region and user-pointer validation for syscall memory access.
- User mappings currently use a shared page directory and are not isolated per process.
- ATA PIO sector reads and writes.
- A generated 20 MiB FAT16 disk image.
- FAT16 root-directory listing, file reads, and file creation/writes using 8.3 filenames.
- USTAR/tar-style initrd support for listing and reading files.
- The build copies the generated initrd into the bootable ISO and test programs into the FAT16 disk image.
- Ring-3 entry and return helpers using the GDT/TSS setup.
- Flat binaries can be loaded at
0x40000000. - Simple 32-bit
ET_EXECELF files are recognized and theirPT_LOADsegments are mapped at their requested virtual addresses. - A user stack is allocated and mapped at
0x80000000. SYS_EXITreturns control to the kernel-side launch path.- The repository includes
user/YOOO.asmas a flat-binary syscall test anduser/hello_elf.asmas a minimal ELF example.
The repository now contains an initial process subsystem:
- Process states:
NEW,READY,RUNNING,BLOCKED, andEXITED. - PID allocation and a FIFO ready queue.
- Process lookup by PID and current-process tracking.
spawn <file.elf>creates a process structure, loads an ELF image, allocates a user stack, and queues it.SYS_YIELDrotates the ready queue and saves/restores the basic register frame.
This is process and scheduling groundwork rather than complete multitasking: context switching, address-space isolation, and lifecycle cleanup are still incomplete.
System calls are entered through int 0x80 with the following register convention:
eaxβ syscall numberebxβ first argumentecxβ second argumentedxβ third argument
Defined calls:
| Number | Name | Current behavior |
|---|---|---|
0 |
SYS_EXIT |
Records the exit status and requests return from the user program. |
1 |
SYS_WRITE |
Writes to stdout/stderr; other file descriptors are not fully implemented. |
2 |
SYS_OPEN |
Minimal placeholder behavior; only limited paths are accepted. |
3 |
SYS_CLOSE |
Accepts the standard descriptors; other descriptors are not complete. |
4 |
SYS_READ |
stdin currently returns zero; general file reads are not implemented. |
5 |
SYS_YIELD |
Performs an early cooperative ready-queue rotation when a process is active. |
Standard descriptors are 0 for stdin, 1 for stdout, and 2 for stderr. User pointers are validated against mapped, user-accessible pages before copying data, except for the temporary kernel-mode test fallback used by early execution paths.
CIndy-OS/
βββ src/
β βββ boot.asm # Multiboot entry and early startup
β βββ kernel.c # Kernel initialization and interrupt registration
β βββ screen.c # VGA text output and serial helpers
β βββ ports.c # x86 I/O port wrappers
β βββ idt.c # IDT construction
β βββ idt_load.asm # lidt helper
β βββ isr.asm # exception, IRQ, and syscall stubs
β βββ interrupts.asm # interrupt enable helper
β βββ pic.c # PIC remapping
β βββ timer.c # PIT timer support
β βββ keyboard.c # PS/2 keyboard, shell, binary and ELF loading
β βββ memory.c # Kernel allocator and physical memory manager
β βββ paging.c # Page directories, mapping, and validation
β βββ fs.c # Initrd/USTAR filesystem
β βββ ata.c # ATA PIO disk access
β βββ fat16.c # FAT16 driver
β βββ gdt.c # GDT and TSS setup
β βββ syscall.c # Syscall dispatch and user copies
β βββ process.c # Early process and ready-queue support
β βββ string.c # Freestanding string/memory helpers
β βββ gdt_flush.asm # GDT reload helper
β βββ usermode.asm # Ring-3 entry/return helpers
βββ include/ # Kernel headers and ABI definitions
βββ user/
β βββ YOOO.asm # Flat user-mode syscall test
β βββ hello_elf.asm # Minimal 32-bit ELF test program
βββ fs/ # Files packaged into the initrd
βββ iso/boot/grub/grub.cfg # GRUB configuration
βββ linker.ld # Kernel linker script
βββ Makefile # Build, image, ISO, and QEMU targets
βββ disk.img # Generated FAT16 disk image
βββ recap.md # Project workflow recap
βββ docs/ # Learning notes, internals, devlog, and errors
βββ README.md
Generated artifacts such as kernel.bin, CIndy-os.iso, initrd.tar, and the FAT16 image are produced or refreshed by the build process and may be present in the working tree.
GRUB -> kernel.bin -> boot.asm -> kernel_main()
Kernel startup initializes the serial console, screen, GDT/TSS, IDT, PIC, timer, paging, physical memory manager, initrd, FAT16 driver, and process subsystem before entering the keyboard-driven shell.
run <file>
-> read a file from FAT16
-> detect a flat binary or ELF image
-> allocate physical memory and map user pages
-> allocate/map a user stack
-> enter ring 3
-> user code invokes int 0x80
-> syscall handler validates arguments and dispatches the call
-> SYS_EXIT returns to the kernel launcher
spawn <file.elf>
-> read and validate an ELF image
-> map its PT_LOAD segments
-> allocate a user stack
-> create a process structure and PID
-> enqueue it as READY
-> dequeue and enter its user entry point
On Debian/Ubuntu-like systems:
sudo apt-get install -y nasm gcc grub-pc-bin grub-common xorriso mtools qemu-system-x86A working 32-bit freestanding toolchain is required because the kernel is compiled with gcc -m32 and linked as elf_i386.
makeThe Makefile:
- assembles the boot, interrupt, GDT, and user-mode assembly;
- compiles the freestanding C kernel sources;
- links
kernel.bin; - builds the flat-binary and ELF user tests;
- creates a 20 MiB FAT16
disk.imgand copies the user images into it; - packages
fs/asinitrd.tar; and - creates
CIndy-os.isowithgrub-mkrescue.
make run
make run-curses
make run-debugmake run-debug hides the graphical display and sends the QEMU debug console to standard output.
make cleanCIndy-OS is not yet a general-purpose operating system. Current limitations include:
- one shared page directory; no per-process address spaces or CR3 switching;
- no complete scheduler or timer-driven preemptive multitasking;
- cooperative yield support is present but still an early context-switching prototype;
- process kernel stacks, cleanup, termination, and complete saved CPU state are unfinished;
- file descriptors are only partially modeled and filesystem-backed syscall I/O is incomplete;
SYS_READis not connected to a keyboard input buffer;SYS_OPENandSYS_CLOSEhave placeholder semantics;- ELF loading supports a small 32-bit
ET_EXECsubset and does not provide relocations, dynamic linking, or full permission handling; - user memory isolation is limited by the shared identity-mapped address space;
- the kernel allocator has no general free path, so some temporary load buffers are leaked;
- hardware support is intentionally limited to the devices needed by the current learning path.
These constraints are deliberate milestones for an educational kernel, not claims of production readiness.
- GRUB boot and protected mode
- GDT, TSS, IDT, PIC, timer, and keyboard interrupt setup
- Kernel shell and basic diagnostics
- Physical memory manager and paging
- Initrd/USTAR and FAT16 read/write support
- Flat-binary and minimal ELF loading
- Ring-3 transition and basic syscall entry
- Initial process structures, PID allocation, ready queue, and cooperative yield prototype
- Per-process page directories and address-space isolation
- Complete process context switching and kernel stacks
- Timer-driven preemptive scheduler
- Process exit/reaping and resource cleanup
- Proper file-descriptor table and filesystem-backed syscalls
- Keyboard input buffering for
SYS_READ - More complete ELF validation, permissions, and relocation support
- OSDev Wiki
- Multiboot Specification
- NASM Documentation
- James Molloy's Kernel Tutorial
- x86 Instruction Reference
This is a personal learning project. Use and modify freely.