// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: AGPL-3.0-or-later const std = @import("std"); const Console = @import("Console.zig"); const TrapFrame = @import("TrapFrame.zig"); const channel = @import("channel.zig"); const hwinfo = @import("hwinfo.zig"); const mem = @import("mem.zig"); const paging = @import("paging.zig"); const process = @import("process.zig"); const riscv = @import("riscv.zig"); pub const ArgumentError = error{ ZeroAddressSupplied, OutOfRange }; pub const HandleError = error{UnknownSyscall}; pub fn handler(proc: *process.Info, trap_frame: *TrapFrame) !void { switch (trap_frame.general_purpose_registers[17]) { 100000 => trap_frame.setReturnValue(errorName(trap_frame)), 100001 => trap_frame.setReturnValue(consoleWrite(trap_frame)), 100002 => trap_frame.setReturnValue(launch(trap_frame)), 100003 => trap_frame.setReturnValue(end(proc)), 100004 => trap_frame.setReturnValue(terminate(proc, trap_frame)), 100005 => trap_frame.setReturnValue(processId(proc)), 100006 => trap_frame.setReturnValue(threadId(proc)), 100007 => trap_frame.setReturnValue(devicesByKind(trap_frame)), 100008 => trap_frame.setReturnValue(unlock(proc, trap_frame)), 100009 => trap_frame.setReturnValue(lock(proc, trap_frame)), 100010 => trap_frame.setReturnValue(join(proc, trap_frame)), 100011 => trap_frame.setReturnValue(leave(proc, trap_frame)), 100012 => trap_frame.setReturnValue(pass(proc, trap_frame)), 100013 => trap_frame.setReturnValue(receive(proc, trap_frame)), else => return HandleError.UnknownSyscall, } } // errorName(code: u16, buffer: [*]u8, len: usize) !usize fn errorName(trap_frame: *const TrapFrame) !usize { const code_wide = trap_frame.general_purpose_registers[10]; const buffer_opt: ?[*]u8 = @ptrFromInt(trap_frame.general_purpose_registers[11]); const buffer_ptr = buffer_opt orelse return ArgumentError.ZeroAddressSupplied; const len = trap_frame.general_purpose_registers[12]; const code = std.math.cast(u16, code_wide) orelse return ArgumentError.OutOfRange; const buffer = buffer_ptr[0..len]; if (code == 0) return 0; const error_name = @errorName(@errorFromInt(code)); const n = @min(buffer.len, error_name.len); paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); @memcpy(buffer[0..n], error_name[0..n]); return n; } // consoleWrite(bytes: [*]const u8, len: usize) !usize fn consoleWrite(trap_frame: *const TrapFrame) !usize { const vaddr = trap_frame.general_purpose_registers[10]; const len = trap_frame.general_purpose_registers[11]; const procmem: *paging.Table = @ptrFromInt(riscv.satp.read().ppn << 12); const flags = paging.EntryFlags.userReadOnly; const paddr = procmem.translate(vaddr, flags) orelse { const faulter: *volatile u8 = @ptrFromInt(vaddr); _ = faulter.*; unreachable; }; const bytes_ptr: [*]const u8 = @ptrFromInt(paddr); const bytes = bytes_ptr[0..len]; const w = Console.autoChoose().?.writer(); return w.write(bytes); } // launch(bytes: [*]align(@alignOf(std.elf.Elf64_Ehdr)) const u8, len: usize) !usize fn launch(trap_frame: *const TrapFrame) !usize { const alignment = @alignOf(std.elf.Elf64_Ehdr); const bytes_addr = trap_frame.general_purpose_registers[10]; const bytes_opt: ?[*]const u8 = @ptrFromInt(bytes_addr); const bytes_noalign = bytes_opt orelse return ArgumentError.ZeroAddressSupplied; const bytes_ptr = try std.math.alignCast(alignment, bytes_noalign); const len = trap_frame.general_purpose_registers[11]; const bytes = bytes_ptr[0..len]; paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); const new_proc = try process.create(mem.page_allocator, bytes); return new_proc.id; } // end() noreturn fn end(proc: *process.Info) noreturn { proc.terminate(); process.schedule() catch |err| { std.debug.panic("schedule error: {}", .{err}); }; } pub const TerminateError = error{ProcessNotFound}; // terminate(pid: u16, tid: usize) !void fn terminate(proc: *const process.Info, trap_frame: *const TrapFrame) !void { const pid_wide = trap_frame.general_purpose_registers[10]; const pid = std.math.cast(u16, pid_wide) orelse { return ArgumentError.OutOfRange; }; const tid = trap_frame.general_purpose_registers[11]; const target = process.findThread(pid, tid) orelse { return TerminateError.ProcessNotFound; }; target.terminate(); if (target.shouldTerminate(proc)) { process.schedule() catch |err| { std.debug.panic("schedule error: {}", .{err}); }; } } // processId() u16 fn processId(proc: *const process.Info) usize { return proc.id; } // threadId() usize fn threadId(proc: *const process.Info) usize { return proc.thread_id; } pub const DeviceError = error{ KindNotUserManageable, DeviceNotFound, VirtualAddressOutOfRange, }; // devicesByKind(kind: hwinfo.DevKind, devices: [*]hwinfo.Dev, len: usize) !usize fn devicesByKind(trap_frame: *const TrapFrame) !usize { const kind: hwinfo.DevKind = @enumFromInt(trap_frame.general_purpose_registers[10]); const devices: [*]hwinfo.Dev = @ptrFromInt(trap_frame.general_purpose_registers[11]); const len = trap_frame.general_purpose_registers[12]; if (!kind.isUserManageable()) return DeviceError.KindNotUserManageable; var i: usize = 0; var devs = try hwinfo.byKind(kind); while (devs.next()) |device| { if (i >= len) break; devices[i] = device; i += 1; } return i; } // unlock(reg_addr: usize, writable: bool) !usize fn unlock(proc: *process.Info, trap_frame: *const TrapFrame) !usize { const reg_addr = trap_frame.general_purpose_registers[10]; const writable = trap_frame.general_purpose_registers[11] != 0; const flags = if (writable) blk: { break :blk paging.EntryFlags.userReadWrite; } else blk: { break :blk paging.EntryFlags.userReadOnly; }; const vaddr = proc.next_mmio_vaddr; const device = try hwinfo.byAddress(reg_addr, true) orelse return DeviceError.DeviceNotFound; try proc.page_table.mapDevice(device.reg, &proc.next_mmio_vaddr, flags); asm volatile ( \\ sfence.vma %[addr], %[asid] : : [addr] "r" (device.reg.addr), [asid] "r" (proc.id), ); return vaddr; } // lock(vaddr: usize) !void fn lock(proc: *const process.Info, trap_frame: *const TrapFrame) !void { var vaddr = trap_frame.general_purpose_registers[10]; if (vaddr < process.mmio_start_vaddr) return DeviceError.VirtualAddressOutOfRange; const reg_addr = proc.page_table.translate(vaddr, paging.EntryFlags.invalid) orelse { const faulter: *volatile u8 = @ptrFromInt(vaddr); _ = faulter.*; unreachable; }; const device = try hwinfo.byAddress(reg_addr, true) orelse return DeviceError.DeviceNotFound; try proc.page_table.mapDevice(device.reg, &vaddr, paging.EntryFlags.invalid); asm volatile ( \\ sfence.vma %[addr], %[asid] : : [addr] "r" (device.reg.addr), [asid] "r" (proc.id), ); } // join(channel: usize) !void fn join(proc: *const process.Info, trap_frame: *const TrapFrame) !void { const id = trap_frame.general_purpose_registers[10]; return channel.join(proc.id, id); } // leave(channel: usize) void fn leave(proc: *const process.Info, trap_frame: *const TrapFrame) void { const id = trap_frame.general_purpose_registers[10]; channel.leave(proc.id, id); } // pass(channel: usize, receiver: u16, identify: bool, bytes: [*]const u8, len: usize) !void fn pass(proc: *const process.Info, trap_frame: *const TrapFrame) !void { const id = trap_frame.general_purpose_registers[10]; const receiver_wide = trap_frame.general_purpose_registers[11]; const identify: bool = trap_frame.general_purpose_registers[12] != 0; const bytes_ptr: [*]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[13]); const len = trap_frame.general_purpose_registers[14]; const receiver = std.math.cast(u16, receiver_wide) orelse return ArgumentError.OutOfRange; const bytes = bytes_ptr[0..len]; const copy = try channel.allocator().alloc(u8, bytes.len); paging.setUserMemoryAccess(true); @memcpy(copy, bytes); paging.setUserMemoryAccess(false); try channel.pass(proc.id, id, receiver, identify, copy); } // receive(channel: usize, sender: ?*u16, buffer: [*]u8, len: usize) !usize fn receive(proc: *const process.Info, trap_frame: *const TrapFrame) !usize { const id = trap_frame.general_purpose_registers[10]; const sender: ?*u16 = @ptrFromInt(trap_frame.general_purpose_registers[11]); const buffer_ptr: [*]u8 = @ptrFromInt(trap_frame.general_purpose_registers[12]); const len = trap_frame.general_purpose_registers[13]; const buffer = buffer_ptr[0..len]; paging.setUserMemoryAccess(true); defer paging.setUserMemoryAccess(false); return channel.receive(proc.id, id, sender, buffer); }