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:
48
src/paging.c
48
src/paging.c
@@ -276,3 +276,51 @@ void init_paging(void) {
|
||||
|
||||
offset_print(" PAGING: enabled\n");
|
||||
}
|
||||
|
||||
uint32_t paging_get_directory_phys(void) {
|
||||
return (uint32_t)page_directory;
|
||||
}
|
||||
|
||||
uint32_t paging_clone_directory(void) {
|
||||
/* Allocate a new page for the directory */
|
||||
phys_addr_t new_dir_phys = pmm_alloc_page(PMM_ZONE_NORMAL);
|
||||
if (new_dir_phys == 0) {
|
||||
offset_print(" PAGING: cannot allocate page directory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t *new_dir = (uint32_t *)new_dir_phys;
|
||||
|
||||
/* Copy all entries from the kernel page directory.
|
||||
* This shares the kernel-space mappings (identity map, kernel heap)
|
||||
* with the new process. User-space mappings will be added separately. */
|
||||
memcpy(new_dir, page_directory, 4096);
|
||||
|
||||
return new_dir_phys;
|
||||
}
|
||||
|
||||
void paging_map_page_in(uint32_t *pd, uint32_t vaddr, uint32_t paddr, uint32_t flags) {
|
||||
uint32_t pd_idx = PD_INDEX(vaddr);
|
||||
uint32_t pt_idx = PT_INDEX(vaddr);
|
||||
|
||||
uint32_t *pt;
|
||||
if (pd[pd_idx] & PAGE_PRESENT) {
|
||||
pt = (uint32_t *)(pd[pd_idx] & 0xFFFFF000);
|
||||
} else {
|
||||
/* Allocate a new page table */
|
||||
phys_addr_t pt_phys = pmm_alloc_page(PMM_ZONE_NORMAL);
|
||||
if (pt_phys == 0) {
|
||||
offset_print(" PAGING: cannot allocate page table for process\n");
|
||||
return;
|
||||
}
|
||||
memset((void *)pt_phys, 0, 4096);
|
||||
pd[pd_idx] = pt_phys | PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
|
||||
pt = (uint32_t *)pt_phys;
|
||||
}
|
||||
|
||||
pt[pt_idx] = (paddr & 0xFFFFF000) | (flags & 0xFFF);
|
||||
}
|
||||
|
||||
void paging_switch_directory(uint32_t phys_addr) {
|
||||
__asm__ volatile("mov %0, %%cr3" : : "r"(phys_addr) : "memory");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user