From 5d4a723a5f157c0ed38a348099a600d1d21c3c2b Mon Sep 17 00:00:00 2001 From: hachem Date: Tue, 6 Oct 2026 12:49:46 +0200 Subject: feat: gils core utils and other shit --- Makefile | 8 +- boot/boot.zig | 21 ++- boot/idt.zig | 2 +- boot/regs.zig | 2 +- boot/sched.zig | 2 +- bridge/bridge.zig | 35 +++-- bridge/hosts.zig | 83 +++++++++++- bridge/serial.zig | 4 + build.zig | 32 ++--- gils/cat.rv | 10 ++ gils/cp.rv | 15 +++ gils/ed.rv | 190 ++++++++++++++++++++++++++ gils/gils.rv | 50 +++++++ gils/head.rv | 27 ++++ gils/ls.rv | 10 ++ gils/mkdir.rv | 5 + gils/mv.rv | 14 ++ gils/rm.rv | 6 + gils/tail.rv | 29 ++++ gils/touch.rv | 9 ++ gils/tree.rv | 35 +++++ gils/wc.rv | 22 +++ kernel/console.rv | 111 ++++++++------- kernel/fs.rv | 393 +++++++++++++++++++++++++++++------------------------- kernel/input.rv | 34 ++--- kernel/main.rv | 2 +- kernel/proc.rv | 75 ++++++----- kernel/seed.rv | 13 ++ kernel/shell.rv | 281 +++++++++++++++----------------------- kernel/vfs.rv | 38 ++---- kernel/vmm.rv | 14 +- revo | 2 +- 32 files changed, 1044 insertions(+), 530 deletions(-) create mode 100644 gils/cat.rv create mode 100644 gils/cp.rv create mode 100644 gils/ed.rv create mode 100644 gils/gils.rv create mode 100644 gils/head.rv create mode 100644 gils/ls.rv create mode 100644 gils/mkdir.rv create mode 100644 gils/mv.rv create mode 100644 gils/rm.rv create mode 100644 gils/tail.rv create mode 100644 gils/touch.rv create mode 100644 gils/tree.rv create mode 100644 gils/wc.rv diff --git a/Makefile b/Makefile index 57a6554..5b44c0b 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ # make kernel : just compile the kernel ELF # make clean : remove build output (kernel, ISO, caches) # make distclean : also remove Limine's built host tool +# +# WARNING: DO NOT FUCK W/ THE \ DELIMETERS THEY ARE MADE TO BE PRETTY +# ON OUTPUT, NOT PRETTY IN CODE. IF YOU DONT LIKE IT, GO +# SHOKE ON A TIT ZIG ?= zig QEMU ?= qemu-system-x86_64 @@ -22,9 +26,7 @@ kernel: limine/limine: $(MAKE) -C limine -# WARNING: DO NOT FUCK W/ THE \ DELIMETERS THEY ARE MADE TO BE PRETTY -# ON OUTPUT, NOT PRETTY IN CODE. IF YOU DONT LIKE IT, GO -# SHOKE ON A TIT + iso: kernel limine/limine rm -rf iso_root mkdir -p iso_root/boot/limine iso_root/EFI/BOOT diff --git a/boot/boot.zig b/boot/boot.zig index fff8a6b..4918881 100644 --- a/boot/boot.zig +++ b/boot/boot.zig @@ -60,6 +60,9 @@ fn kPhysToVirt(p: u64) u64 { fn kAllocFrame() u64 { return pmm.alloc() orelse 0; } +fn kAllocContig(n: u64) u64 { + return pmm.allocContig(@intCast(n)) orelse 0; +} fn kCreateAddrspace() u64 { return vmm.createAddressSpace() orelse 0; } @@ -91,7 +94,7 @@ fn kMemmapLen(i: u64) u64 { } fn kMemmapKind(i: u64) u64 { - return if (memmapEntry(i)) |e| @intFromEnum(e.type) else 0; + return if (memmapEntry(i)) |e| @backingInt(e.type) else 0; } fn kMemFree() u64 { @@ -161,8 +164,23 @@ fn kTaskState(i: u64) u64 { return sched.taskState(i); } +// limine doesnt fully enable SSE the only reason i need this +// is because Zig 0.17 bullshit +inline fn enableSse() void { + asm volatile ( + \\ mov %%cr0, %%rax + \\ and $0xfffb, %%ax + \\ or $0x2, %%ax + \\ mov %%rax, %%cr0 + \\ mov %%cr4, %%rax + \\ or $0x600, %%rax + \\ mov %%rax, %%cr4 + ::: .{ .rax = true, .memory = true }); +} + // entry(_start) export fn _start() callconv(.c) noreturn { + enableSse(); serial.init(); serial.write("if you see this, it means revel didn't shit the bed\r\n"); @@ -217,6 +235,7 @@ export fn _start() callconv(.c) noreturn { .disk_sectors = &kDiskSectors, .disk_read = &kDiskRead, .disk_write = &kDiskWrite, + .alloc_contig = &kAllocContig, }; // hand the kernel heap + framebuffer + low level ops to revo and bring up the vm diff --git a/boot/idt.zig b/boot/idt.zig index 0ffd116..15e6588 100644 --- a/boot/idt.zig +++ b/boot/idt.zig @@ -29,7 +29,7 @@ const Idtr = packed struct { base: u64, }; -var idt = [_]Gate{@bitCast(@as(u128, 0))} ** 256; +var idt: [256]Gate = @splat(@bitCast(@as(u128, 0))); var idtr: Idtr = undefined; const names = [_][]const u8{ diff --git a/boot/regs.zig b/boot/regs.zig index 7b2bb88..3a30860 100644 --- a/boot/regs.zig +++ b/boot/regs.zig @@ -24,7 +24,7 @@ pub const Frame = extern struct { ss: u64, }; -// push/pop the 15 gprs in frames exact order +// push/pop the 15 gprs in frames exact order // (rax pushed first -> highest field, r15 last -> at rsp = &frame) pub const PUSH_GPRS = \\push %rax diff --git a/boot/sched.zig b/boot/sched.zig index 1842309..383b40d 100644 --- a/boot/sched.zig +++ b/boot/sched.zig @@ -84,7 +84,7 @@ pub fn taskMax() u64 { } pub fn taskState(i: u64) u64 { if (i >= MAX_TASKS) return 0; - return @intFromEnum(tasks[i].state); + return @backingInt(tasks[i].state); } var proc_exited_flag: bool = false; diff --git a/bridge/bridge.zig b/bridge/bridge.zig index 71f7023..87548eb 100644 --- a/bridge/bridge.zig +++ b/bridge/bridge.zig @@ -15,16 +15,25 @@ pub const KernelOps = hosts.KernelOps; // the vm outlives boot() now, so we juts make this hoe static var g_vm: ?*revo.VM = null; -const init_program = @embedFile("k_font") ++ "\n" ++ - @embedFile("k_console") ++ "\n" ++ - @embedFile("k_vmm") ++ "\n" ++ - @embedFile("k_fs") ++ "\n" ++ - @embedFile("k_vfs") ++ "\n" ++ - @embedFile("k_seed") ++ "\n" ++ - @embedFile("k_proc") ++ "\n" ++ - @embedFile("k_shell") ++ "\n" ++ - @embedFile("k_input") ++ "\n" ++ - @embedFile("k_main"); +// NOTE: ORDER MATTERS +const init_program = blk: { + var s: []const u8 = @embedFile("k_font") ++ "\n" ++ + @embedFile("k_console") ++ "\n" ++ + @embedFile("k_vmm") ++ "\n" ++ + @embedFile("k_fs") ++ "\n" ++ + @embedFile("k_vfs") ++ "\n" ++ + @embedFile("k_seed") ++ "\n"; + + // gils shit + const gils_embedded = .{ "gils", "ed" }; + for (gils_embedded) |g| s = s ++ @embedFile("k_gils_" ++ g) ++ "\n"; + + s = s ++ @embedFile("k_proc") ++ "\n" ++ + @embedFile("k_shell") ++ "\n" ++ + @embedFile("k_input") ++ "\n" ++ + @embedFile("k_main"); + break :blk s; +}; pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb, ops: ?KernelOps) void { serial.setSink(out); @@ -50,7 +59,7 @@ pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb, ops: ?KernelOps) void }; out("bridge: revo VM up\r\n"); - hosts.registerPrimitives(vm) catch { + hosts.passShitToRevo(vm) catch { out("bridge: registering primitives failed\r\n"); return; }; @@ -64,7 +73,7 @@ pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb, ops: ?KernelOps) void .err => |failure| { out("bridge: compile error: "); var sw = serial.SinkWriter.init(); - revo.lang.renderError(alloc, &sw.interface, .{ .name = "kernel", .text = init_program }, failure) catch {}; + revo.lang.renderError(alloc, &sw.interface, .{ .name = "kernel", .text = init_program }, failure, .{}) catch {}; out("\r\n"); return; }, @@ -86,7 +95,7 @@ pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb, ops: ?KernelOps) void .err => |failure| { out("\r\nbridge: runtime error: "); var sw = serial.SinkWriter.init(); - failure.render(alloc, &sw.interface, init_program) catch {}; + failure.render(alloc, &sw.interface, init_program, false) catch {}; out("\r\n"); }, } diff --git a/bridge/hosts.zig b/bridge/hosts.zig index e329316..51362dc 100644 --- a/bridge/hosts.zig +++ b/bridge/hosts.zig @@ -8,6 +8,22 @@ const Data = convert.Data; const argInt = convert.argInt; const emit = serial.emit; +// gils shit +const Script = struct { name: []const u8, src: []const u8 }; +const scripts = [_]Script{ + .{ .name = "ls", .src = @embedFile("k_gils_ls") }, + .{ .name = "tree", .src = @embedFile("k_gils_tree") }, + .{ .name = "cat", .src = @embedFile("k_gils_cat") }, + .{ .name = "rm", .src = @embedFile("k_gils_rm") }, + .{ .name = "mkdir", .src = @embedFile("k_gils_mkdir") }, + .{ .name = "touch", .src = @embedFile("k_gils_touch") }, + .{ .name = "cp", .src = @embedFile("k_gils_cp") }, + .{ .name = "mv", .src = @embedFile("k_gils_mv") }, + .{ .name = "wc", .src = @embedFile("k_gils_wc") }, + .{ .name = "head", .src = @embedFile("k_gils_head") }, + .{ .name = "tail", .src = @embedFile("k_gils_tail") }, +}; + // the actual framebuffer, the data layout feels pretty self explanatory pub const Fb = struct { ptr: [*]u8, @@ -50,6 +66,7 @@ pub const KernelOps = struct { disk_sectors: *const fn () u64, disk_read: *const fn (u64, u64) u64, // lba, phys buf -> 1 ok / 0 fail disk_write: *const fn (u64, u64) u64, // lba, phys buf -> 1 ok / 0 fail + alloc_contig: *const fn (u64) u64, // n contiguous pages -> phys base, 0 on fail }; var g_kops: ?KernelOps = null; @@ -337,7 +354,63 @@ fn hostDiskWrite(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { } // pass shit to revo -pub fn registerPrimitives(vm: *revo.VM) !void { +fn hostAllocContig(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const n = argInt(u64, args, 0) orelse return HostResult.other("alloc_contig: bad count"); + return HostResult.data(Data.new.num(kops().alloc_contig(n))); +} + +// pass shit to revo +fn hostBlitStr(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const phys = argInt(u64, args, 0) orelse return HostResult.other("blit_str: bad addr"); + const sid = args[1].asString() orelse return HostResult.other("blit_str: not a string"); + const bytes = vm.stringValue(sid); + const dst: [*]u8 = @ptrFromInt(kops().phys_to_virt(phys)); + @memcpy(dst[0..bytes.len], bytes); + return HostResult.data(Data.new.num(bytes.len)); +} + +// pass shit to revo +fn hostPhysToStr(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const phys = argInt(u64, args, 0) orelse return HostResult.other("phys_to_str: bad addr"); + const len = argInt(usize, args, 1) orelse return HostResult.other("phys_to_str: bad len"); + const src: [*]const u8 = @ptrFromInt(kops().phys_to_virt(phys)); + return HostResult.data(try vm.ownValueString(src[0..len])); +} + +// pass shit to revo +fn hostScriptCount(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(scripts.len)); +} + +// pass shit to revo +fn hostScriptName(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const i = argInt(usize, args, 0) orelse return HostResult.other("script_name: bad index"); + if (i >= scripts.len) return HostResult.other("script_name: out of range"); + return HostResult.data(try vm.ownValueString(scripts[i].name)); +} + +// pass shit to revo +fn hostScriptSrc(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const i = argInt(usize, args, 0) orelse return HostResult.other("script_src: bad index"); + if (i >= scripts.len) return HostResult.other("script_src: out of range"); + return HostResult.data(try vm.ownValueString(scripts[i].src)); +} + +// pass shit to revo +fn hostEvalScript(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const sid = args[0].asString() orelse return HostResult.other("eval_script: not a string"); + const src = vm.stringValue(sid); + const res = revo.run.runModule(vm, "