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:
72
src/kernel.c
72
src/kernel.c
@@ -10,6 +10,9 @@
|
||||
#include "kmalloc.h"
|
||||
#include "driver.h"
|
||||
#include "vga.h"
|
||||
#include "tss.h"
|
||||
#include "syscall.h"
|
||||
#include "process.h"
|
||||
|
||||
void offset_print(const char *str)
|
||||
{
|
||||
@@ -69,6 +72,15 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
init_kmalloc();
|
||||
offset_print("Memory allocator initialized\n");
|
||||
|
||||
init_tss();
|
||||
offset_print("TSS initialized\n");
|
||||
|
||||
init_syscalls();
|
||||
offset_print("Syscalls initialized\n");
|
||||
|
||||
init_process();
|
||||
offset_print("Process subsystem initialized\n");
|
||||
|
||||
init_drivers();
|
||||
offset_print("Drivers initialized\n");
|
||||
|
||||
@@ -88,6 +100,66 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
offset_print("FAILED to kmalloc\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a minimal test user-mode program.
|
||||
* This is flat binary machine code that calls SYS_WRITE then SYS_EXIT.
|
||||
*
|
||||
* The program writes "Hello from Ring 3!\n" to stdout (fd=1) via INT 0x80,
|
||||
* then exits with code 42.
|
||||
*
|
||||
* Assembly (i386):
|
||||
* ; SYS_WRITE(1, msg, 19)
|
||||
* mov eax, 1 ; SYS_WRITE
|
||||
* mov ebx, 1 ; fd = stdout
|
||||
* call next ; get EIP for position-independent addressing
|
||||
* next:
|
||||
* pop ecx ; ECX = address of 'next'
|
||||
* add ecx, 25 ; ECX = address of message string (offset to msg)
|
||||
* mov edx, 19 ; len = 19
|
||||
* int 0x80
|
||||
* ; SYS_EXIT(42)
|
||||
* mov eax, 0 ; SYS_EXIT
|
||||
* mov ebx, 42 ; code = 42
|
||||
* int 0x80
|
||||
* ; loop forever (shouldn't reach here)
|
||||
* jmp $
|
||||
* msg:
|
||||
* db "Hello from Ring 3!", 10
|
||||
*/
|
||||
static const uint8_t user_program[] = {
|
||||
0xB8, 0x01, 0x00, 0x00, 0x00, /* mov eax, 1 (SYS_WRITE) */
|
||||
0xBB, 0x01, 0x00, 0x00, 0x00, /* mov ebx, 1 (stdout) */
|
||||
0xE8, 0x00, 0x00, 0x00, 0x00, /* call next (push EIP) */
|
||||
/* next: offset 15 */
|
||||
0x59, /* pop ecx */
|
||||
0x83, 0xC1, 0x19, /* add ecx, 25 (offset from 'next' to msg) */
|
||||
0xBA, 0x13, 0x00, 0x00, 0x00, /* mov edx, 19 (length) */
|
||||
0xCD, 0x80, /* int 0x80 */
|
||||
/* SYS_EXIT(42): offset 26 */
|
||||
0xB8, 0x00, 0x00, 0x00, 0x00, /* mov eax, 0 (SYS_EXIT) */
|
||||
0xBB, 0x2A, 0x00, 0x00, 0x00, /* mov ebx, 42 (exit code) */
|
||||
0xCD, 0x80, /* int 0x80 */
|
||||
0xEB, 0xFE, /* jmp $ (infinite loop safety) */
|
||||
/* msg: offset 40 */
|
||||
'H','e','l','l','o',' ','f','r','o','m',' ',
|
||||
'R','i','n','g',' ','3','!','\n'
|
||||
};
|
||||
|
||||
int32_t pid = process_create("init", user_program, sizeof(user_program));
|
||||
if (pid > 0) {
|
||||
offset_print("Created init process, pid=");
|
||||
print_hex((uint32_t)pid);
|
||||
|
||||
/* Enable interrupts before entering user mode */
|
||||
asm volatile("sti");
|
||||
offset_print("Interrupts enabled\n");
|
||||
|
||||
/* Enter user mode - does not return */
|
||||
process_run_first();
|
||||
} else {
|
||||
offset_print("FAILED to create init process\n");
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
asm volatile("sti");
|
||||
offset_print("Interrupts enabled\n");
|
||||
|
||||
Reference in New Issue
Block a user