From 52dab7fc85d877e80e3032f0afabae906adebad8 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 4 Oct 2026 16:03:42 +0200 Subject: feat: fs tree and free list alloc --- bridge/hosts.zig | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 bridge/hosts.zig (limited to 'bridge/hosts.zig') diff --git a/bridge/hosts.zig b/bridge/hosts.zig new file mode 100644 index 0000000..e329316 --- /dev/null +++ b/bridge/hosts.zig @@ -0,0 +1,380 @@ +const std = @import("std"); +const revo = @import("revo"); +const serial = @import("serial.zig"); +const convert = @import("convert.zig"); + +const HostResult = convert.HostResult; +const Data = convert.Data; +const argInt = convert.argInt; +const emit = serial.emit; + +// the actual framebuffer, the data layout feels pretty self explanatory +pub const Fb = struct { + ptr: [*]u8, + width: usize, + height: usize, + pitch: usize, +}; +var g_fb: ?Fb = null; + +pub fn setFb(fb: ?Fb) void { + g_fb = fb; +} + +pub const KernelOps = struct { + phys_to_virt: *const fn (u64) u64, + alloc_frame: *const fn () u64, // 0 on failure + create_addrspace: *const fn () u64, // pml4 phys, 0 on failure (raw 64 bit entry copy incl. possible nx bit -> not f64 representable) + spawn: *const fn (u64, u64, u64, u64, u64) void, // pml4, entry, ustack, arg_ptr, arg_len -> queue a ring 3 task + deliver_input: *const fn (u8) bool, // hand a byte to a read()-blocked task, false if none + proc_exited: *const fn () bool, // true once after a process exits (consumed) + serial_next: *const fn () i64, // next mirrored serial byte, or -1 if empty + + // read only state for revo to present (mem/uptime/ps/dump_memmap): + memmap_count: *const fn () u64, + memmap_base: *const fn (u64) u64, + memmap_len: *const fn (u64) u64, + memmap_kind: *const fn (u64) u64, + mem_free: *const fn () u64, + mem_total: *const fn () u64, + + uptime: *const fn () u64, + proc_count: *const fn () u64, + prog_count: *const fn () u64, + prog_phys: *const fn (u64) u64, + prog_name: *const fn (u64) []const u8, + prog_size: *const fn (u64) u64, + task_max: *const fn () u64, + task_state: *const fn (u64) u64, + + 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 +}; +var g_kops: ?KernelOps = null; + +pub fn setKops(ops: ?KernelOps) void { + g_kops = ops; +} + +fn kops() KernelOps { + return g_kops.?; +} + +// pass shit to revo +fn hostOutb(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const port = argInt(u16, args, 0) orelse return HostResult.other("outb: port out of range"); + const val = argInt(u8, args, 1) orelse return HostResult.other("outb: value out of range"); + asm volatile ("outb %[v], %[p]" + : + : [v] "{al}" (val), + [p] "N{dx}" (port), + ); + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostInb(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const port = argInt(u16, args, 0) orelse return HostResult.other("inb: port out of range"); + const val = asm volatile ("inb %[p], %[r]" + : [r] "={al}" (-> u8), + : [p] "N{dx}" (port), + ); + return HostResult.data(Data.new.num(val)); +} + +// pass shit to revo +fn hostPuts(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const string_id = args[0].asString() orelse return HostResult.other("puts: not a string"); + emit(vm.stringValue(string_id)); + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostFbWidth(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(if (g_fb) |fb| fb.width else 0)); +} + +// pass shit to revo +fn hostFbHeight(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(if (g_fb) |fb| fb.height else 0)); +} + +// color is 0xrrggbb in the usual limine 32 bpp layout +// pass shit to revo +fn hostFillRect(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const fb = g_fb orelse return HostResult.data(Data.new.nil()); // no screen, no op + const x = argInt(usize, args, 0) orelse return HostResult.other("fill_rect: bad x"); + const y = argInt(usize, args, 1) orelse return HostResult.other("fill_rect: bad y"); + const w = argInt(usize, args, 2) orelse return HostResult.other("fill_rect: bad w"); + const h = argInt(usize, args, 3) orelse return HostResult.other("fill_rect: bad h"); + const color = argInt(u32, args, 4) orelse return HostResult.other("fill_rect: bad color"); + + var yy = y; + const y_end = @min(y + h, fb.height); + const x_end = @min(x + w, fb.width); + while (yy < y_end) : (yy += 1) { + const row = fb.ptr + yy * fb.pitch; + var xx = x; + while (xx < x_end) : (xx += 1) { + const px: *align(1) u32 = @ptrCast(row + xx * 4); + px.* = color; + } + } + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostFbScroll(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const fb = g_fb orelse return HostResult.data(Data.new.nil()); + const dy = argInt(usize, args, 0) orelse return HostResult.other("fb_scroll: bad dy"); + const color = argInt(u32, args, 1) orelse return HostResult.other("fb_scroll: bad color"); + if (dy == 0 or dy >= fb.height) return HostResult.data(Data.new.nil()); + const move_bytes = dy * fb.pitch; + const total = fb.height * fb.pitch; + std.mem.copyForwards(u8, fb.ptr[0 .. total - move_bytes], fb.ptr[move_bytes..total]); + var y: usize = fb.height - dy; + while (y < fb.height) : (y += 1) { + const line = fb.ptr + y * fb.pitch; + var x: usize = 0; + while (x < fb.width) : (x += 1) { + const px: *align(1) u32 = @ptrCast(line + x * 4); + px.* = color; + } + } + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostMemRead(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const phys = argInt(u64, args, 0) orelse return HostResult.other("mem_read: bad addr"); + const size = argInt(u64, args, 1) orelse return HostResult.other("mem_read: bad size"); + const virt = kops().phys_to_virt(phys); + const val: u64 = switch (size) { + 1 => @as(*const u8, @ptrFromInt(virt)).*, + 2 => @as(*align(1) const u16, @ptrFromInt(virt)).*, + 4 => @as(*align(1) const u32, @ptrFromInt(virt)).*, + 8 => @as(*align(1) const u64, @ptrFromInt(virt)).*, + else => return HostResult.other("mem_read: size must be 1/2/4/8"), + }; + return HostResult.data(Data.new.num(val)); +} + +// pass shit to revo +fn hostMemCopy(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const dst = kops().phys_to_virt(argInt(u64, args, 0) orelse return HostResult.other("mem_copy: bad dst")); + const src = kops().phys_to_virt(argInt(u64, args, 1) orelse return HostResult.other("mem_copy: bad src")); + const len = argInt(usize, args, 2) orelse return HostResult.other("mem_copy: bad len"); + @memcpy(@as([*]u8, @ptrFromInt(dst))[0..len], @as([*]const u8, @ptrFromInt(src))[0..len]); + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostMemZero(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const dst = kops().phys_to_virt(argInt(u64, args, 0) orelse return HostResult.other("mem_zero: bad addr")); + const len = argInt(usize, args, 1) orelse return HostResult.other("mem_zero: bad len"); + @memset(@as([*]u8, @ptrFromInt(dst))[0..len], 0); + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostMemWrite(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const phys = argInt(u64, args, 0) orelse return HostResult.other("mem_write: bad addr"); + const val = argInt(u64, args, 1) orelse return HostResult.other("mem_write: bad val"); + const size = argInt(u64, args, 2) orelse return HostResult.other("mem_write: bad size"); + const virt = kops().phys_to_virt(phys); + switch (size) { + 1 => @as(*u8, @ptrFromInt(virt)).* = @truncate(val), + 2 => @as(*align(1) u16, @ptrFromInt(virt)).* = @truncate(val), + 4 => @as(*align(1) u32, @ptrFromInt(virt)).* = @truncate(val), + 8 => @as(*align(1) u64, @ptrFromInt(virt)).* = val, + else => return HostResult.other("mem_write: size must be 1/2/4/8"), + } + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostInvlpg(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const virt = argInt(u64, args, 0) orelse return HostResult.other("invlpg: bad addr"); + asm volatile ("invlpg (%[v])" + : + : [v] "r" (virt), + : .{ .memory = true }); + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostFrameAlloc(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().alloc_frame())); +} + +// pass shit to revo +fn hostAsCreate(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().create_addrspace())); +} + +// pass shit to revo +fn hostProcRun(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const as = argInt(u64, args, 0) orelse return HostResult.other("proc_run: bad as"); + const entry = argInt(u64, args, 1) orelse return HostResult.other("proc_run: bad entry"); + const ustack = argInt(u64, args, 2) orelse return HostResult.other("proc_run: bad ustack"); + const arg = argInt(u64, args, 3) orelse return HostResult.other("proc_run: bad arg"); + const arglen = argInt(u64, args, 4) orelse return HostResult.other("proc_run: bad arglen"); + kops().spawn(as, entry, ustack, arg, arglen); + return HostResult.data(Data.new.nil()); +} + +// pass shit to revo +fn hostStdinDeliver(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const ch = argInt(u8, args, 0) orelse return HostResult.other("stdin_deliver: bad char"); + return HostResult.data(Data.new.num(if (kops().deliver_input(ch)) @as(f64, 1) else 0)); +} + +// pass shit to revo +fn hostProcExited(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(if (kops().proc_exited()) @as(f64, 1) else 0)); +} + +// pass shit to revo +fn hostSerialNext(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().serial_next())); +} + +// pass shit to revo +fn hostProgCount(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().prog_count())); +} + +// pass shit to revo +fn hostProgPhys(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const i = argInt(u64, args, 0) orelse return HostResult.other("prog_phys: bad index"); + return HostResult.data(Data.new.num(kops().prog_phys(i))); +} + +// pass shit to revo +fn hostProgName(args: []const revo.Value, vm: *revo.VM) anyerror!HostResult { + const i = argInt(u64, args, 0) orelse return HostResult.other("prog_name: bad index"); + return HostResult.data(try vm.ownValueString(kops().prog_name(i))); +} + +// pass shit to revo +fn hostProgSize(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const i = argInt(u64, args, 0) orelse return HostResult.other("prog_size: bad index"); + return HostResult.data(Data.new.num(kops().prog_size(i))); +} + +// pass shit to revo +fn hostTaskMax(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().task_max())); +} + +// pass shit to revo +fn hostTaskState(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const i = argInt(u64, args, 0) orelse return HostResult.other("task_state: bad index"); + return HostResult.data(Data.new.num(kops().task_state(i))); +} + +// pass shit to revo +fn hostMemmapCount(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().memmap_count())); +} + +// pass shit to revo +fn hostMemmapBase(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const index = argInt(u64, args, 0) orelse return HostResult.other("memmap_base: bad index"); + return HostResult.data(Data.new.num(kops().memmap_base(index))); +} + +// pass shit to revo +fn hostMemmapLen(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const index = argInt(u64, args, 0) orelse return HostResult.other("memmap_len: bad index"); + return HostResult.data(Data.new.num(kops().memmap_len(index))); +} + +// pass shit to revo +fn hostMemmapKind(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const index = argInt(u64, args, 0) orelse return HostResult.other("memmap_kind: bad index"); + return HostResult.data(Data.new.num(kops().memmap_kind(index))); +} + +// pass shit to revo +fn hostMemFree(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().mem_free())); +} + +// pass shit to revo +fn hostMemTotal(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().mem_total())); +} + +// pass shit to revo +fn hostUptime(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().uptime())); +} + +// pass shit to revo +fn hostProcCount(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().proc_count())); +} + +// pass shit to revo +fn hostDiskSectors(_: []const revo.Value, _: *revo.VM) anyerror!HostResult { + return HostResult.data(Data.new.num(kops().disk_sectors())); +} + +// pass shit to revo +fn hostDiskRead(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const lba = argInt(u64, args, 0) orelse return HostResult.other("disk_read: bad lba"); + const phys = argInt(u64, args, 1) orelse return HostResult.other("disk_read: bad phys"); + return HostResult.data(Data.new.num(kops().disk_read(lba, phys))); +} + +// pass shit to revo +fn hostDiskWrite(args: []const revo.Value, _: *revo.VM) anyerror!HostResult { + const lba = argInt(u64, args, 0) orelse return HostResult.other("disk_write: bad lba"); + const phys = argInt(u64, args, 1) orelse return HostResult.other("disk_write: bad phys"); + return HostResult.data(Data.new.num(kops().disk_write(lba, phys))); +} + +// pass shit to revo +pub fn registerPrimitives(vm: *revo.VM) !void { + const define = revo.baselib.host.define; + const T = revo.baselib.host.ParamType; + try vm.registerGlobal("outb", try vm.installHost("outb", define(&[_]T{ .number, .number }, hostOutb))); + try vm.registerGlobal("inb", try vm.installHost("inb", define(&[_]T{.number}, hostInb))); + try vm.registerGlobal("serial_puts", try vm.installHost("serial_puts", define(&[_]T{.string}, hostPuts))); + try vm.registerGlobal("fb_width", try vm.installHost("fb_width", define(&[_]T{}, hostFbWidth))); + try vm.registerGlobal("fb_height", try vm.installHost("fb_height", define(&[_]T{}, hostFbHeight))); + try vm.registerGlobal("fill_rect", try vm.installHost("fill_rect", define(&[_]T{ .number, .number, .number, .number, .number }, hostFillRect))); + try vm.registerGlobal("fb_scroll", try vm.installHost("fb_scroll", define(&[_]T{ .number, .number }, hostFbScroll))); + + try vm.registerGlobal("mem_read", try vm.installHost("mem_read", define(&[_]T{ .number, .number }, hostMemRead))); + try vm.registerGlobal("mem_copy", try vm.installHost("mem_copy", define(&[_]T{ .number, .number, .number }, hostMemCopy))); + try vm.registerGlobal("mem_zero", try vm.installHost("mem_zero", define(&[_]T{ .number, .number }, hostMemZero))); + try vm.registerGlobal("mem_write", try vm.installHost("mem_write", define(&[_]T{ .number, .number, .number }, hostMemWrite))); + try vm.registerGlobal("invlpg", try vm.installHost("invlpg", define(&[_]T{.number}, hostInvlpg))); + try vm.registerGlobal("frame_alloc", try vm.installHost("frame_alloc", define(&[_]T{}, hostFrameAlloc))); + try vm.registerGlobal("as_create", try vm.installHost("as_create", define(&[_]T{}, hostAsCreate))); + try vm.registerGlobal("proc_run", try vm.installHost("proc_run", define(&[_]T{ .number, .number, .number, .number, .number }, hostProcRun))); + try vm.registerGlobal("stdin_deliver", try vm.installHost("stdin_deliver", define(&[_]T{.number}, hostStdinDeliver))); + try vm.registerGlobal("proc_exited", try vm.installHost("proc_exited", define(&[_]T{}, hostProcExited))); + try vm.registerGlobal("serial_next", try vm.installHost("serial_next", define(&[_]T{}, hostSerialNext))); + try vm.registerGlobal("prog_count", try vm.installHost("prog_count", define(&[_]T{}, hostProgCount))); + try vm.registerGlobal("prog_phys", try vm.installHost("prog_phys", define(&[_]T{.number}, hostProgPhys))); + try vm.registerGlobal("prog_name", try vm.installHost("prog_name", define(&[_]T{.number}, hostProgName))); + try vm.registerGlobal("prog_size", try vm.installHost("prog_size", define(&[_]T{.number}, hostProgSize))); + try vm.registerGlobal("task_max", try vm.installHost("task_max", define(&[_]T{}, hostTaskMax))); + try vm.registerGlobal("task_state", try vm.installHost("task_state", define(&[_]T{.number}, hostTaskState))); + try vm.registerGlobal("memmap_count", try vm.installHost("memmap_count", define(&[_]T{}, hostMemmapCount))); + try vm.registerGlobal("memmap_base", try vm.installHost("memmap_base", define(&[_]T{.number}, hostMemmapBase))); + try vm.registerGlobal("memmap_len", try vm.installHost("memmap_len", define(&[_]T{.number}, hostMemmapLen))); + try vm.registerGlobal("memmap_kind", try vm.installHost("memmap_kind", define(&[_]T{.number}, hostMemmapKind))); + try vm.registerGlobal("mem_free", try vm.installHost("mem_free", define(&[_]T{}, hostMemFree))); + try vm.registerGlobal("mem_total", try vm.installHost("mem_total", define(&[_]T{}, hostMemTotal))); + try vm.registerGlobal("uptime", try vm.installHost("uptime", define(&[_]T{}, hostUptime))); + try vm.registerGlobal("proc_count", try vm.installHost("proc_count", define(&[_]T{}, hostProcCount))); + + try vm.registerGlobal("disk_sectors", try vm.installHost("disk_sectors", define(&[_]T{}, hostDiskSectors))); + try vm.registerGlobal("disk_read", try vm.installHost("disk_read", define(&[_]T{ .number, .number }, hostDiskRead))); + try vm.registerGlobal("disk_write", try vm.installHost("disk_write", define(&[_]T{ .number, .number }, hostDiskWrite))); +} -- cgit v1.3