# 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 0–3. 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 ```c 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`