aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-07-11 12:02:38 +0200
committerHimbeer <himbeer@disroot.org>2024-07-11 12:02:38 +0200
commit13da66a7d65c3ab7960bef7f8914c5a09ae2d5bd (patch)
treec4294be7cbf18d4e7de9b75acf9bdddae0287628
parentea070c02fb0894f264e815d37527ae0e9b67759c (diff)
resources: Add initial process creation API
-rw-r--r--src/kernel.zig2
-rw-r--r--src/lib/process.zig4
-rw-r--r--src/lib/resources.zig50
-rw-r--r--src/lib/syscall.zig2
-rw-r--r--src/lib/trap.zig72
-rw-r--r--src/lib/vfs.zig94
6 files changed, 214 insertions, 10 deletions
diff --git a/src/kernel.zig b/src/kernel.zig
index 820ee3e..72588dd 100644
--- a/src/kernel.zig
+++ b/src/kernel.zig
@@ -201,7 +201,7 @@ fn pagedRun() !noreturn {
try vfs.init(allocator);
try w.print("Initialize VFS\r\n", .{});
- try resources.provideBuiltin();
+ try resources.provideBuiltin(&allocator);
try w.print("Provide builtin resources\r\n", .{});
try w.print("Start init process\r\n", .{});
diff --git a/src/lib/process.zig b/src/lib/process.zig
index aa3568c..7a39d10 100644
--- a/src/lib/process.zig
+++ b/src/lib/process.zig
@@ -71,7 +71,7 @@ pub const Info = struct {
};
pub const TermHook = struct {
- hookFn: *const fn (context: *anyopaque, proc: *const Info) void,
+ hookFn: *const fn (context: *anyopaque, proc: *const Info) anyerror!usize,
context: *anyopaque,
};
@@ -197,7 +197,7 @@ pub const Info = struct {
cleanup_hook.cleanupFn(self, cleanup_hook.buffer, cleanup_hook.copy);
}
if (self.term_hook) |term_hook| {
- term_hook.hookFn(term_hook.context, self);
+ _ = term_hook.hookFn(term_hook.context, self) catch 0;
}
}
diff --git a/src/lib/resources.zig b/src/lib/resources.zig
index 0dc6d9f..55a964a 100644
--- a/src/lib/resources.zig
+++ b/src/lib/resources.zig
@@ -4,22 +4,56 @@
const std = @import("std");
+const process = @import("process.zig");
+const syscall = @import("syscall.zig");
const sysexchange = @import("sysexchange.zig");
const userinit = @import("userinit.zig");
const vfs = @import("vfs.zig");
+const Allocator = std.mem.Allocator;
const Result = sysexchange.Result;
const io = std.io;
const tar = std.tar;
const File = tar.Iterator(io.FixedBufferStream([]const u8).Reader).File;
+const procfs = struct {
+ fn new(context: ?*anyopaque, _: u16, data: usize) callconv(.C) Result(usize) {
+ const allocator: *const Allocator = @alignCast(@ptrCast(context.?));
+
+ const ptr: ?[*:0]const u8 = @ptrFromInt(data);
+ const path = ptr orelse {
+ return Result(usize).fromAnyTypeOrError(syscall.Error.InvalidArgument);
+ };
+
+ const rd = vfs.openZ(null, path, 0, 0) catch |err| {
+ return Result(usize).fromAnyTypeOrError(err);
+ };
+ defer rd.deinit();
+
+ const alignment = @alignOf(std.elf.Elf64_Ehdr);
+
+ var exe_list = std.ArrayListAligned(u8, alignment).init(allocator.*);
+ defer exe_list.deinit();
+ rd.reader().readAllArrayListAligned(alignment, &exe_list, vfs.max_buffer_size) catch |err| {
+ return Result(usize).fromAnyTypeOrError(err);
+ };
+
+ const proc = process.create(allocator.*, exe_list.items) catch |err| {
+ return Result(usize).fromAnyTypeOrError(err);
+ };
+
+ return Result(usize).fromAnyTypeOrError(proc.id);
+ }
+};
+
pub const Error = error{
NoTarFileInitializer,
};
-pub fn provideBuiltin() !void {
+pub fn provideBuiltin(allocator: *const Allocator) !void {
try provideUserinit();
+ try provideProcess(allocator);
}
fn provideUserinit() !void {
@@ -47,6 +81,20 @@ fn provideUserinit() !void {
}
}
+fn provideProcess(allocator: *const Allocator) !void {
+ try addDir("/proc");
+
+ try vfs.provideResource("/proc/new", .{
+ .tag = .hook,
+ .data = .{
+ .hook = .{
+ .callback = procfs.new,
+ .context = @constCast(allocator),
+ },
+ },
+ }, 0);
+}
+
fn addFile(path: []const u8, file: File) !void {
const allocator = vfs.treeRoot().allocator;
diff --git a/src/lib/syscall.zig b/src/lib/syscall.zig
index 661c98d..c92dad3 100644
--- a/src/lib/syscall.zig
+++ b/src/lib/syscall.zig
@@ -13,6 +13,7 @@ const vfs = @import("vfs.zig");
pub const Error = error{
Unimplemented,
+ InvalidArgument,
};
pub const HandleError = error{
@@ -138,6 +139,7 @@ fn provideHook(proc: *const process.Info, trap_frame: *trap.Frame) void {
.tag = .hook,
.data = .{ .hook = .{
.callback = callback,
+ .context = null,
} },
}, proc.id));
}
diff --git a/src/lib/trap.zig b/src/lib/trap.zig
index 4460cfd..672c45f 100644
--- a/src/lib/trap.zig
+++ b/src/lib/trap.zig
@@ -8,4 +8,76 @@ pub const Frame = extern struct {
satp: usize, // Offset: 512
stack_pointer: *allowzero u8, // Offset: 520
hart_id: usize, // Offset: 528
+
+ pub inline fn save(self: *Frame) void {
+ _ = self;
+ asm volatile (
+ \\ sd x1, 8(a0)
+ \\ sd x2, 16(a0)
+ \\ sd x3, 24(a0)
+ \\ sd x4, 32(a0)
+ \\ sd x5, 40(a0)
+ \\ sd x6, 48(a0)
+ \\ sd x7, 56(a0)
+ \\ sd x8, 64(a0)
+ \\ sd x9, 72(a0)
+ \\ sd x10, 80(a0)
+ \\ sd x11, 88(a0)
+ \\ sd x12, 96(a0)
+ \\ sd x13, 104(a0)
+ \\ sd x14, 112(a0)
+ \\ sd x15, 120(a0)
+ \\ sd x16, 128(a0)
+ \\ sd x17, 136(a0)
+ \\ sd x18, 144(a0)
+ \\ sd x19, 152(a0)
+ \\ sd x20, 160(a0)
+ \\ sd x21, 168(a0)
+ \\ sd x22, 176(a0)
+ \\ sd x23, 184(a0)
+ \\ sd x24, 192(a0)
+ \\ sd x25, 200(a0)
+ \\ sd x26, 208(a0)
+ \\ sd x27, 216(a0)
+ \\ sd x28, 224(a0)
+ \\ sd x29, 232(a0)
+ \\ sd x30, 240(a0)
+ );
+ }
+
+ pub inline fn load(self: *const Frame) void {
+ _ = self;
+ asm volatile (
+ \\ ld x1, 8(a0)
+ \\ ld x2, 16(a0)
+ \\ ld x3, 24(a0)
+ \\ ld x4, 32(a0)
+ \\ ld x5, 40(a0)
+ \\ ld x6, 48(a0)
+ \\ ld x7, 56(a0)
+ \\ ld x8, 64(a0)
+ \\ ld x9, 72(a0)
+ \\ ld x10, 80(a0)
+ \\ ld x11, 88(a0)
+ \\ ld x12, 96(a0)
+ \\ ld x13, 104(a0)
+ \\ ld x14, 112(a0)
+ \\ ld x15, 120(a0)
+ \\ ld x16, 128(a0)
+ \\ ld x17, 136(a0)
+ \\ ld x18, 144(a0)
+ \\ ld x19, 152(a0)
+ \\ ld x20, 160(a0)
+ \\ ld x21, 168(a0)
+ \\ ld x22, 176(a0)
+ \\ ld x23, 184(a0)
+ \\ ld x24, 192(a0)
+ \\ ld x25, 200(a0)
+ \\ ld x26, 208(a0)
+ \\ ld x27, 216(a0)
+ \\ ld x28, 224(a0)
+ \\ ld x29, 232(a0)
+ \\ ld x30, 240(a0)
+ );
+ }
};
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;
}