hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/boot/user.zig
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-10-04 02:02:15 +0200
committerhachem <im@hachem.wtf>2026-10-04 02:02:15 +0200
commit7c1bd03a71cccfcf48e79d241787021b0de98e73 (patch)
treeb7131a1f4eee60d24781d3f549d191e17793c282 /boot/user.zig
parent627f05098785327744e666c9149375125499d2ff (diff)
feat: add more user programs
Diffstat (limited to 'boot/user.zig')
-rw-r--r--boot/user.zig69
1 files changed, 47 insertions, 22 deletions
diff --git a/boot/user.zig b/boot/user.zig
index 1d0839f..5ba6604 100644
--- a/boot/user.zig
+++ b/boot/user.zig
@@ -1,15 +1,23 @@
-// the syscall boundary (int 0x80). ring 3 entry itself now lives in the
-// scheduler (boot/sched.zig), which iretqs into tasks, this file is just the
-// syscall gate + handlers
-//
+// int 0x80 syscall gate + handlers. ring 3 entry itself lives in sched.zig
// see: https://wiki.osdev.org/System_Calls
const serial = @import("serial.zig");
const idt = @import("idt.zig");
const sched = @import("sched.zig");
const regs = @import("regs.zig");
+const bridge = @import("bridge");
const Frame = regs.Frame;
+// scratch for a filename copied out of the caller. one static buffer is fine,
+// syscalls run one at a time (if=0)
+var name_buf: [64]u8 = undefined;
+fn userName(ptr: u64) []const u8 {
+ const p: [*]const u8 = @ptrFromInt(ptr);
+ var i: usize = 0;
+ while (i < name_buf.len - 1 and p[i] != 0) : (i += 1) name_buf[i] = p[i];
+ return name_buf[0..i];
+}
+
// what the syscall stub leaves on the stack: the gp regs it pushed, then the
// frame the cpu pushed on int 0x80. rax holds the syscall number in, and the
// handler writes the return value back into it (restored by the stubs pop)
@@ -58,42 +66,59 @@ export fn syscallStub() callconv(.naked) void {
export fn syscallHandler(frame: *SyscallFrame) callconv(.c) void {
switch (frame.rax) {
0 => { // exit(code): code in rdi
- sched.exitCurrent(); // mark dead, the next tick reclaims + switches away
+ sched.exitCurrent();
asm volatile ("sti");
- while (true) asm volatile ("hlt"); // never returns to this task
+ while (true) asm volatile ("hlt");
},
1 => { // write(ptr, len): rdi = user pointer, rsi = length
const len = frame.rsi;
- // the processs address space is active during the syscall, so the
- // kernel can read the user pointer directly. cap the length so a
- // bogus arg cant run us off into unmapped memory
if (len > 0 and len <= 4096) {
const bytes: [*]const u8 = @ptrFromInt(frame.rdi);
- serial.write(bytes[0..len]); // mirrored -> kernel tasks on_tick renders it
+ serial.write(bytes[0..len]);
}
frame.rax = len; // syscall return value
},
2 => { // read(): return a buffered byte, else block for one
- // become the foreground reader, if a byte is already queued, take it
if (sched.readReady()) |byte| {
frame.rax = byte;
return;
}
- // otherwise save a context that resumes right after this int 0x80
- // (with rax = the delivered byte), mark the task blocked, and yield
- // the kernel task decodes keys and calls sched.deliverinput to wake us
const resume_frame: Frame = .{
- .r15 = frame.r15, .r14 = frame.r14, .r13 = frame.r13, .r12 = frame.r12,
- .r11 = frame.r11, .r10 = frame.r10, .r9 = frame.r9, .r8 = frame.r8,
- .rbp = frame.rbp, .rdi = frame.rdi, .rsi = frame.rsi, .rdx = frame.rdx,
- .rcx = frame.rcx, .rbx = frame.rbx, .rax = frame.rax,
- .vector = 0, .error_code = 0,
- .rip = frame.rip, .cs = frame.cs, .rflags = frame.rflags,
- .rsp = frame.rsp, .ss = frame.ss,
+ .r15 = frame.r15,
+ .r14 = frame.r14,
+ .r13 = frame.r13,
+ .r12 = frame.r12,
+ .r11 = frame.r11,
+ .r10 = frame.r10,
+ .r9 = frame.r9,
+ .r8 = frame.r8,
+ .rbp = frame.rbp,
+ .rdi = frame.rdi,
+ .rsi = frame.rsi,
+ .rdx = frame.rdx,
+ .rcx = frame.rcx,
+ .rbx = frame.rbx,
+ .rax = frame.rax,
+ .vector = 0,
+ .error_code = 0,
+ .rip = frame.rip,
+ .cs = frame.cs,
+ .rflags = frame.rflags,
+ .rsp = frame.rsp,
+ .ss = frame.ss,
};
sched.blockCurrentOnRead(resume_frame);
asm volatile ("sti");
- while (true) asm volatile ("hlt"); // yield, resumed via rf when input arrives
+ while (true) asm volatile ("hlt");
+ },
+ 4 => { // fs_size(name_ptr): returns size (like obv), or -1
+ frame.rax = @bitCast(bridge.fsSize(userName(frame.rdi)));
+ },
+ 5 => { // fs_read(name_ptr, offset, buf_ptr, len): returns bytes read
+ const len = @min(frame.rcx, 4096);
+ const name = userName(frame.rdi);
+ const dst: [*]u8 = @ptrFromInt(frame.rdx);
+ frame.rax = bridge.fsReadInto(name, frame.rsi, dst[0..len]);
},
else => serial.write("[syscall] unknown\r\n"),
}