v0.5.3: syscalls, minilibc.

- fix(gs): was a nightmare. now its solved. Both MSR_GS_BASE and MSR_KERNEL_GS_BASE are now initialized to , preventing null GS_BASE when interrupt occurs during the first kernel task run
- chore(syscall.asm): stability improvements. User RSP is now saved via kernel stack instead of global g_cpu, preventing stack theft when task switch occurs during a syscall
- feat(interrupts): all irq and isr handlers now conditionally perform swapgs based on the CS selector (checkin if comfing from ring 3)
- upgrade(scheduler): big update: 1) added TASK_BLOCKED state from process sync; 2) syncronized PID and Task ID to fix wakeup logic; 3) implemented sched_block and sched_exit() for proper wait/exit syscalls
This commit is contained in:
Karina
2026-01-30 07:12:57 +04:00
parent 888bc5abdd
commit caf2d2b4a8
18 changed files with 122 additions and 41 deletions
-1
View File
@@ -3,7 +3,6 @@ build
.venv
kernel/data
*.md
tools/
bootloader/
bootloader/src/uefi
initramfs/
+5 -5
View File
@@ -23,13 +23,13 @@
### v0.5.3 (Immediate Priority)
*Focus: Lifecycle & Memory*
- [ ] **Syscall: Memory Management**
- Implement `sys_sbrk` or `sys_mem`.
- [x] **Syscall: Memory Management**
- Implement `sys_mem`.
- Allow userspace to dynamically allocate pages (User Heap).
- [ ] **Syscall: Process Lifecycle**
- [x] **Syscall: Process Lifecycle**
- `sys_spawn(path)`: Load `.hot` files via VFS, create new process structures.
- `sys_exit(code)`: Proper termination, ZOMBIE state, resource cleanup in scheduler.
- [ ] **Userspace Lib (mini-libc)**
- [x] **Userspace Lib (mini-libc)**
- Wrappers: `malloc`, `free`, `exec`, `exit`.
- `crt0.asm` for proper `main()` entry.
@@ -51,7 +51,7 @@
- [ ] **Bootloader Independence**
- Remove `posix-uefi` library.
- Write custom UEFI entry point (pure PE).
- Kernel itself becomes a valid `.hot` executable (or loaded by custom bootloader).
- Kernel itself becomes a valid `.hot` executable.
- [ ] **HOT! Format Hardening**
- Fix segment alignment (Page Alignment) in `elf2hot` and kernel loader.
- Add metadata headers (stack size, permissions).
+6 -4
View File
@@ -3,10 +3,11 @@
#pragma once
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_FMASK 0xC0000084
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_FMASK 0xC0000084
#define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
#define EFER_SCE 0x01 // System Call Enable
@@ -17,6 +18,7 @@ typedef enum {
SYS_MEM,
SYS_WRITE,
SYS_READ,
SYS_WAIT,
} syscalls;
void syscall_init();
+8 -1
View File
@@ -7,6 +7,9 @@
typedef enum process_state {
DEAD,
RUNNING,
READY,
BLOCKED,
SLEEPING,
} process_state;
typedef struct process {
@@ -27,9 +30,13 @@ typedef struct task {
process_state task_state; // reusing process_state cuz wn
u64 kernel_stack_top;
process* proc;
i32 waiting_on_pid;
} task;
void sched_init();
task* sched_spawn(void(*entry)(), process* owner, bool is_user, u64 fixed_user_stack);
u64 sched_next(u64 curr_rsp);
void yield(u64 ticks);
void yield(u64 ticks);
void sched_block(u32 pid);
void sched_wakeup(u32 pid);
void sched_exit(); // suicide
+1 -1
View File
@@ -7,4 +7,4 @@
void timer_init(u32 freq);
u64 timer_handler(Registers *regs);
void sleep(u64 ms);
u64 get_uptime();
u64 get_uptime();
+2 -1
View File
@@ -6,4 +6,5 @@
#include <types.h>
u64 sys_exit(i32 code);
u64 sys_spawn(const char* path);
u64 sys_spawn(const char* path);
u64 sys_wait(u64 pid);
+31 -5
View File
@@ -63,16 +63,37 @@ isr%1:
global %2
%2:
push 0 ; dummy err code
push %1
push %1 ; int_no
; [rsp] = int_no (8)
; [rsp + 8] = err_code (8)
; [rsp + 16] = RIP (8)
; [rsp + 24] = CS (8)
test qword [rsp + 24], 3
jz .skip_swap
swapgs
.skip_swap:
PUSHALL
mov rdi, rsp
cld
call irq_handler_c
mov rsp, rax
mov rsp, rax
; PUSHALL - 15 * 8 = 120 bytes
; + int_no (8) + err_code (8) = 136 bytes
; [rsp + 136] = RIP
; [rsp + 144] = CS
test qword [rsp + 144], 3
jz .skip_swap_back
swapgs
.skip_swap_back:
POPALL
add rsp, 16
iretq
@@ -112,13 +133,18 @@ ISR_ERRCODE 30 ; Security Exception
ISR_NOERRCODE 31 ; Reserved
isr_common_stub:
test qword [rsp + 24], 3
jz .skip_swap
swapgs
.skip_swap:
PUSHALL
mov rdi, rsp
call isr_handler_c
test qword [rsp + 144], 3
jz .skip_swap_back
swapgs
.skip_swap_back:
POPALL
add rsp, 16
iretq
+1 -1
View File
@@ -21,7 +21,7 @@ u64 irq_handler_c(Registers *regs) {
switch (regs->int_no) {
case 32: curr_rsp = timer_handler(regs); break;
case 33: kb_handler(); break;
default: outb(SLAVE_COMMAND, 0x20); break;
default: break;
}
outb(MASTER_COMMAND, 0x20);
+2 -2
View File
@@ -58,9 +58,9 @@ syscall_entry:
pop rbp
pop rcx ; rip
pop r11 ; rflags
add rsp, 8 ; skip rsp
pop rsp
mov rsp, [gs:0]
swapgs
o64 sysret
+6 -4
View File
@@ -47,18 +47,20 @@ void syscall_init() {
g_cpu.kernel_rsp = (u64)stack_phys + HHDM_OFFSET + 4096;
}
wrmsr(MSR_GS_BASE, (u64)&g_cpu);
wrmsr(MSR_KERNEL_GS_BASE, (u64)&g_cpu);
}
u64 syscall_dispatch(u64 id, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5) {
__asm__ volatile("cli");
switch (id) {
case SYS_EXIT: return sys_exit(arg1);
case SYS_SPAWN: return sys_spawn((const char*)arg1);
case SYS_MEM: return sys_mem(arg1);
case SYS_WRITE: return sys_write(arg1, arg2, arg3);
case SYS_READ: return sys_read(arg1, arg2, arg3);
default: kprintf("[Dewar] Unknown syscall %d\n", id); return -1;
case SYS_READ: return sys_read(arg1, arg2, arg3);
case SYS_WAIT: return sys_wait(arg1);
default:
kprintf("[Dewar] Unknown syscall %d\n", id);
return -1;
}
__asm__ volatile("sti");
}
+37 -7
View File
@@ -37,6 +37,7 @@ void sched_init() {
kt->sleep_ticks = 0;
kt->next = kt;
kt->task_state = RUNNING;
kt->waiting_on_pid = -1;
curr_task = kt;
sched_spawn(idle_task, &kernel_process, false, 0);
@@ -72,11 +73,12 @@ task* sched_spawn(void(*entry)(), process* owner, bool is_user, u64 fixed_user_s
t->rsp = (u64)rsp;
t->proc = owner;
t->id = next_pid++;
t->id = owner->pid;
t->sleep_ticks = 0;
t->next = curr_task->next;
t->kernel_stack_top = (u64)stack_base + stack_size;
t->task_state = RUNNING;
t->waiting_on_pid = -1;
curr_task->next = t;
return t;
}
@@ -94,24 +96,52 @@ u64 sched_next(u64 curr_rsp) {
if (curr_task->sleep_ticks > 0) curr_task->sleep_ticks--;
task* next = curr_task->next;
while (next->task_state == DEAD) {
task* next = curr_task;
while (1) {
// TODO: add gc here;
next = next->next;
if (next == curr_task) panic("no alive tasks");
if (next->task_state == SLEEPING && next->sleep_ticks == 0) next->task_state = RUNNING;
if (next->task_state == RUNNING) break;
if (next == curr_task) {
if (curr_task->task_state == RUNNING) break;
panic("no running tasks");
}
}
while (next != curr_task && next->sleep_ticks > 0) next = next->next; // what the fuck i just wrote
if (next->proc->pml4_phys != curr_task->proc->pml4_phys) load_cr3(next->proc->pml4_phys);
curr_task = next;
tss.rsp0 = curr_task->kernel_stack_top;
g_cpu.kernel_rsp = curr_task->kernel_stack_top;
return curr_task->rsp;
}
void sched_block(u32 pid) {
curr_task->task_state = BLOCKED;
curr_task->waiting_on_pid = pid;
__asm__ volatile("int $32");
}
void sched_wakeup(u32 pid) {
task* it = curr_task;
do {
if (it->task_state == BLOCKED && it->waiting_on_pid == (i32)pid) {
it->task_state = RUNNING;
it->waiting_on_pid = -1;
}
it = it->next;
} while (it != curr_task);
}
void sched_exit() {
u32 my_pid = curr_task->id;
curr_task->task_state = DEAD;
sched_wakeup(my_pid);
__asm__ volatile("int $32");
}
void yield(u64 ticks) {
curr_task->sleep_ticks = ticks;
curr_task->task_state = SLEEPING;
__asm__ volatile("hlt");
}
-1
View File
@@ -111,5 +111,4 @@ void kb_handler() {
default: break;
}
}
outb(MASTER_COMMAND, 0x20);
}
+1 -1
View File
@@ -26,10 +26,10 @@ void timer_init(u32 freq) {
u64 timer_handler(Registers *regs) {
ticks++;
outb(MASTER_COMMAND, 0x20);
return sched_next((u64)regs);
}
void sleep(u64 ms) {
u64 start = ticks;
while (ticks < start + ms) __asm__ volatile ("hlt");
+4 -3
View File
@@ -10,9 +10,7 @@ extern task* curr_task;
u64 sys_exit(i32 code) {
kprintf("\n[Dewar] process %s exited with code %d", curr_task->proc->name, code);
curr_task->task_state = DEAD;
sched_next(curr_task->rsp);
while (1) { __asm__ ("hlt"); }
sched_exit();
}
@@ -20,3 +18,6 @@ u64 sys_spawn(const char* path) {
return process_spawn(path, path);
}
u64 sys_wait(u64 pid) {
sched_block(pid);
}
+5 -3
View File
@@ -6,7 +6,9 @@
#include <malloc.h>
int main() {
printf("Launching debug\n");
spawn("debug");
while (1) {}
wait(spawn("debug"));
printf("\nStill here?\n");
while (1) {
printf("1");
}
}
+2 -1
View File
@@ -4,4 +4,5 @@
#pragma once
#include <types.h>
u64 spawn(const char* path);
u64 spawn(const char* path);
u64 wait(u64 pid);
+5
View File
@@ -4,7 +4,12 @@
#include <process.h>
extern u64 sys_spawn(const char* path);
extern u64 sys_wait(u64 pid);
u64 spawn(const char* path) {
return sys_spawn(path);
}
u64 wait(u64 pid) {
return sys_wait(pid);
}
+6
View File
@@ -10,6 +10,7 @@ global sys_spawn
global sys_mem
global sys_write
global sys_read
global sys_wait
sys_exit:
mov rax, 0
@@ -35,3 +36,8 @@ sys_read:
mov rax, 4
syscall
ret
sys_wait:
mov rax, 5
syscall
ret