Skip to content

Latest commit

 

History

History
138 lines (103 loc) · 5.07 KB

File metadata and controls

138 lines (103 loc) · 5.07 KB

PromptOS Developer Guide

This guide covers architecture, build process, and how to extend PromptOS.

Architecture Overview

PromptOS is a monolithic x86-64 kernel with the following components:

Boot Flow

  1. UEFI Bootloader (bootloader/) – Loads kernel and initramfs, sets up framebuffer, passes boot info
  2. Kernel Entry (kernel/arch/x86_64/boot/) – Assembly bootstrap, GDT, IDT
  3. Kernel Main (kernel/main.c) – Phased initialization:
    • Phase 1: CPU (GDT, TSS, IDT)
    • Phase 2: Boot info parsing
    • Phase 3: Memory (PMM, heap, slab)
    • Phase 4: ACPI
    • Phase 5: Interrupts (APIC, HPET)
    • Phase 6: Scheduler and threads
    • Phase 7: VFS, drivers, network
    • Phase 8: Syscalls, userspace prep

Core Subsystems

Subsystem Location Description
Memory kernel/mm/ PMM, VMM, heap, slab, uaccess
Process kernel/proc/ Processes, ELF, signals
Scheduler kernel/sched/ Threads, context switch
Filesystem kernel/fs/ VFS, ramfs, betterfs, procfs
Network kernel/net/ TCP/UDP, sockets, routing
Drivers kernel/drivers/ Block, input, net, PCI
Debug kernel/debug/ ksyms, profiler

Build Instructions

Prerequisites

  • Cross-compiler: x86_64-elf-gcc, x86_64-elf-ld
  • NASM assembler
  • For bootloader: MinGW (gcc-mingw-w64-x86-64)
  • For disk image: sgdisk, mkfs.fat, mtools, mkfs.betterfs

Building the Toolchain

make toolchain

This builds a cross-compiler into toolchain/.

Building the OS

make              # Full build (kernel, bootloader, userland, tests, image)
make kernel       # Kernel only
make bootloader   # UEFI bootloader
make userland     # init, hello
make tests        # Stress tests
make image        # Create bootable disk image

Running in QEMU

make run

Or with GDB:

make debug
# In another terminal: gdb -ex 'target remote :1234' build/kernel/kernel.elf

Directory Structure

PromptOS/
├── bootloader/       # UEFI bootloader (MinGW)
├── kernel/           # Kernel source
│   ├── arch/x86_64/  # Architecture-specific (boot, SMP, syscall)
│   ├── acpi/         # ACPI parsing, power
│   ├── drivers/      # PCI, block, net, input, etc.
│   ├── fs/           # VFS and filesystems
│   ├── ipc/          # Pipes, FIFOs, futex, poll
│   ├── mm/           # Memory management
│   ├── net/          # TCP/IP stack
│   ├── proc/         # Process management
│   ├── sched/        # Scheduler
│   ├── sync/         # Spinlocks, mutexes
│   ├── syscall/      # GUI syscalls
│   └── debug/        # ksyms, profiler
├── userland/         # Userspace programs
│   ├── init/         # Init process
│   ├── libc/         # Minimal libc (syscall wrappers)
│   └── apps/         # Desktop applications
├── tests/            # Stress tests
├── tools/            # mkfs.betterfs, fsck
├── scripts/          # Build and run scripts
└── docs/             # Documentation

How to Add a New Syscall

  1. Choose a syscall number – See docs/SYSCALL.md for the number table. Use the next free slot in the appropriate range.

  2. Implement in kernel – In kernel/arch/x86_64/syscall.c:

    • Add #define SYS_MYNEW XX
    • Implement static i64 sys_mynew(...) with proper argument handling
    • Add a case SYS_MYNEW: in syscall_handler_c()
  3. Expose to userspace – In userland/libc/syscall.h:

    • Add #define SYS_MYNEW XX
    • Add wrapper: static inline int64_t mynew(...) { return syscallN(SYS_MYNEW, ...); }
  4. Copy from user – For pointer arguments, use copy_from_user() / copy_to_user() from mm/uaccess.h. Validate with is_user_address().

  5. Error handling – Return negative errno on failure (e.g. -EINVAL, -ENOMEM).

How to Add a New Driver

  1. Identify the bus – PCI drivers use kernel/drivers/pci/. Create a subdirectory under drivers/ (e.g. drivers/mydev/).

  2. Register with PCI – Use pci_register_driver() or scan the PCI bus in your init. See kernel/drivers/net/e1000.c for an example.

  3. Implement ops – For block devices: implement BlockDeviceOps. For character devices: register with devfs.

  4. Interrupt handling – Register IRQ via irq_register_handler(). In the handler, acknowledge the interrupt (device-specific) and signal completion.

  5. Add to kernel Makefile – Ensure your .c files are covered by $(wildcard drivers/mydev/*.c) or add explicit entries.

Debugging

  • Serial output – Kernel logs to COM1 (0x3F8). Use -serial stdio in QEMU.
  • Profiler – Call profiler_start("name") / profiler_stop("name") in kernel code, then profiler_dump() to print statistics.
  • Stack tracesksyms_print_stack_trace(rbp, max_frames) after a fault.
  • QEMU monitor – Use Ctrl+A C to enter the monitor, info mem for mappings.