aboutsummaryrefslogtreecommitdiff
path: root/src/syscall.zig
blob: e30c0d8d8a1613402b4aa7c8d1097091b661deda (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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// 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");

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);
}