AnmiTaliDev/uuux

A monolithic kernel.

★ 2Forks 0CGitHub ↗Compare
kernelmonolithposixunix

README

uuux

About

uuux is a monolithic kernel. It is intended for systems programming research, operating systems education, and low-level kernel development.

Dependencies

The following tools are required to build and run uuux:

  • GCC (with 32-bit compilation support via -m32, or an i686-elf cross-compiler)
  • GNU Make
  • GNU Binutils
  • xorriso
  • Limine bootloader (version 5.x or newer)
  • QEMU (qemu-system-i386)
  • GDB (for debugging)

On Debian/Ubuntu systems:

sudo apt install build-essential gcc-multilib qemu-system-x86 xorriso gdb

On Arch Linux:

sudo pacman -S base-devel lib32-gcc-libs qemu-system-x86 xorriso limine gdb

Build

The build system uses GNU Make with GCC. GCC with -m32 -march=i686 is selected because it produces bare-metal 32-bit ELF binaries without requiring an out-of-tree cross-compiler toolchain installation on systems with multilib support, while allowing seamless substitution of an i686-elf-gcc cross toolchain via the CC variable.

To compile the kernel and create a bootable ISO image:

make

To run the kernel in QEMU with serial output directed to the terminal:

make run

To run QEMU waiting for GDB connection on TCP port 1234:

make debug

In another terminal, connect with GDB:

gdb bin/uuux.elf -ex "target remote :1234"

To remove build artifacts:

make clean

Bootloader

Limine is used as the bootloader to provide a portable entry into 32-bit protected mode across diverse BIOS and UEFI firmware environments. Limine protocol conveniences, such as the Limine graphical terminal, framebuffer allocation requests, and higher-level memory map parsers, are not utilized. The kernel uses Limine strictly to load the executable into memory and transfer control. Upon handoff, the kernel immediately establishes its own Global Descriptor Table (GDT), configures hardware serial communications, and manages all subsequent memory and device operations directly.

Memory Management

uuux employs a bitmap-based physical frame allocator (kernel/mm/pmm.c) reading raw memory map entries from the bootloader, an identity-mapped two-level x86 paging implementation (hal/x86/paging.c), and a first-fit kernel heap allocator (kernel/mm/kheap.c). Identity mapping was chosen for simplicity, direct hardware register and memory addressability, and avoiding translation stub overhead during early protected mode initialization.

Interrupt Architecture

uuux implements an x86 Interrupt Descriptor Table (hal/x86/idt.c) and low-level assembly dispatch stubs (hal/x86/interrupt.S). Hardware interrupts are managed via the dual 8259 Programmable Interrupt Controllers (hal/x86/pic.c), remapped to vectors 32 through 47. Periodic timing is driven by the 8254 Programmable Interval Timer (hal/x86/pit.c) generating IRQ0 ticks at 100 Hz, tracked by kernel/time.c. Keyboard input is handled on IRQ1 (kernel/drivers/kbd.c) with Scan Code Set 1 translation. CPU exceptions (vectors 0 through 31) are routed to dedicated diagnostic dumps and halt handlers.

Process Scheduler

uuux implements a round-robin process scheduler (kernel/proc.c) managing fixed-table process slots (struct proc). Both cooperative task switching (via proc_yield using software interrupt vector 0x30) and preemptive timeslicing (triggered on PIT IRQ0 timer ticks) share a unified interrupt frame execution context (struct interrupt_frame). Sleep and wakeup synchronization primitives allow processes to block on arbitrary wait channels. Terminated threads are marked as zombies and their kernel stacks are reclaimed on subsequent scheduling rounds.

System Call Interface

uuux provides a standard int 0x80 software interrupt vector for system calls (kernel/syscall.c). Calling conventions pass the system call index in %eax and arguments in %ebx, %ecx, and %edx. The return value is passed back in %eax, with negative values indicating standard error codes (such as -ENOSYS and -EBADF). The initial dispatch table handles SYS_EXIT, SYS_OPEN, SYS_CLOSE, SYS_READ, SYS_WRITE, SYS_LSEEK, SYS_GETPID, SYS_TIME, and SYS_YIELD.

Virtual File System and RAMFS

uuux implements an in-memory Virtual File System layer (kernel/fs/vfs.c) abstracting file and directory operations through struct vfs_node function pointers. Path resolution parses absolute paths component by component using vfs_lookup and vfs_create. A memory-backed file system (kernel/fs/ramfs.c) populates initial root directories (/dev, /etc, /bin, /tmp) and files, and dynamically allocates storage for file data and directory entries. Per-process file descriptor tables track open files, access modes, and byte offsets.

Userspace and Minimal Shell

uuux implements ring 3 privilege transitions through hardware task state segment configuration (hal/x86/gdt.c) and user descriptor segments (code selector 0x1B and data selector 0x23). Processes created via proc_create_user (kernel/proc.c) start in user mode with dedicated user stacks and trap into the kernel via int 0x80 system calls. On privilege elevation, the CPU automatically switches to the process kernel stack via tss.esp0. A minimal command line interpreter runs as an unprivileged user process supporting built-in commands (help, uname, uptime, exit).

Acknowledgments

  • Dennis Ritchie and Ken Thompson for seminal work on operating systems.
  • The Limine bootloader project and contributors.

Links to docs and CONTRIBUTING

License

uuux is licensed under the GNU General Public License version 3 (GPL-3.0). See the LICENSE file for the full license text.

Contributors

AnmiTaliDev

Issues