// 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 { const ret = sysexchange.frameReturn; switch (trap_frame.general_purpose_registers[17]) { 100000 => ret(null, trap_frame, uprint(trap_frame)), 100001 => ret(null, trap_frame, open(proc, trap_frame)), 100002 => ret(null, trap_frame, close(proc, trap_frame)), 100003 => ret(null, trap_frame, provideStream(proc, trap_frame)), 100004 => ret(null, trap_frame, provideFile(proc, trap_frame)), 100005 => ret(null, trap_frame, provideHook(proc, trap_frame)), 100006 => ret(null, trap_frame, mkdir(proc, trap_frame)), 100007 => ret(null, trap_frame, provideDirHook(proc, trap_frame)), 100008 => ret(null, trap_frame, remove(trap_frame)), 100009 => ret(null, trap_frame, read(proc, trap_frame)), 100010 => ret(null, trap_frame, write(proc, trap_frame)), 100011 => ret(null, trap_frame, list(proc, trap_frame)), 100012 => ret(null, trap_frame, terminate(proc)), 100013 => ret(null, trap_frame, processId(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 result = vfs.openZ(proc, path_c, data) catch |err| { sysexchange.frameReturn(usize, trap_frame, err); return; }; switch (result) { .rd => |rd| { const maybe_handle = proc.createRdHandle(rd); sysexchange.frameReturn(usize, trap_frame, maybe_handle); }, .data => |result_data| { sysexchange.frameReturn(null, trap_frame, result_data); }, } } // 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 // readFn: ?vfs.File.ReadFn, // writeFn: ?vfs.File.WriteFn, // closeFn: ?vfs.File.CloseFn, // initializer: ?*anyopaque, // ) 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 readFn: ?vfs.File.ReadFn = @ptrFromInt(trap_frame.general_purpose_registers[12]); const writeFn: ?vfs.File.WriteFn = @ptrFromInt(trap_frame.general_purpose_registers[13]); const closeFn: ?vfs.File.CloseFn = @ptrFromInt(trap_frame.general_purpose_registers[14]); const initializer: ?*anyopaque = @ptrFromInt(trap_frame.general_purpose_registers[15]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .file, .data = .{ .file = .{ .readFn = readFn, .writeFn = writeFn, .closeFn = closeFn, .initializer = initializer, } }, }, 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, // listFn:vfs.DirHook.ListFn, // 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 listFn: vfs.DirHook.ListFn = @ptrFromInt(trap_frame.general_purpose_registers[14]); const removeFn: vfs.DirHook.RemoveFn = @ptrFromInt(trap_frame.general_purpose_registers[15]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ .tag = .dir_hook, .data = .{ .dir_hook = .{ .provideFn = provideFn, .findFn = findFn, .listFn = listFn, .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); removeFrom(trap_frame, inode, basename); } else { sysexchange.frameReturn(void, trap_frame, vfs.Error.NotFound); } } fn removeFrom(trap_frame: *trap.Frame, inode: *vfs.Inode, basename: []const u8) void { 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 removeFn = dir_hook.removeFn orelse { sysexchange.frameReturn(void, trap_frame, vfs.Error.RemoveNotSupported); return; }; const result = removeFn(basename.ptr, basename.len); sysexchange.frameReturnResult(void, trap_frame, result); }, else => sysexchange.frameReturn(void, trap_frame, vfs.Error.NotADirectory), } } // 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); } // list(path_c: [*:0]const u8, entries: [*]vfs.DirEntry, len: usize) Result(usize) fn list(proc: *process.Info, trap_frame: *trap.Frame) void { // fixme: Kernel panic if null pointer const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); // fixme: Kernel panic if null pointer const entries: [*]vfs.DirEntry = @ptrFromInt(trap_frame.general_purpose_registers[11]); const len = trap_frame.general_purpose_registers[12]; const result = vfs.listZ(proc, path_c, entries[0..len]); sysexchange.frameReturn(null, trap_frame, result); } // terminate() void fn terminate(proc: *process.Info) void { proc.terminate(); process.schedule() catch |err| { std.debug.panic("Unable to schedule because all processes are terminated: {}", .{err}); }; } // processId() usize fn processId(proc: *process.Info) usize { return proc.id; }