aboutsummaryrefslogtreecommitdiff
path: root/src/lib/vfs.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/vfs.zig')
-rw-r--r--src/lib/vfs.zig94
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;
}