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.
This commit is contained in:
109
docs/process.md
Normal file
109
docs/process.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user