diff options
author | Himbeer <himbeer@disroot.org> | 2024-07-11 12:02:38 +0200 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2024-07-11 12:02:38 +0200 |
commit | 13da66a7d65c3ab7960bef7f8914c5a09ae2d5bd (patch) | |
tree | c4294be7cbf18d4e7de9b75acf9bdddae0287628 | |
parent | ea070c02fb0894f264e815d37527ae0e9b67759c (diff) |
resources: Add initial process creation API
-rw-r--r-- | src/kernel.zig | 2 | ||||
-rw-r--r-- | src/lib/process.zig | 4 | ||||
-rw-r--r-- | src/lib/resources.zig | 50 | ||||
-rw-r--r-- | src/lib/syscall.zig | 2 | ||||
-rw-r--r-- | src/lib/trap.zig | 72 | ||||
-rw-r--r-- | src/lib/vfs.zig | 94 |
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; } |