Files
claude-os/docs/process.md
AI 71e2ae482a Implement Ring 3 process subsystem with syscalls and context switching (AI)
Add complete user-mode process support:

- TSS (tss.c/h): Task State Segment for Ring 3->0 transitions, installed
  as GDT entry 5 (selector 0x28). ESP0 updated per-process for kernel
  stack switching.

- Process management (process.c/h): Process table with up to 64 processes.
  process_create() clones kernel page directory, maps user code at
  0x08048000 and user stack at 0xBFFFF000, copies flat binary code.
  Round-robin scheduler via schedule_tick() modifies the interrupt frame
  in-place for zero-copy context switching.

- System calls (syscall.c/h): INT 0x80 dispatcher with 8 syscalls:
  SYS_EXIT, SYS_WRITE (to debug port + VGA), SYS_READ, SYS_FORK,
  SYS_GETPID, SYS_YIELD, SYS_WAITPID, SYS_EXEC. IDT gate at 0x80
  uses DPL=3 (flags 0xEE) so user code can invoke it.

- Assembly stubs (interrupts.S): isr128 for INT 0x80, tss_flush for
  loading the Task Register, enter_usermode for initial iret to Ring 3.

- Paging extensions (paging.c/h): paging_clone_directory() to create
  per-process page directories, paging_map_page_in() for mapping into
  non-active directories, paging_switch_directory() for CR3 switching.

- GDT expanded from 5 to 6 entries to accommodate TSS descriptor.
  gdt_set_gate() exposed in header for TSS initialization.

- ISR handler routes timer IRQ (32) to scheduler and INT 0x80 to
  syscall dispatcher. Exception handler now prints EIP/CS/ERR for
  debugging.

- Kernel boots a test user program that writes 'Hello from Ring 3!'
  via SYS_WRITE and exits with code 42 via SYS_EXIT. Verified working
  in QEMU.

Context switching approach: Timer/syscall interrupts save all registers
via the ISR stub. schedule_tick() copies saved_regs between PCBs and
overwrites the interrupt frame, so the existing iret restores the next
process's state without separate switch assembly.
2026-02-23 12:10:46 +00:00

4.1 KiB
Raw Permalink Blame History

Process Subsystem

Overview

The process subsystem enables user-mode (Ring 3) process execution on ClaudeOS. It provides process creation, context switching via the timer interrupt, and system calls via INT 0x80.

Architecture

Ring Transition

x86 protected mode uses privilege rings 03. The kernel runs in Ring 0 (full hardware access) and user processes run in Ring 3 (restricted). The GDT defines segment descriptors for both:

GDT Entry Selector Purpose DPL
0 0x00 Null
1 0x08 Kernel Code 0
2 0x10 Kernel Data 0
3 0x18 User Code 3
4 0x20 User Data 3
5 0x28 TSS 0

User-mode selectors include RPL=3: code = 0x1B, data = 0x23.

Task State Segment (TSS)

The TSS (tss.c) stores the kernel stack pointer (SS0:ESP0) used when the CPU transitions from Ring 3 to Ring 0 on an interrupt. Before running each process, the scheduler updates TSS.ESP0 to that process's kernel stack top.

Memory Layout

Each process gets its own page directory, cloned from the kernel's:

0x00000000  0x07FFFFFF : Identity-mapped (kernel/device access)
0x08048000  ...         : User code (loaded from binary image)
0xBFFF7000  0xBFFFF000 : User stack (2 pages, grows downward)
0xD0000000  0xF0000000 : Kernel heap (shared across all processes)

Process Control Block

typedef struct process {
    uint32_t pid;
    process_state_t state;       // UNUSED, READY, RUNNING, BLOCKED, ZOMBIE
    registers_t saved_regs;      // Full interrupt frame
    uint32_t kernel_stack;       // Base of per-process kernel stack
    uint32_t kernel_stack_top;   // TSS ESP0 value
    uint32_t page_directory;     // Physical address of page directory
    uint32_t user_stack;         // User stack virtual address
    uint32_t entry_point;        // User code entry point
    int32_t exit_code;           // Set on exit
    uint32_t parent_pid;
    char name[32];
} process_t;

Context Switching

Context switching uses the interrupt frame directly:

  1. Timer IRQ (or INT 0x80 for SYS_YIELD) fires
  2. CPU pushes SS/ESP/EFLAGS/CS/EIP onto the process's kernel stack
  3. ISR stub pushes the rest (pusha + DS) forming a registers_t
  4. schedule_tick() is called with a pointer to these registers
  5. Current process's registers are saved into its PCB
  6. Next READY process's saved registers are written over the interrupt frame
  7. TSS.ESP0 is updated, CR3 is switched to the new page directory
  8. ISR stub restores the (now different) registers and iret enters the new process in user mode

This avoids separate context-switch assembly — the existing ISR stub handles everything.

System Calls

System calls use INT 0x80 with the call number in EAX:

Number Name Arguments
0 SYS_EXIT EBX = exit code
1 SYS_WRITE EBX = fd, ECX = buf, EDX = len
2 SYS_READ (not implemented)
3 SYS_FORK (returns child PID/0)
4 SYS_GETPID (returns PID in EAX)
5 SYS_YIELD (voluntary preemption)
6 SYS_WAITPID EBX = child PID
7 SYS_EXEC (not implemented)

The INT 0x80 IDT gate has DPL=3 (flags 0xEE) so user-mode code can invoke it.

Initial Process Entry

process_run_first() performs the initial transition to user mode using an iret instruction that sets up Ring 3 segment selectors, the user stack pointer, and the entry point. This is a one-way transition — the function does not return.

Files

  • tss.h / tss.c — TSS structure and initialization
  • process.h / process.c — Process table, creation, scheduling, exit, fork
  • syscall.h / syscall.c — System call dispatch and handlers
  • interrupts.S — Assembly stubs: isr128 (INT 0x80), tss_flush, enter_usermode