diff options
Diffstat (limited to 'src/lib/vfs.zig')
-rw-r--r-- | src/lib/vfs.zig | 94 |
1 files changed, 88 insertions, 6 deletions
diff --git a/src/lib/vfs.zig b/src/lib/vfs.zig index a687c43..2a36586 100644 --- a/src/lib/vfs.zig +++ b/src/lib/vfs.zig @@ -4,14 +4,22 @@ const std = @import("std"); +const interrupts = @import("interrupts.zig"); const paging = @import("paging.zig"); const process = @import("process.zig"); const sysexchange = @import("sysexchange.zig"); +const io = std.io; const mem = std.mem; var root: Node = .{ .data = .{ .name = "", .inode = .{ .resource = .{ .tag = .dir, .data = .{ .dir = undefined } }, .pid = 0 } } }; +pub const gib = 1024 * mib; +pub const mib = 1024 * kib; +pub const kib = 1024; + +pub const max_buffer_size = 512 * gib; + pub fn treeRoot() *const Tree { return root.data.inode.resource.data.dir; } @@ -25,6 +33,7 @@ pub const Error = error{ ReadNotSupported, WriteNotSupported, InUse, + HookAccessFromKernel, }; // A stream is a resource that provides a shared data stream with a driver. @@ -53,8 +62,9 @@ pub const File = extern struct { // A hook is a resource that invokes raw driver callbacks when interacted with. pub const Hook = extern struct { callback: Callback, + context: ?*anyopaque, - pub const Callback = *allowzero const fn (pid: u16, data: usize) callconv(.C) sysexchange.Result(usize); + pub const Callback = *allowzero const fn (context: ?*anyopaque, pid: u16, data: usize) callconv(.C) sysexchange.Result(usize); }; // A directory hook is a resource that provides other resources via driver callbacks. @@ -139,6 +149,9 @@ pub const Tree = struct { pub const ResourceDescriptor = struct { inode: *Inode, + pub const Reader = io.Reader(ResourceDescriptor, anyerror, readK); + pub const Writer = io.Writer(ResourceDescriptor, anyerror, writeK); + pub fn init(inode: *Inode) !ResourceDescriptor { inode.refs = std.math.add(usize, inode.refs, 1) catch return Error.TooManyReferences; return .{ @@ -194,6 +207,58 @@ pub const ResourceDescriptor = struct { }; } + fn readK(self: ResourceDescriptor, buffer: []u8) !usize { + return switch (self.inode.resource.tag) { + .stream => { + const stream = self.inode.resource.data.stream; + + const driver = process.latestThread(self.inode.pid).?; + const copy = try driver.copyBuffer(buffer); + + const readFn = stream.readFn orelse return Error.ReadNotSupported; + interrupts.trap_frame.save(); + try call(driver, readFn, .{ copy.ptr, copy.len }, .{ + .cleanupFn = moveBack, + .buffer = buffer, + .copy = copy, + }, .{ + .hookFn = kernelReturn, + .context = &interrupts.trap_frame, + }); + }, + .hook => Error.ReadNotSupported, + else => Error.ReadNotSupported, + }; + } + + fn writeK(self: ResourceDescriptor, bytes: []const u8) !usize { + return switch (self.inode.resource.tag) { + .stream => { + const stream = self.inode.resource.data.stream; + + const driver = process.latestThread(self.inode.pid).?; + const copy = try driver.copyBytes(bytes); + + const writeFn = stream.writeFn orelse return Error.WriteNotSupported; + interrupts.trap_frame.save(); + try call(driver, writeFn, .{ copy.ptr, copy.len }, null, .{ + .hookFn = kernelReturn, + .context = &interrupts.trap_frame, + }); + }, + .hook => Error.WriteNotSupported, + else => Error.WriteNotSupported, + }; + } + + pub fn reader(self: ResourceDescriptor) Reader { + return .{ .context = self }; + } + + pub fn writer(self: ResourceDescriptor) Writer { + return .{ .context = self }; + } + fn moveBack(driver: *const process.Info, buffer: []u8, copy: []const u8) void { paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); @@ -283,25 +348,27 @@ pub fn provideResourceZ(path_c: [*:0]const u8, resource: Resource, pid: u16) !vo return provideResource(mem.sliceTo(path_c, 0), resource, pid); } -pub fn open(proc: *process.Info, path: []const u8, pid: u16, data: usize) !ResourceDescriptor { +pub fn open(proc: ?*process.Info, path: []const u8, pid: u16, data: usize) !ResourceDescriptor { const inode = find(path) orelse return Error.NotFound; return switch (inode.resource.tag) { .hook => { const hook = inode.resource.data.hook; + const procinfo = proc orelse return Error.HookAccessFromKernel; + const driver = process.latestThread(inode.pid).?; - proc.state = .suspended; + procinfo.state = .suspended; try call(driver, hook.callback, .{ pid, data }, null, .{ .hookFn = crossProcessReturn, - .context = proc, + .context = procinfo, }); }, else => ResourceDescriptor.init(inode), }; } -pub fn openZ(proc: *process.Info, path_c: [*:0]const u8, pid: u16, data: usize) !ResourceDescriptor { +pub fn openZ(proc: ?*process.Info, path_c: [*:0]const u8, pid: u16, data: usize) !ResourceDescriptor { return open(proc, mem.sliceTo(path_c, 0), pid, data); } @@ -310,11 +377,26 @@ fn call(proc: *process.Info, function: *const anyopaque, args: anytype, cleanup_ callback_thread.call(@intFromPtr(function), args, cleanup_hook, term_hook); } -fn crossProcessReturn(context: *anyopaque, driver: *const process.Info) void { +fn crossProcessReturn(context: *anyopaque, driver: *const process.Info) !usize { const proc: *process.Info = @alignCast(@ptrCast(context)); proc.trap_frame.general_purpose_registers[10] = driver.trap_frame.general_purpose_registers[10]; proc.trap_frame.general_purpose_registers[11] = driver.trap_frame.general_purpose_registers[11]; proc.pc += 4; // Skip ecall instruction proc.state = .waiting; // Scheduler is called by the "terminate" syscall after two layers of returning. + return 0; +} + +fn kernelReturn(_: *anyopaque, driver: *const process.Info) !usize { + interrupts.trap_frame.general_purpose_registers[10] = driver.trap_frame.general_purpose_registers[10]; + interrupts.trap_frame.general_purpose_registers[11] = driver.trap_frame.general_purpose_registers[11]; + interrupts.trap_frame.load(); + + const result = sysexchange.Result(usize){ + .value = interrupts.trap_frame.general_purpose_registers[10], + .status = @enumFromInt(interrupts.trap_frame.general_purpose_registers[11]), + }; + + const ret = result.toErrorUnion(); + return ret; } |