diff options
author | Himbeer <himbeer@disroot.org> | 2024-07-03 12:37:05 +0200 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2024-07-03 12:37:05 +0200 |
commit | ce59ff2e9d848e886c21616c082fe02e1fad680a (patch) | |
tree | 9ae95409c26d23655c6fccbe00c5546c3f86640a /src/lib | |
parent | 66ef95399a1cbe11ea3e8cf84a6e26aa211bc561 (diff) |
process: Add threading and driver handler calling infrastructure
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/interrupts.zig | 11 | ||||
-rw-r--r-- | src/lib/paging.zig | 5 | ||||
-rw-r--r-- | src/lib/process.zig | 111 | ||||
-rw-r--r-- | src/lib/syscall.zig | 6 |
4 files changed, 122 insertions, 11 deletions
diff --git a/src/lib/interrupts.zig b/src/lib/interrupts.zig index e11c693..3b1884d 100644 --- a/src/lib/interrupts.zig +++ b/src/lib/interrupts.zig @@ -114,7 +114,7 @@ export fn handleTrap(epc: usize, tval: usize, cause_bits: usize, frame: *trap.Fr process.list.last.?.data.pc = epc; process.list.last.?.data.state = .waiting; - schedule() catch |err| { + process.schedule() catch |err| { std.debug.panic("Hart {d}: Unable to schedule next process: {any}", .{ frame.hart_id, err }); }; } @@ -281,15 +281,6 @@ export fn supervisorTrapVector() align(4) callconv(.Naked) noreturn { ); } -fn schedule() !noreturn { - if (process.next()) |next| { - try time.interruptInMillis(process.schedule_interval_millis); - process.switchTo(next); - } - - return process.Error.EmptySchedule; -} - pub fn init(hart_id: usize) void { trap_frame = .{ .general_purpose_registers = [_]usize{0} ** 32, diff --git a/src/lib/paging.zig b/src/lib/paging.zig index 87a5c0c..8c2e521 100644 --- a/src/lib/paging.zig +++ b/src/lib/paging.zig @@ -12,6 +12,8 @@ const hwinfo = @import("hwinfo.zig"); // Defined by linker script. pub const text_start = @extern(*anyopaque, .{ .name = "_text_start" }); pub const text_end = @extern(*anyopaque, .{ .name = "_text_end" }); +pub const rethooks_start = @extern(*anyopaque, .{ .name = "_rethooks_start" }); +pub const rethooks_end = @extern(*anyopaque, .{ .name = "_rethooks_end" }); pub const rodata_start = @extern(*anyopaque, .{ .name = "_rodata_start" }); pub const rodata_end = @extern(*anyopaque, .{ .name = "_rodata_end" }); pub const data_start = @extern(*anyopaque, .{ .name = "_data_start" }); @@ -335,7 +337,7 @@ pub const Table = struct { // This function does not deallocate memory pages mapped by the provided table // or any of its (recursive) children. pub fn unmap(table: *Table) void { - for (table.entries) |entry| { + for (&table.entries) |*entry| { if (entry.isValid() and !entry.isLeaf()) { // This cast is safe because the only field of a Table is its entries. const lowerLevelTable: *Table = @ptrFromInt(entry.mappingAddr()); @@ -423,6 +425,7 @@ pub const Table = struct { pub fn mapKernel(root: *Table) !void { try root.identityMapRange(@intFromPtr(text_start), @intFromPtr(text_end), EntryFlags.readExec); + try root.identityMapRange(@intFromPtr(rethooks_start), @intFromPtr(rethooks_end), EntryFlags.userReadExec); try root.identityMapRange(@intFromPtr(rodata_start), @intFromPtr(rodata_end), EntryFlags.readOnly); try root.identityMapRange(@intFromPtr(data_start), @intFromPtr(data_end), EntryFlags.readWrite); try root.identityMapRange(@intFromPtr(bss_start), @intFromPtr(bss_end), EntryFlags.readWrite); diff --git a/src/lib/process.zig b/src/lib/process.zig index cc0f7d2..28dc04b 100644 --- a/src/lib/process.zig +++ b/src/lib/process.zig @@ -45,14 +45,19 @@ pub const State = enum(u8) { waiting, active, sleeping, + suspended, terminated, }; pub const Info = struct { + allocator: std.mem.Allocator, id: u16, + thread_id: usize, trap_frame: trap.Frame, + pages: []u8, stack: *[num_stack_pages * paging.page_size]u8, pc: usize, + terminate_ra: ?usize, page_table: *paging.Table, state: State, rds: std.AutoArrayHashMap(usize, vfs.ResourceDescriptor), @@ -85,17 +90,119 @@ pub const Info = struct { kv.value.deinit(); } } + + pub fn createThread(self: *const Info, allocator: ?std.mem.Allocator) !*Info { + const alloc = allocator orelse self.allocator; + + var trap_frame = std.mem.zeroInit(trap.Frame, .{}); + + const stack = try paging.zeroedAlloc(num_stack_pages); + errdefer paging.free(stack); + + const stack_top = @intFromPtr(stack.ptr) + num_stack_pages * paging.page_size; + try self.page_table.identityMapRange(@intFromPtr(stack.ptr), stack_top, paging.EntryFlags.userReadWrite); + + trap_frame.general_purpose_registers[2] = stack_top; + + const thread_id = std.math.add(usize, self.thread_id, 1) catch { + return ProcessError.TooManyThreads; + }; + + const proc = .{ + .allocator = alloc, + .id = self.id, + .thread_id = thread_id, + .trap_frame = trap_frame, + .stack = stack, + .pc = self.pc, + .page_table = self.page_table, + .state = .suspended, + .rds = self.rds, + }; + + const proc_node = try alloc.create(std.DoublyLinkedList(Info).Node); + proc_node.data = proc; + list.prepend(proc_node); + + return &proc_node.data; + } + + pub fn call(self: *Info, function: usize, args: anytype) noreturn { + const Container = struct { + fn terminate() linksection(".rethooks") callconv(.Naked) noreturn { + // Syscall #100011 is "terminate". + asm volatile ( + \\ li a7, 100011 + \\ ecall + ); + } + }; + + self.pc = function; + self.terminate_ra = @returnAddress(); + self.trap_frame.general_purpose_registers[1] = &Container.terminate; + for (args, 0..6) |arg, i| { + self.trap_frame.general_purpose_registers[10 + i] = arg; + } + + self.state = .waiting; + schedule() catch |err| { + std.debug.panic("Unable to schedule thread: {any}", .{err}); + }; + } + + pub fn terminate( + self: *Info, + ) void { + var node = list.first; + while (node) |proc_node| : (node = proc_node.next) { + if (self.shouldRemove(&proc_node.data)) { + list.remove(proc_node); + self.allocator.destroy(proc_node); + } + } + + paging.free(self.stack); + + if (self.thread_id == 0) { + self.page_table.unmap(); + paging.free(self.page_table); + + paging.free(self.pages); + + self.rds.deinit(); + } + } + + fn shouldRemove(self: *const Info, candidate: *const Info) bool { + return candidate.id == self.id and self.shouldRemoveThread(candidate); + } + + fn shouldRemoveThread(self: *const Info, candidate: *const Info) bool { + return candidate.thread_id == self.thread_id or self.thread_id == 0; + } }; pub fn next() ?*Info { if (list.popFirst()) |info| { list.append(info); + // fixme: Suspending or sleeping init process causes infinite recursion. + if (info.data.state != .waiting) return next(); return &info.data; } return null; } +pub fn schedule() !noreturn { + if (next()) |proc| { + try time.interruptInMillis(schedule_interval_millis); + switchTo(proc); + } + + return Error.EmptySchedule; +} + pub fn switchTo(proc: *Info) noreturn { proc.state = .active; @@ -215,10 +322,14 @@ pub fn create(allocator: std.mem.Allocator, elf_buf: []align(@alignOf(elf.Elf64_ try procmem.identityMapRange(@intFromPtr(stack.ptr), stack_top, paging.EntryFlags.userReadWrite); var proc = Info{ + .allocator = allocator, .id = next_pid, + .thread_id = 0, .trap_frame = std.mem.zeroInit(trap.Frame, .{}), + .pages = pages, .stack = @ptrCast(stack), .pc = hdr.entry, + .terminate_ra = null, .page_table = procmem, .state = .waiting, .rds = std.AutoArrayHashMap(usize, vfs.ResourceDescriptor).init(allocator), diff --git a/src/lib/syscall.zig b/src/lib/syscall.zig index ee47a6b..63f9afd 100644 --- a/src/lib/syscall.zig +++ b/src/lib/syscall.zig @@ -32,6 +32,7 @@ pub fn handler(proc: *process.Info, trap_frame: *trap.Frame) !void { 100008 => remove(trap_frame), 100009 => read(proc, trap_frame), 100010 => write(proc, trap_frame), + 100011 => terminate(proc), else => return Error.UnknownSyscall, } } @@ -218,3 +219,8 @@ fn write(proc: *const process.Info, trap_frame: *trap.Frame) void { }; sysexchange.frameReturnResult(usize, trap_frame, rd.write(bytes[0..len])); } + +// terminate() noreturn +fn terminate(proc: *process.Info) void { + proc.terminate(); +} |