// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: AGPL-3.0-or-later const std = @import("std"); const instructions = @import("instructions.zig"); const paging = @import("paging.zig"); const process = @import("process.zig"); const sysexchange = @import("sysexchange.zig"); const trap = @import("trap.zig"); const vfs = @import("vfs.zig"); pub const Error = error{ Unimplemented, }; pub const HandleError = error{ UnknownSyscall, }; pub fn handler(proc: *process.Info, trap_frame: *trap.Frame) !void { switch (trap_frame.general_purpose_registers[17]) { 100000 => uprint(trap_frame), 100001 => open(proc, trap_frame), 100002 => close(proc, trap_frame), 100003 => provideStream(proc, trap_frame), 100004 => provideFile(proc, trap_frame), 100005 => provideHook(proc, trap_frame), 100006 => mkdir(proc, trap_frame), 100007 => provideDirHook(proc, trap_frame), 100008 => remove(trap_frame), 100009 => read(proc, trap_frame), 100010 => write(proc, trap_frame), 100011 => terminate(proc), else => return HandleError.UnknownSyscall, } } // uprint(str_addr: usize, len: usize) void fn uprint(trap_frame: *const trap.Frame) void { const procmem: *paging.Table = @ptrFromInt(instructions.satp.read().ppn << 12); const paddr = procmem.translate(trap_frame.general_purpose_registers[10]).?; const str_ptr: [*]const u8 = @ptrFromInt(paddr); const str = str_ptr[0..trap_frame.general_purpose_registers[11]]; const w = @import("Console.zig").autoChoose().?.writer(); w.print("User message: {s}\r\n", .{str}) catch unreachable; } // open(path_c: [*:0]const u8, data: usize) Result(usize) // fixme: Kernel panic if null pointer fn open(proc: *process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const data = trap_frame.general_purpose_registers[11]; const rd = vfs.openZ(proc, path_c, proc.id, data) catch |err| { sysexchange.frameReturn(usize, trap_frame, err); return; }; const maybe_handle = proc.createRdHandle(rd); sysexchange.frameReturn(null, trap_frame, maybe_handle); } // close(handle: usize) void fn close(proc: *process.Info, trap_frame: *const trap.Frame) void { const handle = trap_frame.general_purpose_registers[10]; proc.destroyRdHandle(handle); } // provideStream( // path_c: [*:0]const u8, // fixme: Kernel panic if null pointer // options: *const vfs.Options, // fixme: Kernel panic if null pointer // readFn: ?vfs.Stream.ReadFn, // writeFn: ?vfs.Stream.WriteFn, // ) Result(void) fn provideStream(proc: *const process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const options: *const vfs.Options = @ptrFromInt(trap_frame.general_purpose_registers[11]); const readFn: ?vfs.Stream.ReadFn = @ptrFromInt(trap_frame.general_purpose_registers[12]); const writeFn: ?vfs.Stream.WriteFn = @ptrFromInt(trap_frame.general_purpose_registers[13]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .stream, .data = .{ .stream = .{ .readFn = readFn, .writeFn = writeFn, } }, }, proc.id, options.*)); } // provideFile( // path_c: [*:0]const u8, // fixme: Kernel panic if null pointer // options: *const vfs.Options, // fixme: Kernel panic if null pointer // openFn: vfs.File.OpenFn, // readFn: ?vfs.File.ReadFn, // writeFn: ?vfs.File.WriteFn, // closeFn: ?vfs.File.CloseFn, // ) Result(void) fn provideFile(proc: *const process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const options: *const vfs.Options = @ptrFromInt(trap_frame.general_purpose_registers[11]); const openFn: vfs.File.OpenFn = @ptrFromInt(trap_frame.general_purpose_registers[12]); const readFn: ?vfs.File.ReadFn = @ptrFromInt(trap_frame.general_purpose_registers[13]); const writeFn: ?vfs.File.WriteFn = @ptrFromInt(trap_frame.general_purpose_registers[14]); const closeFn: ?vfs.File.CloseFn = @ptrFromInt(trap_frame.general_purpose_registers[15]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .file, .data = .{ .file = .{ .openFn = openFn, .readFn = readFn, .writeFn = writeFn, .closeFn = closeFn, .initializer = null, } }, }, proc.id, options.*)); } // provideHook( // path_c: [*:0]const u8, // fixme: Kernel panic if null pointer // options: *const vfs.Options, // fixme: Kernel panic if null pointer, // callback: vfs.Hook.Callback, // ) Result(void) fn provideHook(proc: *const process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const options: *const vfs.Options = @ptrFromInt(trap_frame.general_purpose_registers[11]); const callback: vfs.Hook.Callback = @ptrFromInt(trap_frame.general_purpose_registers[12]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .hook, .data = .{ .hook = .{ .callback = callback, } }, }, proc.id, options.*)); } // mkdir( // path_c: [*:0]const u8, // fixme: Kernel panic if null pointer // ) Result(void) fn mkdir(proc: *const process.Info, trap_frame: *trap.Frame) void { const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const allocator = vfs.treeRoot().allocator; const tree = allocator.create(vfs.Tree) catch |err| { sysexchange.frameReturn(void, trap_frame, err); return; }; tree.* = vfs.Tree.init(allocator); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .dir, .data = .{ .dir = tree }, }, proc.id, .{ .reclaimable = false, })); } // provideDirHook( // path_c: [*:0]const u8, // fixme: Kernel panic if null pointer // options: *const vfs.Options, // fixme: Kernel panic if null pointer // provideFn: vfs.DirHook.ProvideFn, // findFn: vfs.DirHook.FindFn, // removeFn: vfs.DirHook.RemoveFn, // ) Result(void) fn provideDirHook(proc: *const process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const options: *const vfs.Options = @ptrFromInt(trap_frame.general_purpose_registers[11]); const provideFn: vfs.DirHook.ProvideFn = @ptrFromInt(trap_frame.general_purpose_registers[12]); const findFn: vfs.DirHook.FindFn = @ptrFromInt(trap_frame.general_purpose_registers[13]); const removeFn: vfs.DirHook.RemoveFn = @ptrFromInt(trap_frame.general_purpose_registers[14]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .dir_hook, .data = .{ .dir_hook = .{ .provideFn = provideFn, .findFn = findFn, .removeFn = removeFn, } }, }, proc.id, options.*)); } // remove(path_c: [*:0]const u8) Result(void) // fixme: Kernel panic if null pointer fn remove(trap_frame: *trap.Frame) void { const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); const path = std.mem.sliceTo(path_c, 0); const dirname = std.fs.path.dirnamePosix(path) orelse "/"; if (vfs.find(dirname)) |inode| { const basename = std.fs.path.basenamePosix(path); switch (inode.resource.tag) { .dir => { const dir = inode.resource.data.dir; sysexchange.frameReturn(null, trap_frame, dir.remove(basename)); }, .dir_hook => { const dir_hook = inode.resource.data.dir_hook; const result = dir_hook.removeFn(basename.ptr, basename.len); sysexchange.frameReturnResult(void, trap_frame, result); }, else => sysexchange.frameReturn(void, trap_frame, vfs.Error.NotADirectory), } } else { sysexchange.frameReturn(void, trap_frame, vfs.Error.NotFound); } } // read(handle: usize, buffer: [*]u8, len: usize) Result(usize) fn read(proc: *process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const handle = trap_frame.general_purpose_registers[10]; const buffer: [*]u8 = @ptrFromInt(trap_frame.general_purpose_registers[11]); const len = trap_frame.general_purpose_registers[12]; const rd = proc.rds.get(handle) orelse { sysexchange.frameReturn(usize, trap_frame, process.Error.BadRdHandle); return; }; const result = rd.read(proc, buffer[0..len]) catch |err| blk: { if (err == vfs.Error.Orphaned) { proc.destroyRdHandle(handle); } break :blk err; }; sysexchange.frameReturn(usize, trap_frame, result); } // write(handle: usize, bytes: [*]const u8, len: usize) Result(usize) fn write(proc: *process.Info, trap_frame: *trap.Frame) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const handle = trap_frame.general_purpose_registers[10]; const bytes: [*]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[11]); const len = trap_frame.general_purpose_registers[12]; const rd = proc.rds.get(handle) orelse { sysexchange.frameReturn(usize, trap_frame, process.Error.BadRdHandle); return; }; const result = rd.write(proc, bytes[0..len]) catch |err| blk: { if (err == vfs.Error.Orphaned) { proc.destroyRdHandle(handle); } break :blk err; }; sysexchange.frameReturn(usize, trap_frame, result); } // terminate() noreturn fn terminate(proc: *process.Info) noreturn { proc.terminate(); process.schedule() catch |err| { std.debug.panic("Unable to schedule because all processes are terminated: {any}", .{err}); }; }