aboutsummaryrefslogtreecommitdiff
path: root/src/lib/syscall.zig
blob: ca8e2c8e412fceac3467c2ee2d3e352710e08fc7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
//
// 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");
const userinit = @import("userinit.zig");

pub const Error = error{
    ZeroAddressSupplied,
};

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(rawUserinit(trap_frame)),
        100008 => trap_frame.setReturnValue(devicesByKind(trap_frame)),
        100009 => trap_frame.setReturnValue(join(proc, trap_frame)),
        100010 => trap_frame.setReturnValue(leave(proc, trap_frame)),
        100011 => trap_frame.setReturnValue(pass(trap_frame)),
        100012 => trap_frame.setReturnValue(receive(proc, trap_frame)),
        else => return HandleError.UnknownSyscall,
    }
}

pub const ErrorNameError = error{ErrorCodeOutOfRange};

// 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 Error.ZeroAddressSupplied;
    const len = trap_frame.general_purpose_registers[12];

    const code = std.math.cast(u16, code_wide) orelse {
        return ErrorNameError.ErrorCodeOutOfRange;
    };
    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: ?[*]align(alignment) const u8 = @ptrFromInt(bytes_addr);
    const bytes_ptr = bytes_opt orelse return Error.ZeroAddressSupplied;
    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{
    PidOutOfRange,
    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 TerminateError.PidOutOfRange;
    };
    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;
}

// rawUserinit(ptr: *[*]const u8) usize
fn rawUserinit(trap_frame: *const TrapFrame) usize {
    const ptr: *[*]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]);
    ptr.* = userinit.tarball;
    return userinit.tarball.len;
}

// 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];

    var i: usize = 0;
    var devs = try hwinfo.byKind(kind);
    while (try devs.next()) |dev| {
        if (i >= len) break;

        devices[i] = dev;
        i += 1;
    }

    return i;
}

// join(channel_id: 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_id: 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_id: usize, bytes: [*]const u8, len: usize) !void
fn pass(trap_frame: *const TrapFrame) !void {
    const id = trap_frame.general_purpose_registers[10];
    const bytes_ptr: [*]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[11]);
    const len = trap_frame.general_purpose_registers[12];

    const bytes = bytes_ptr[0..len];
    const copy = try channel.allocator().alloc(u8, bytes.len);
    @memcpy(copy, bytes);
    try channel.pass(id, copy);
}

// receive(channel_id: usize, 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 buffer_ptr: [*]u8 = @ptrFromInt(trap_frame.general_purpose_registers[11]);
    const len = trap_frame.general_purpose_registers[12];

    const buffer = buffer_ptr[0..len];
    return channel.receive(proc.id, id, buffer);
}