aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-07-03 12:37:05 +0200
committerHimbeer <himbeer@disroot.org>2024-07-03 12:37:05 +0200
commitce59ff2e9d848e886c21616c082fe02e1fad680a (patch)
tree9ae95409c26d23655c6fccbe00c5546c3f86640a /src/lib
parent66ef95399a1cbe11ea3e8cf84a6e26aa211bc561 (diff)
process: Add threading and driver handler calling infrastructure
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/interrupts.zig11
-rw-r--r--src/lib/paging.zig5
-rw-r--r--src/lib/process.zig111
-rw-r--r--src/lib/syscall.zig6
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();
+}