diff options
| author | hachem <im@hachem.wtf> | 2026-10-10 08:53:20 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-10-10 08:53:20 +0200 |
| commit | 89abe49aad1ac7416844f3c5e95655ad2614392b (patch) | |
| tree | ff9170aa0673b3db76e03ab80a5056e576a48e7b /boot | |
| parent | 7642b3819c90ec0c4686c32e5f89c59cc47cc2d3 (diff) | |
feat: brk/sbrk syscalls + shitty heap
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/elf.zig | 1 | ||||
| -rw-r--r-- | boot/sched.zig | 49 | ||||
| -rw-r--r-- | boot/user.zig | 6 |
3 files changed, 56 insertions, 0 deletions
diff --git a/boot/elf.zig b/boot/elf.zig index a6e621c..32acbfe 100644 --- a/boot/elf.zig +++ b/boot/elf.zig @@ -5,4 +5,5 @@ pub const programs = [_]Program{ .{ .name = "primes", .bytes = @embedFile("user_primes") }, .{ .name = "save", .bytes = @embedFile("user_save") }, .{ .name = "chat", .bytes = @embedFile("user_chat") }, + .{ .name = "membrk", .bytes = @embedFile("user_membrk") }, }; diff --git a/boot/sched.zig b/boot/sched.zig index 30d5698..4367fc0 100644 --- a/boot/sched.zig +++ b/boot/sched.zig @@ -3,6 +3,7 @@ const std = @import("std"); const Frame = @import("regs.zig").Frame; const vmm = @import("vmm.zig"); +const pmm = @import("pmm.zig"); const gdt = @import("gdt.zig"); const bridge = @import("bridge"); @@ -11,6 +12,9 @@ const KSTACK_SIZE = 16 * 1024; const MAX_FDS = 8; const MAX_PATH = 128; +// user heap lives here in the gap between the program and the arg/stack +const HEAP_BASE: u64 = 0x1000_0000; + const State = enum { free, ready, dead, blocked }; // file descriptor not "foid decimator" @@ -27,6 +31,8 @@ const Task = struct { cr3: u64 = 0, kstack_top: u64 = 0, // tss rsp0 for this task, 0 for the ring 0 kernel task state: State = .free, + brk_cur: u64 = 0, // the program break, top of the user heap + brk_top: u64 = 0, // first page above the break thats actually mapped }; var tasks: [MAX_TASKS]Task = @splat(.{}); @@ -75,12 +81,55 @@ pub fn spawn(cr3: u64, entry: u64, ustack: u64, arg: u64, arglen: u64) void { .cr3 = cr3, .kstack_top = @intFromPtr(&kstacks[i]) + KSTACK_SIZE, .state = .ready, + .brk_cur = HEAP_BASE, + .brk_top = HEAP_BASE, }; initFds(i); return; } } +const USER_PAGE: u64 = vmm.WRITE | vmm.USER; + +// map heap pages until everything below target is present +// false on oom +fn growHeap(t: *Task, target: u64) bool { + while (t.brk_top < target) { + const phys = pmm.alloc() orelse return false; + // zero it so we never hand ring 3 whatever the fuck was in that frame before + const page: [*]u8 = @ptrFromInt(pmm.physToVirt(phys)); + @memset(page[0..pmm.PAGE_SIZE], 0); + if (!vmm.map(t.cr3, t.brk_top, phys, USER_PAGE)) { + pmm.free(phys); + return false; + } + t.brk_top += pmm.PAGE_SIZE; + } + return true; +} + +// move the break to addr +pub fn brk(addr: u64) i64 { + if (current == 0 or addr < HEAP_BASE) return -1; + const t = &tasks[current]; + if (addr > t.brk_cur and !growHeap(t, addr)) return -1; + t.brk_cur = addr; + return @intCast(addr); +} + +// move the break by increment +pub fn sbrk(increment: i64) i64 { + if (current == 0) return -1; + const t = &tasks[current]; + const old = t.brk_cur; + const want: i64 = @as(i64, @intCast(old)) + increment; + if (want < @as(i64, @intCast(HEAP_BASE))) return -1; + const new_brk: u64 = @intCast(want); + if (new_brk > old and !growHeap(t, new_brk)) return -1; + t.brk_cur = new_brk; + return @intCast(old); +} + // stdin at keyboard fn initFds(i: usize) void { for (&fdtabs[i]) |*f| f.* = .{}; diff --git a/boot/user.zig b/boot/user.zig index 78d20ff..0cda97d 100644 --- a/boot/user.zig +++ b/boot/user.zig @@ -214,6 +214,12 @@ export fn syscallHandler(frame: *SyscallFrame) callconv(.c) void { const src: [*]const u8 = @ptrFromInt(frame.rsi); frame.rax = if (bridge.fsStore(name, src[0..len])) 1 else 0; }, + 10 => { // brk(addr) + frame.rax = @bitCast(sched.brk(frame.rdi)); + }, + 11 => { // sbrk(increment) + frame.rax = @bitCast(sched.sbrk(@bitCast(frame.rdi))); + }, else => serial.write("[syscall] unknown\r\n"), } } |
