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:
@@ -118,3 +118,59 @@ ISR_IRQ 12, 44
|
||||
ISR_IRQ 13, 45
|
||||
ISR_IRQ 14, 46
|
||||
ISR_IRQ 15, 47
|
||||
|
||||
/*
|
||||
* INT 0x80 - System call entry point.
|
||||
* Uses the same isr_common_stub so the register layout matches registers_t.
|
||||
*/
|
||||
.global isr128
|
||||
.type isr128, @function
|
||||
isr128:
|
||||
cli
|
||||
push $0 /* Fake error code */
|
||||
push $0x80 /* Interrupt number 128 */
|
||||
jmp isr_common_stub
|
||||
|
||||
/*
|
||||
* tss_flush - Load the Task Register with the TSS selector.
|
||||
* TSS is GDT entry 5, selector = 5*8 = 0x28. With RPL=0: 0x28.
|
||||
*/
|
||||
.global tss_flush
|
||||
.type tss_flush, @function
|
||||
tss_flush:
|
||||
mov $0x28, %ax
|
||||
ltr %ax
|
||||
ret
|
||||
|
||||
/*
|
||||
* enter_usermode - Switch to Ring 3 user mode via iret.
|
||||
* void enter_usermode(uint32_t eip, uint32_t esp);
|
||||
*
|
||||
* Builds an iret frame on the stack:
|
||||
* SS = 0x23 (user data)
|
||||
* ESP = user stack pointer
|
||||
* EFLAGS = IF=1
|
||||
* CS = 0x1B (user code)
|
||||
* EIP = user entry point
|
||||
*/
|
||||
.global enter_usermode
|
||||
.type enter_usermode, @function
|
||||
enter_usermode:
|
||||
mov 4(%esp), %ecx /* user EIP */
|
||||
mov 8(%esp), %edx /* user ESP */
|
||||
|
||||
/* Set data segment registers to user data segment */
|
||||
mov $0x23, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
/* Build iret frame */
|
||||
push $0x23 /* SS (user data) */
|
||||
push %edx /* ESP (user stack) */
|
||||
pushf /* EFLAGS */
|
||||
orl $0x200, (%esp) /* Ensure IF (Interrupt Flag) is set */
|
||||
push $0x1B /* CS (user code) */
|
||||
push %ecx /* EIP (entry point) */
|
||||
iret
|
||||
|
||||
Reference in New Issue
Block a user