diff options
-rw-r--r-- | src/kernel.zig | 4 | ||||
-rw-r--r-- | src/lib/process.zig | 76 | ||||
-rw-r--r-- | src/lib/syscall.zig | 262 | ||||
-rw-r--r-- | src/lib/vfs.zig | 556 |
4 files changed, 1 insertions, 897 deletions
diff --git a/src/kernel.zig b/src/kernel.zig index ff04f89..ea64a43 100644 --- a/src/kernel.zig +++ b/src/kernel.zig @@ -13,7 +13,6 @@ const paging = @import("lib/paging.zig"); const plic = @import("lib/plic.zig"); const process = @import("lib/process.zig"); const userinit = @import("lib/userinit.zig"); -const vfs = @import("lib/vfs.zig"); const Error = error{ HartIdOutOfRange, @@ -176,9 +175,6 @@ fn pagedRun() !noreturn { try w.print("\r\n", .{}); - try vfs.init(allocator); - try w.print("Initialize VFS\r\n", .{}); - try w.print("Start init process\r\n", .{}); var userinit_stream = std.io.fixedBufferStream(userinit.tarball); try process.runInit(allocator, userinit_stream.reader()); diff --git a/src/lib/process.zig b/src/lib/process.zig index c7eddf6..2fbab11 100644 --- a/src/lib/process.zig +++ b/src/lib/process.zig @@ -11,7 +11,6 @@ const rethooks = @import("rethooks.zig"); const sysexchange = @import("sysexchange.zig"); const time = @import("sbi/time.zig"); const trap = @import("trap.zig"); -const vfs = @import("vfs.zig"); const elf = std.elf; @@ -61,49 +60,13 @@ pub const Info = struct { pages: []align(paging.page_size) u8, stack: []align(paging.page_size) u8, pc: usize, - cleanup_hook: ?CleanupHook, - term_hook: ?TermHook, page_table: *paging.Table, state: State, - rds: std.AutoArrayHashMap(usize, vfs.ResourceDescriptor), - - pub const CleanupHook = struct { - pub const CleanupFn = *const fn (proc: *const Info, buffer: []u8, copy: []const u8) void; - - cleanupFn: CleanupFn, - buffer: []u8, - copy: []const u8, - }; - - pub const TermHook = struct { - pub const HookFn = *const fn (context: *anyopaque, proc: *const Info) void; - - hookFn: HookFn, - context: *anyopaque, - }; pub fn satp(self: *const Info) paging.Satp { return self.page_table.satp(self.id); } - pub fn createRdHandle(self: *Info, rd: vfs.ResourceDescriptor) !usize { - if (self.rds.count() == std.math.maxInt(usize)) { - return Error.TooManyResourceDescriptors; - } - - var handle = self.rds.count() + 1; - while (self.rds.contains(handle)) : (handle +%= 1) {} - - try self.rds.put(handle, rd); - return handle; - } - - pub fn destroyRdHandle(self: *Info, handle: usize) void { - if (self.rds.fetchSwapRemove(handle)) |kv| { - kv.value.deinit(); - } - } - pub fn createThread(self: *const Info, allocator: ?std.mem.Allocator) !*Info { const alloc = allocator orelse self.allocator; @@ -121,7 +84,7 @@ pub const Info = struct { return Error.TooManyThreads; }; - const proc = .{ + const proc = Info{ .allocator = alloc, .id = self.id, .thread_id = thread_id, @@ -130,10 +93,8 @@ pub const Info = struct { .stack = stack, .pc = self.pc, .cleanup_hook = null, - .term_hook = null, .page_table = self.page_table, .state = .suspended, - .rds = self.rds, }; const proc_node = try alloc.create(std.DoublyLinkedList(Info).Node); @@ -143,24 +104,6 @@ pub const Info = struct { return &proc_node.data; } - pub fn call(self: *Info, function: usize, args: anytype, cleanup_hook: ?CleanupHook, term_hook: ?TermHook) noreturn { - self.pc = function; - self.cleanup_hook = cleanup_hook; - self.term_hook = term_hook; - self.trap_frame.general_purpose_registers[1] = @intFromPtr(&rethooks.terminate); - inline for (args, 0..) |arg, i| { - if (i >= 6) break; - - const value = usizeFromArg(arg); - self.trap_frame.general_purpose_registers[10 + i] = value; - } - - self.state = .waiting; - schedule() catch |err| { - std.debug.panic("schedule error: {}", .{err}); - }; - } - pub fn terminate( self: *Info, ) void { @@ -185,21 +128,7 @@ pub const Info = struct { if (self.thread_id == 0) { self.page_table.unmap(); paging.free(self.page_table); - paging.free(self.pages); - - for (self.rds.values()) |rd| { - rd.deinit(); - } - self.rds.clearAndFree(); - } - - if (self.cleanup_hook) |cleanup_hook| { - cleanup_hook.cleanupFn(self, cleanup_hook.buffer, cleanup_hook.copy); - } - if (self.term_hook) |term_hook| { - sysexchange.frameReturn(void, &self.trap_frame, vfs.Error.Detached); - term_hook.hookFn(term_hook.context, self); } } @@ -408,11 +337,8 @@ pub fn create(allocator: std.mem.Allocator, elf_buf: []align(@alignOf(elf.Elf64_ .pages = pages, .stack = @ptrCast(stack), .pc = hdr.entry, - .cleanup_hook = null, - .term_hook = null, .page_table = procmem, .state = .waiting, - .rds = std.AutoArrayHashMap(usize, vfs.ResourceDescriptor).init(allocator), }; proc.trap_frame.general_purpose_registers[2] = stack_top; diff --git a/src/lib/syscall.zig b/src/lib/syscall.zig index e4d5254..1497cec 100644 --- a/src/lib/syscall.zig +++ b/src/lib/syscall.zig @@ -10,7 +10,6 @@ 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, @@ -24,17 +23,6 @@ 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, end(proc)), 100013 => ret(null, trap_frame, processId(proc)), 100014 => ret(null, trap_frame, threadId(proc)), @@ -57,256 +45,6 @@ fn uprint(trap_frame: *const trap.Frame) void { 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); -} - // end() void fn end(proc: *process.Info) void { proc.terminate(); diff --git a/src/lib/vfs.zig b/src/lib/vfs.zig deleted file mode 100644 index 26ac977..0000000 --- a/src/lib/vfs.zig +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org> -// -// SPDX-License-Identifier: AGPL-3.0-or-later - -const std = @import("std"); - -const mem = @import("mem.zig"); -const paging = @import("paging.zig"); -const process = @import("process.zig"); -const sysexchange = @import("sysexchange.zig"); - -const Allocator = std.mem.Allocator; - -const Result = sysexchange.Result; -const TermHook = process.Info.TermHook; - -var root: Node = .{ - .data = .{ - .name_ptr = "", - .name_len = 0, - .inode = .{ - .resource = .{ - .tag = .dir, - .data = .{ .dir = undefined }, - }, - .pid = 0, - .options = .{ - .reclaimable = false, - }, - .flags = .{}, - }, - }, -}; - -pub fn treeRoot() *const Tree { - return root.data.inode.resource.data.dir; -} - -pub const Error = error{ - NotFound, - RelativePathNotAllowed, - NotADirectory, - NoAbsoluteContainingDirectory, - TooManyReferences, - ReadNotSupported, - WriteNotSupported, - InUse, - Detached, - Orphaned, - AlreadyExists, - IsAHook, - IsAContainer, - ProvideNotSupported, - RemoveNotSupported, -}; - -// A stream is a resource that provides a shared data stream with a driver. -pub const Stream = extern struct { - readFn: ?ReadFn, - writeFn: ?WriteFn, - - pub const ReadFn = *const fn (ptr: [*]u8, len: usize) callconv(.C) Result(usize); - pub const WriteFn = *const fn (ptr: [*]const u8, len: usize) callconv(.C) Result(usize); -}; - -// A file is a resource that creates a unique data stream with a driver. -pub const File = extern struct { - readFn: ?ReadFn, - writeFn: ?WriteFn, - closeFn: ?CloseFn, - initializer: ?*anyopaque, - - pub const ReadFn = *const fn (context: *FileContext, ptr: [*]u8, len: usize) callconv(.C) Result(usize); - pub const WriteFn = *const fn (context: *FileContext, ptr: [*]const u8, len: usize) callconv(.C) Result(usize); - pub const CloseFn = *const fn (context: *FileContext) callconv(.C) void; -}; - -// A hook is a resource that invokes raw driver callbacks when interacted with. -pub const Hook = extern struct { - callback: Callback, - - pub const Callback = *allowzero const fn (pid: u16, thread_id: usize, data: usize) callconv(.C) Result(usize); -}; - -// A directory hook is a resource that provides other resources via driver callbacks. -pub const DirHook = extern struct { - provideFn: ?ProvideFn, - findFn: FindFn, - listFn: ListFn, - removeFn: ?RemoveFn, - - pub const ProvideFn = *const fn (name_ptr: [*]const u8, name_len: usize, inode: Inode) callconv(.C) Result(void); - pub const FindFn = *allowzero const fn (name_ptr: [*]const u8, name_len: usize) callconv(.C) ?*Inode; - pub const ListFn = *allowzero const fn (entries_ptr: [*]DirEntry, entries_len: usize) callconv(.C) Result(usize); - pub const RemoveFn = *const fn (name_ptr: [*]const u8, name_len: usize) callconv(.C) Result(void); -}; - -pub const FileContext = extern struct { - inner: ?*anyopaque, - initializer: ?*anyopaque, -}; - -pub const ResourceKind = enum(u32) { - stream, - file, - hook, - dir, - dir_hook, -}; - -pub const Resource = extern struct { - tag: ResourceKind, - data: extern union { - stream: Stream, - file: File, - hook: Hook, - dir: *Tree, - dir_hook: DirHook, - }, -}; - -pub const Inode = extern struct { - resource: Resource, - refs: usize = 0, - pid: u16, - options: Options, - flags: Flags, -}; - -pub const Options = extern struct { - reclaimable: bool, -}; - -pub const Flags = extern struct { - detached: bool = false, -}; - -pub const DirEntry = extern struct { - name_ptr: [*]const u8, - name_len: usize, - inode: Inode, - - pub fn name(self: DirEntry) []const u8 { - return self.name_ptr[0..self.name_len]; - } -}; - -pub const Node = std.DoublyLinkedList(DirEntry).Node; - -pub const Tree = struct { - allocator: Allocator, - nodes: std.DoublyLinkedList(DirEntry), - - pub fn init(allocator: Allocator) Tree { - return .{ - .allocator = allocator, - .nodes = std.DoublyLinkedList(DirEntry){}, - }; - } - - pub fn find(self: *const Tree, name: []const u8) ?*Node { - var node = self.nodes.first; - while (node) |current_node| { - if (current_node.next == current_node) break; - - if (std.mem.eql(u8, current_node.data.name(), name)) return current_node; - node = current_node.next; - } - return null; - } - - pub fn provideResource(self: *Tree, entry: DirEntry) !void { - var node = try self.allocator.create(Node); - node.data = entry; - self.nodes.append(node); - } - - pub fn remove(self: *Tree, name: []const u8) !void { - if (self.find(name)) |node| { - if (node.data.inode.refs > 0) return Error.InUse; - self.nodes.remove(node); - } else return Error.NotFound; - } -}; - -pub const OpenResult = union(enum) { - rd: ResourceDescriptor, - data: usize, -}; - -pub const ResourceDescriptor = struct { - inode: *Inode, - context: ?*FileContext, - - pub fn init(allocator: Allocator, inode: *Inode) !ResourceDescriptor { - inode.refs = std.math.add(usize, inode.refs, 1) catch return Error.TooManyReferences; - - if (inode.resource.tag == .file) { - const context = try allocator.create(FileContext); - context.inner = null; - context.initializer = inode.resource.data.file.initializer; - - return .{ - .inode = inode, - .context = context, - }; - } - - return .{ - .inode = inode, - .context = null, - }; - } - - pub fn deinit(self: ResourceDescriptor) void { - self.inode.refs -|= 1; - - if (self.inode.resource.tag == .file) { - if (self.inode.resource.data.file.closeFn) |closeFn| { - if (self.inode.pid == 0) { - closeFn(self.context.?); - return; - } - - const driver = process.latestThread(self.inode.pid).?; - call(driver, closeFn, .{self.context.?}, null, null) catch {}; - } - } - } - - pub fn read(self: ResourceDescriptor, proc: *process.Info, buffer: []u8) !usize { - return readHooked(self, proc, buffer, .{ - .hookFn = crossProcessReturn, - .context = proc, - }); - } - - pub fn readHooked(self: ResourceDescriptor, proc: *process.Info, buffer: []u8, hook: TermHook) !usize { - if (self.inode.flags.detached and self.inode.options.reclaimable) { - return Error.Detached; - } - if (self.inode.flags.detached and !self.inode.options.reclaimable) { - return Error.Orphaned; - } - - return switch (self.inode.resource.tag) { - .stream => self.readStream(self.inode.resource.data.stream, proc, buffer, hook), - .file => self.readFile(self.inode.resource.data.file, proc, buffer, hook), - else => Error.ReadNotSupported, - }; - } - - fn readStream(self: ResourceDescriptor, stream: Stream, proc: *process.Info, buffer: []u8, hook: TermHook) !usize { - const readFn = stream.readFn orelse return Error.ReadNotSupported; - if (self.inode.pid == 0) { - return readFn(buffer.ptr, buffer.len).toErrorUnion(); - } - - const driver = process.latestThread(self.inode.pid).?; - const copy = try driver.copyBuffer(buffer); - - proc.state = .suspended; - try call(driver, readFn, .{ copy.ptr, copy.len }, .{ - .cleanupFn = moveBack, - .buffer = buffer, - .copy = copy, - }, hook); - } - - fn readFile(self: ResourceDescriptor, file: File, proc: *process.Info, buffer: []u8, hook: TermHook) !usize { - const readFn = file.readFn orelse return Error.ReadNotSupported; - if (self.inode.pid == 0) { - return readFn(self.context.?, buffer.ptr, buffer.len).toErrorUnion(); - } - - const driver = process.latestThread(self.inode.pid).?; - const copy = try driver.copyBuffer(buffer); - - proc.state = .suspended; - try call(driver, readFn, .{ self.context.?, copy.ptr, copy.len }, .{ - .cleanupFn = moveBack, - .buffer = buffer, - .copy = copy, - }, hook); - } - - pub fn write(self: ResourceDescriptor, proc: *process.Info, bytes: []const u8) !usize { - return writeHooked(self, proc, bytes, .{ - .hookFn = crossProcessReturn, - .context = proc, - }); - } - - pub fn writeHooked(self: ResourceDescriptor, proc: *process.Info, bytes: []const u8, hook: TermHook) !usize { - if (self.inode.flags.detached and self.inode.options.reclaimable) { - return Error.Detached; - } - if (self.inode.flags.detached and !self.inode.options.reclaimable) { - return Error.Orphaned; - } - - return switch (self.inode.resource.tag) { - .stream => self.writeStream(self.inode.resource.data.stream, proc, bytes, hook), - .file => self.writeFile(self.inode.resource.data.file, proc, bytes, hook), - else => Error.WriteNotSupported, - }; - } - - fn writeStream(self: ResourceDescriptor, stream: Stream, proc: *process.Info, bytes: []const u8, hook: TermHook) !usize { - const writeFn = stream.writeFn orelse return Error.WriteNotSupported; - if (self.inode.pid == 0) { - return writeFn(bytes.ptr, bytes.len).toErrorUnion(); - } - - const driver = process.latestThread(self.inode.pid).?; - const copy = try driver.copyBytes(bytes); - - proc.state = .suspended; - try call(driver, writeFn, .{ copy.ptr, copy.len }, null, hook); - } - - fn writeFile(self: ResourceDescriptor, file: File, proc: *process.Info, bytes: []const u8, hook: TermHook) !usize { - const writeFn = file.writeFn orelse return Error.WriteNotSupported; - if (self.inode.pid == 0) { - return writeFn(self.context.?, bytes.ptr, bytes.len).toErrorUnion(); - } - - const driver = process.latestThread(self.inode.pid).?; - const copy = try driver.copyBytes(bytes); - - proc.state = .suspended; - try call(driver, writeFn, .{ self.context.?, copy.ptr, copy.len }, null, hook); - } -}; - -pub const UserInfo = union(enum) { - rd: ResourceDescriptor, - value: Result(usize), -}; - -pub fn init(allocator: Allocator) !void { - const tree = try allocator.create(Tree); - tree.* = Tree.init(allocator); - root.data.inode.resource.data.dir = tree; -} - -pub fn find(path: []const u8) ?*Inode { - if (!std.fs.path.isAbsolutePosix(path)) return null; - - // Error set is empty. - var it = std.fs.path.ComponentIterator(.posix, u8).init(path) catch unreachable; - var resource = root.data.inode.resource; - while (it.next()) |component| { - const inode = switch (resource.tag) { - .dir => blk: { - const dir = resource.data.dir; - break :blk if (dir.find(component.name)) |entry| &entry.data.inode else null; - }, - .dir_hook => blk: { - // fixme: Doesn't call into U-mode drivers correctly - - const dir_hook = resource.data.dir_hook; - break :blk dir_hook.findFn(component.name.ptr, component.name.len); - }, - else => null, // Not a directory (or directory hook). - } orelse return null; - - if (std.mem.eql(u8, component.path, path)) { - return inode; - } - resource = inode.resource; - } - - // No components, this is the root directory (/). - return &root.data.inode; -} - -fn reclaim(node: *Node, resource: Resource, pid: u16, options: Options) !void { - if (node.data.inode.flags.detached and node.data.inode.options.reclaimable) { - node.data.inode = .{ - .resource = resource, - .pid = pid, - .options = options, - .flags = .{}, - }; - } else return Error.AlreadyExists; -} - -fn provideInDir(dir: *Tree, name: []const u8, resource: Resource, pid: u16, options: Options) !void { - if (dir.find(name)) |node| { - return reclaim(node, resource, pid, options); - } - - return dir.provideResource(.{ - .name_ptr = name.ptr, - .name_len = name.len, - .inode = .{ - .resource = resource, - .pid = pid, - .options = options, - .flags = .{}, - }, - }); -} - -fn provideInDirHook(dir_hook: DirHook, name: []const u8, resource: Resource, pid: u16, options: Options) !void { - // fixme: Check for duplicate resources with the same path - // fixme: Doesn't call into U-mode drivers correctly - - return if (dir_hook.provideFn) |provideFn| - provideFn(name.ptr, name.len, .{ - .resource = resource, - .pid = pid, - .options = options, - .flags = .{}, - }).toErrorUnion() - else - Error.ProvideNotSupported; -} - -pub fn provideResource(path: []const u8, resource: Resource, pid: u16, options: Options) !void { - if (!std.fs.path.isAbsolutePosix(path)) return Error.RelativePathNotAllowed; - - const dirname = std.fs.path.dirnamePosix(path) orelse return Error.NoAbsoluteContainingDirectory; - const basename = std.fs.path.basenamePosix(path); - - const inode = find(dirname) orelse return Error.NotFound; - return switch (inode.resource.tag) { - .dir => provideInDir(inode.resource.data.dir, basename, resource, pid, options), - .dir_hook => provideInDirHook(inode.resource.data.dir_hook, basename, resource, pid, options), - else => Error.NotADirectory, - }; -} - -pub fn provideResourceZ(path_c: [*:0]const u8, resource: Resource, pid: u16, options: Options) !void { - return provideResource(std.mem.sliceTo(path_c, 0), resource, pid, options); -} - -fn callHook(hook: Hook, pid: u16, proc: *process.Info, data: usize) !usize { - if (pid == 0) { - return hook.callback(proc.id, proc.thread_id, data).toErrorUnion(); - } - - const driver = process.latestThread(pid).?; - - proc.state = .suspended; - try call(driver, hook.callback, .{ proc.id, proc.thread_id, data }, null, .{ - .hookFn = crossProcessReturn, - .context = proc, - }); -} - -pub fn open(proc: *process.Info, path: []const u8, data: usize) !OpenResult { - const inode = find(path) orelse return Error.NotFound; - if (inode.resource.tag == .hook) { - const data_out = try callHook(inode.resource.data.hook, inode.pid, proc, data); - return .{ .data = data_out }; - } - - return .{ .rd = try ResourceDescriptor.init(mem.page_allocator, inode) }; -} - -pub fn openZ(proc: *process.Info, path_c: [*:0]const u8, data: usize) !OpenResult { - return open(proc, std.mem.sliceTo(path_c, 0), data); -} - -pub fn openNonHook(path: []const u8) !ResourceDescriptor { - const inode = find(path) orelse return Error.NotFound; - if (inode.resource.tag == .hook) return Error.IsAHook; - - return ResourceDescriptor.init(mem.page_allocator, inode); -} - -pub fn openNonHookZ(path_c: [*:0]const u8) !ResourceDescriptor { - return openNonHook(std.mem.sliceTo(path_c, 0)); -} - -fn listDir(dir: *Tree, entries: []DirEntry) !usize { - paging.setUserMemoryAccess(true); - defer paging.setUserMemoryAccess(false); - - var i: usize = 0; - var next_node = dir.nodes.first; - while (next_node) |node| : (next_node = node.next) { - if (i >= entries.len) break; - entries[i] = node.data; - i += 1; - } - - return @min(dir.nodes.len, entries.len); -} - -fn listDirHook(proc: *process.Info, dir_hook: DirHook, pid: u16, entries: []DirEntry) !usize { - return listDirHookHooked(proc, dir_hook, pid, entries, .{ - .hookFn = crossProcessReturn, - .context = proc, - }); -} - -fn listDirHookHooked(proc: *process.Info, dir_hook: DirHook, pid: u16, entries: []DirEntry, hook: TermHook) !usize { - if (pid == 0) { - return dir_hook.listFn(entries.ptr, entries.len).toErrorUnion(); - } - - const entries_ptr: [*]u8 = @ptrCast(entries.ptr); - const entries_bytes = entries_ptr[0 .. entries.len * @sizeOf(DirEntry)]; - - const driver = process.latestThread(pid).?; - paging.setUserMemoryAccess(true); - const copy = try driver.copyBuffer(entries_bytes); - paging.setUserMemoryAccess(false); - - proc.state = .suspended; - try call(driver, dir_hook.listFn, .{ copy.ptr, copy.len }, .{ - .cleanupFn = moveBack, - .buffer = entries_bytes, - .copy = copy, - }, hook); -} - -pub fn list(proc: *process.Info, path: []const u8, entries: []DirEntry) !usize { - const inode = find(path) orelse return Error.NotFound; - return switch (inode.resource.tag) { - .dir => listDir(inode.resource.data.dir, entries), - .dir_hook => listDirHook(proc, inode.resource.data.dir_hook, inode.pid, entries), - else => Error.NotADirectory, - }; -} - -pub fn listZ(proc: *process.Info, path_c: [*:0]const u8, entries: []DirEntry) !usize { - return list(proc, std.mem.sliceTo(path_c, 0), entries); -} - -fn call(proc: *process.Info, function: *const anyopaque, args: anytype, cleanup_hook: ?process.Info.CleanupHook, term_hook: ?process.Info.TermHook) !noreturn { - const callback_thread = try proc.createThread(null); - callback_thread.call(@intFromPtr(function), args, cleanup_hook, term_hook); -} - -fn moveBack(driver: *const process.Info, buffer: []u8, copy: []const u8) void { - paging.setUserMemoryAccess(true); - @memcpy(buffer, copy); - paging.setUserMemoryAccess(false); - - var addr = @intFromPtr(copy.ptr); - const limit = addr + copy.len; - while (addr < limit) : (addr += paging.page_size) { - driver.page_table.unmapEntry(addr); - } -} - -fn crossProcessReturn(context: *anyopaque, driver: *const process.Info) void { - const proc: *process.Info = @alignCast(@ptrCast(context)); - proc.trap_frame.general_purpose_registers[10] = driver.trap_frame.general_purpose_registers[12]; - proc.trap_frame.general_purpose_registers[11] = driver.trap_frame.general_purpose_registers[13]; - proc.allowResume(); -} |