Add error handling for syscalls
This commit is contained in:
parent
3e5d2bec38
commit
c0a0c164c3
2 changed files with 358 additions and 100 deletions
|
@ -9,6 +9,7 @@ const testing = std.testing;
|
||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
const isr = @import("isr.zig");
|
const isr = @import("isr.zig");
|
||||||
const panic = @import("../../panic.zig").panic;
|
const panic = @import("../../panic.zig").panic;
|
||||||
|
const syscalls = @import("../../syscalls.zig");
|
||||||
|
|
||||||
/// The isr number associated with syscalls
|
/// The isr number associated with syscalls
|
||||||
pub const INTERRUPT: u16 = 0x80;
|
pub const INTERRUPT: u16 = 0x80;
|
||||||
|
@ -17,16 +18,20 @@ pub const INTERRUPT: u16 = 0x80;
|
||||||
pub const NUM_HANDLERS: u16 = 256;
|
pub const NUM_HANDLERS: u16 = 256;
|
||||||
|
|
||||||
/// A syscall handler
|
/// A syscall handler
|
||||||
pub const SyscallHandler = fn (ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32;
|
pub const Handler = fn (ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize;
|
||||||
|
|
||||||
/// Errors that syscall utility functions can throw
|
/// Errors that syscall utility functions can throw
|
||||||
pub const SyscallError = error{
|
pub const Error = error{
|
||||||
SyscallExists,
|
SyscallExists,
|
||||||
InvalidSyscall,
|
InvalidSyscall,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
std.debug.assert(@typeInfo(syscalls.Syscall).Enum.fields.len <= NUM_HANDLERS);
|
||||||
|
}
|
||||||
|
|
||||||
/// The array of registered syscalls
|
/// The array of registered syscalls
|
||||||
var handlers: [NUM_HANDLERS]?SyscallHandler = [_]?SyscallHandler{null} ** NUM_HANDLERS;
|
var handlers: [NUM_HANDLERS]?Handler = [_]?Handler{null} ** NUM_HANDLERS;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns true if the syscall is valid, else false.
|
/// Returns true if the syscall is valid, else false.
|
||||||
|
@ -44,19 +49,29 @@ pub fn isValidSyscall(syscall: u32) bool {
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Handle a syscall. Gets the syscall number from eax within the context and calls the registered
|
/// Handle a syscall. Gets the syscall number from eax within the context and calls the registered
|
||||||
/// handler. If there isn't a registered handler or the syscall is invalid (>= NUM_HANDLERS) then a
|
/// handler. If an error occurs ebx will be set to its error code, or 0 otherwise.
|
||||||
/// warning is logged.
|
/// The syscall result will be stored in eax. If there isn't a registered handler or the syscall is
|
||||||
|
/// invalid (>= NUM_HANDLERS) then a warning is logged.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.CpuState - The cpu context when the syscall was triggered. The
|
/// IN ctx: *arch.CpuState - The cpu context when the syscall was triggered. The
|
||||||
/// syscall number is stored in eax.
|
/// syscall number is stored in eax.
|
||||||
///
|
///
|
||||||
fn handle(ctx: *arch.CpuState) u32 {
|
/// Return: usize
|
||||||
|
/// The new stack pointer value
|
||||||
|
///
|
||||||
|
fn handle(ctx: *arch.CpuState) usize {
|
||||||
// The syscall number is put in eax
|
// The syscall number is put in eax
|
||||||
const syscall = ctx.eax;
|
const syscall = ctx.eax;
|
||||||
if (isValidSyscall(syscall)) {
|
if (isValidSyscall(syscall)) {
|
||||||
if (handlers[syscall]) |handler| {
|
if (handlers[syscall]) |handler| {
|
||||||
ctx.eax = handler(ctx, syscallArg(ctx, 0), syscallArg(ctx, 1), syscallArg(ctx, 2), syscallArg(ctx, 3), syscallArg(ctx, 4));
|
const result = handler(ctx, syscallArg(ctx, 0), syscallArg(ctx, 1), syscallArg(ctx, 2), syscallArg(ctx, 3), syscallArg(ctx, 4));
|
||||||
|
if (result) |res| {
|
||||||
|
ctx.eax = res;
|
||||||
|
ctx.ebx = 0;
|
||||||
|
} else |e| {
|
||||||
|
ctx.ebx = syscalls.toErrorCode(e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("Syscall {} triggered but not registered\n", .{syscall});
|
log.warn("Syscall {} triggered but not registered\n", .{syscall});
|
||||||
}
|
}
|
||||||
|
@ -67,147 +82,201 @@ fn handle(ctx: *arch.CpuState) u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Register a syscall so it can be called by triggering interrupt 128.
|
/// Register a syscall so it can be called by triggering interrupt 128 and putting its number in eax.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u8 - The syscall number to register the handler with.
|
/// IN syscall: usize - The syscall to register the handler with.
|
||||||
/// IN handler: SyscallHandler - The handler to register the syscall with.
|
/// IN handler: Handler - The handler to register the syscall with.
|
||||||
///
|
///
|
||||||
/// Errors:
|
/// Errors: Error
|
||||||
/// SyscallError.InvalidSyscall - If the syscall is invalid (see isValidSyscall).
|
/// Error.SyscallExists - If the syscall has already been registered.
|
||||||
/// SyscallError.SyscallExists - If the syscall has already been registered.
|
/// Error.InvalidSyscall - If the syscall is invalid. See isValidSyscall.
|
||||||
///
|
///
|
||||||
pub fn registerSyscall(syscall: u8, handler: SyscallHandler) SyscallError!void {
|
pub fn registerSyscall(syscall: usize, handler: Handler) Error!void {
|
||||||
if (!isValidSyscall(syscall))
|
if (!isValidSyscall(syscall))
|
||||||
return SyscallError.InvalidSyscall;
|
return Error.InvalidSyscall;
|
||||||
if (handlers[syscall]) |_|
|
if (handlers[syscall]) |_|
|
||||||
return SyscallError.SyscallExists;
|
return Error.SyscallExists;
|
||||||
handlers[syscall] = handler;
|
handlers[syscall] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trigger a syscall with no arguments. Returns the value put in eax by the syscall.
|
/// Trigger a syscall with no arguments. Returns the value put in eax by the syscall or the error returned in ebx.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: usize - The syscall to trigger, put in eax.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The return value from the syscall.
|
/// The return value from the syscall.
|
||||||
///
|
///
|
||||||
inline fn syscall0(syscall: u32) u32 {
|
/// Error: syscalls.Error
|
||||||
return asm volatile (
|
/// This function will return the error that the syscall handler returns. See the documentation for the syscall for details.
|
||||||
|
///
|
||||||
|
inline fn syscall0(syscall: usize) syscalls.Error!usize {
|
||||||
|
const res = asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
: [ret] "={eax}" (-> u32)
|
: [ret] "={eax}" (-> usize)
|
||||||
: [syscall] "{eax}" (syscall)
|
: [syscall] "{eax}" (syscall)
|
||||||
|
: "ebx"
|
||||||
);
|
);
|
||||||
|
const err = asm (""
|
||||||
|
: [ret] "={ebx}" (-> usize)
|
||||||
|
);
|
||||||
|
if (err != 0) {
|
||||||
|
return syscalls.fromErrorCode(err);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trigger a syscall with one argument. Returns the value put in eax by the syscall.
|
/// Trigger a syscall with one argument. Returns the value put in eax by the syscall or the error returned in ebx.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: usize - The syscall to trigger, put in eax.
|
||||||
/// IN arg: u32 - The argument to pass. Put in ebx.
|
/// IN arg: usize - The argument to pass. Put in ebx.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The return value from the syscall.
|
/// The return value from the syscall.
|
||||||
///
|
///
|
||||||
inline fn syscall1(syscall: u32, arg: u32) u32 {
|
/// Error: syscalls.Error
|
||||||
return asm volatile (
|
/// This function will return the error that the syscall handler returns. See the documentation for the syscall for details.
|
||||||
|
///
|
||||||
|
inline fn syscall1(syscall: usize, arg: usize) syscalls.Error!usize {
|
||||||
|
const res = asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
: [ret] "={eax}" (-> u32)
|
: [ret] "={eax}" (-> usize)
|
||||||
: [syscall] "{eax}" (syscall),
|
: [syscall] "{eax}" (syscall),
|
||||||
[arg1] "{ebx}" (arg)
|
[arg1] "{ebx}" (arg)
|
||||||
);
|
);
|
||||||
|
const err = asm (""
|
||||||
|
: [ret] "={ebx}" (-> usize)
|
||||||
|
);
|
||||||
|
if (err != 0) {
|
||||||
|
return syscalls.fromErrorCode(err);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trigger a syscall with two arguments. Returns the value put in eax by the syscall.
|
/// Trigger a syscall with two arguments. Returns the value put in eax by the syscall or the error returned in ebx.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: usize - The syscall to trigger, put in eax.
|
||||||
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
/// IN arg1: usize - The first argument to pass. Put in ebx.
|
||||||
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
/// IN arg2: usize - The second argument to pass. Put in ecx.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The return value from the syscall.
|
/// The return value from the syscall.
|
||||||
///
|
///
|
||||||
inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 {
|
/// Error: syscalls.Error
|
||||||
return asm volatile (
|
/// This function will return the error that the syscall handler returns. See the documentation for the syscall for details.
|
||||||
|
///
|
||||||
|
inline fn syscall2(syscall: usize, arg1: usize, arg2: usize) syscalls.Error!usize {
|
||||||
|
const res = asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
: [ret] "={eax}" (-> u32)
|
: [ret] "={eax}" (-> usize)
|
||||||
: [syscall] "{eax}" (syscall),
|
: [syscall] "{eax}" (syscall),
|
||||||
[arg1] "{ebx}" (arg1),
|
[arg1] "{ebx}" (arg1),
|
||||||
[arg2] "{ecx}" (arg2)
|
[arg2] "{ecx}" (arg2)
|
||||||
);
|
);
|
||||||
|
const err = asm (""
|
||||||
|
: [ret] "={ebx}" (-> usize)
|
||||||
|
);
|
||||||
|
if (err != 0) {
|
||||||
|
return syscalls.fromErrorCode(err);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trigger a syscall with three arguments. Returns the value put in eax by the syscall.
|
/// Trigger a syscall with three arguments. Returns the value put in eax by the syscall or the error returned in ebx.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: usize - The syscall to trigger, put in eax.
|
||||||
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
/// IN arg1: usize - The first argument to pass. Put in ebx.
|
||||||
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
/// IN arg2: usize - The second argument to pass. Put in ecx.
|
||||||
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
/// IN arg3: usize - The third argument to pass. Put in edx.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The return value from the syscall.
|
/// The return value from the syscall.
|
||||||
///
|
///
|
||||||
inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 {
|
/// Error: syscalls.Error
|
||||||
return asm volatile (
|
/// This function will return the error that the syscall handler returns. See the documentation for the syscall for details.
|
||||||
|
///
|
||||||
|
inline fn syscall3(syscall: usize, arg1: usize, arg2: usize, arg3: usize) syscalls.Error!usize {
|
||||||
|
const res = asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
: [ret] "={eax}" (-> u32)
|
: [ret] "={eax}" (-> usize)
|
||||||
: [syscall] "{eax}" (syscall),
|
: [syscall] "{eax}" (syscall),
|
||||||
[arg1] "{ebx}" (arg1),
|
[arg1] "{ebx}" (arg1),
|
||||||
[arg2] "{ecx}" (arg2),
|
[arg2] "{ecx}" (arg2),
|
||||||
[arg3] "{edx}" (arg3)
|
[arg3] "{edx}" (arg3)
|
||||||
);
|
);
|
||||||
|
const err = asm (""
|
||||||
|
: [ret] "={ebx}" (-> usize)
|
||||||
|
);
|
||||||
|
if (err != 0) {
|
||||||
|
return syscalls.fromErrorCode(err);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trigger a syscall with four arguments. Returns the value put in eax by the syscall.
|
/// Trigger a syscall with four arguments. Returns the value put in eax by the syscall or the error returned in ebx.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: usize - The syscall to trigger, put in eax.
|
||||||
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
/// IN arg1: usize - The first argument to pass. Put in ebx.
|
||||||
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
/// IN arg2: usize - The second argument to pass. Put in ecx.
|
||||||
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
/// IN arg3: usize - The third argument to pass. Put in edx.
|
||||||
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
/// IN arg4: usize - The fourth argument to pass. Put in esi.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The return value from the syscall.
|
/// The return value from the syscall.
|
||||||
///
|
///
|
||||||
inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32 {
|
/// Error: syscalls.Error
|
||||||
return asm volatile (
|
/// This function will return the error that the syscall handler returns. See the documentation for the syscall for details.
|
||||||
|
///
|
||||||
|
inline fn syscall4(syscall: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) syscalls.Error!usize {
|
||||||
|
const res = asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
: [ret] "={eax}" (-> u32)
|
: [ret] "={eax}" (-> usize)
|
||||||
: [syscall] "{eax}" (syscall),
|
: [syscall] "{eax}" (syscall),
|
||||||
[arg1] "{ebx}" (arg1),
|
[arg1] "{ebx}" (arg1),
|
||||||
[arg2] "{ecx}" (arg2),
|
[arg2] "{ecx}" (arg2),
|
||||||
[arg3] "{edx}" (arg3),
|
[arg3] "{edx}" (arg3),
|
||||||
[arg4] "{esi}" (arg4)
|
[arg4] "{esi}" (arg4)
|
||||||
);
|
);
|
||||||
|
const err = asm (""
|
||||||
|
: [ret] "={ebx}" (-> usize)
|
||||||
|
);
|
||||||
|
if (err != 0) {
|
||||||
|
return syscalls.fromErrorCode(err);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trigger a syscall with five arguments. Returns the value put in eax by the syscall.
|
/// Trigger a syscall with five arguments. Returns the value put in eax by the syscall or the error returned in ebx.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: usize - The syscall to trigger, put in eax.
|
||||||
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
/// IN arg1: usize - The first argument to pass. Put in ebx.
|
||||||
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
/// IN arg2: usize - The second argument to pass. Put in ecx.
|
||||||
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
/// IN arg3: usize - The third argument to pass. Put in edx.
|
||||||
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
/// IN arg4: usize - The fourth argument to pass. Put in esi.
|
||||||
/// IN arg5: u32 - The fifth argument to pass. Put in edi.
|
/// IN arg5: usize - The fifth argument to pass. Put in edi.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The return value from the syscall.
|
/// The return value from the syscall.
|
||||||
///
|
///
|
||||||
inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
/// Error: syscalls.Error
|
||||||
return asm volatile (
|
/// This function will return the error that the syscall handler returns. See the documentation for the syscall for details.
|
||||||
|
///
|
||||||
|
inline fn syscall5(syscall: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
|
const res = asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
: [ret] "={eax}" (-> u32)
|
: [ret] "={eax}" (-> usize)
|
||||||
: [syscall] "{eax}" (syscall),
|
: [syscall] "{eax}" (syscall),
|
||||||
[arg1] "{ebx}" (arg1),
|
[arg1] "{ebx}" (arg1),
|
||||||
[arg2] "{ecx}" (arg2),
|
[arg2] "{ecx}" (arg2),
|
||||||
|
@ -215,6 +284,13 @@ inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg
|
||||||
[arg4] "{esi}" (arg4),
|
[arg4] "{esi}" (arg4),
|
||||||
[arg5] "{edi}" (arg5)
|
[arg5] "{edi}" (arg5)
|
||||||
);
|
);
|
||||||
|
const err = asm (""
|
||||||
|
: [ret] "={ebx}" (-> usize)
|
||||||
|
);
|
||||||
|
if (err != 0) {
|
||||||
|
return syscalls.fromErrorCode(err);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -225,10 +301,10 @@ inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg
|
||||||
/// IN ctx: *arch.CpuState - The interrupt context from which to get the argument
|
/// IN ctx: *arch.CpuState - The interrupt context from which to get the argument
|
||||||
/// IN arg_idx: comptime u32 - The argument index to get. Between 0 and 4.
|
/// IN arg_idx: comptime u32 - The argument index to get. Between 0 and 4.
|
||||||
///
|
///
|
||||||
/// Return: u32
|
/// Return: usize
|
||||||
/// The syscall argument from the given index.
|
/// The syscall argument from the given index.
|
||||||
///
|
///
|
||||||
inline fn syscallArg(ctx: *arch.CpuState, comptime arg_idx: u32) u32 {
|
inline fn syscallArg(ctx: *arch.CpuState, comptime arg_idx: u32) usize {
|
||||||
return switch (arg_idx) {
|
return switch (arg_idx) {
|
||||||
0 => ctx.ebx,
|
0 => ctx.ebx,
|
||||||
1 => ctx.ecx,
|
1 => ctx.ecx,
|
||||||
|
@ -240,13 +316,39 @@ inline fn syscallArg(ctx: *arch.CpuState, comptime arg_idx: u32) u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Initialise syscalls. Registers the isr associated with INTERRUPT.
|
/// Construct a handler for a syscall.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN comptime syscall: Syscall - The syscall to construct the handler for.
|
||||||
|
///
|
||||||
|
/// Return: Handler
|
||||||
|
/// The handler function constructed.
|
||||||
|
///
|
||||||
|
fn makeHandler(comptime syscall: syscalls.Syscall) Handler {
|
||||||
|
return struct {
|
||||||
|
fn func(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
|
return syscalls.handle(syscall, arg1, arg2, arg3, arg4, arg5);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialise syscalls. Registers the isr associated with INTERRUPT and sets up handlers for each syscall.
|
||||||
///
|
///
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
log.info("Init\n", .{});
|
log.info("Init\n", .{});
|
||||||
defer log.info("Done\n", .{});
|
defer log.info("Done\n", .{});
|
||||||
|
|
||||||
isr.registerIsr(INTERRUPT, handle) catch unreachable;
|
isr.registerIsr(INTERRUPT, handle) catch |e| {
|
||||||
|
panic(@errorReturnTrace(), "Failed to register syscall ISR: {}\n", .{e});
|
||||||
|
};
|
||||||
|
|
||||||
|
inline for (std.meta.fields(syscalls.Syscall)) |field| {
|
||||||
|
const syscall = @intToEnum(syscalls.Syscall, field.value);
|
||||||
|
registerSyscall(field.value, makeHandler(syscall)) catch |e| {
|
||||||
|
panic(@errorReturnTrace(), "Failed to register syscall for '" ++ field.name ++ "': {}\n", .{e});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
switch (build_options.test_mode) {
|
switch (build_options.test_mode) {
|
||||||
.Initialisation => runtimeTests(),
|
.Initialisation => runtimeTests(),
|
||||||
|
@ -257,78 +359,112 @@ pub fn init() void {
|
||||||
/// Tests
|
/// Tests
|
||||||
var test_int: u32 = 0;
|
var test_int: u32 = 0;
|
||||||
|
|
||||||
fn testHandler0(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler0(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
test_int += 1;
|
test_int += 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler1(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler1(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
test_int += arg1;
|
test_int += arg1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler2(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler2(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
test_int += arg1 + arg2;
|
test_int += arg1 + arg2;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler3(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler3(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
test_int += arg1 + arg2 + arg3;
|
test_int += arg1 + arg2 + arg3;
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler4(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler4(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
test_int += arg1 + arg2 + arg3 + arg4;
|
test_int += arg1 + arg2 + arg3 + arg4;
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler5(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler5(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
test_int += arg1 + arg2 + arg3 + arg4 + arg5;
|
test_int += arg1 + arg2 + arg3 + arg4 + arg5;
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn testHandler6(ctx: *arch.CpuState, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) syscalls.Error!usize {
|
||||||
|
return syscalls.Error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
test "registerSyscall returns SyscallExists" {
|
test "registerSyscall returns SyscallExists" {
|
||||||
registerSyscall(123, testHandler0) catch unreachable;
|
try registerSyscall(122, testHandler0);
|
||||||
registerSyscall(123, testHandler0) catch |err| {
|
std.testing.expectError(Error.SyscallExists, registerSyscall(122, testHandler0));
|
||||||
return;
|
|
||||||
};
|
|
||||||
expect(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtimeTests() void {
|
fn runtimeTests() void {
|
||||||
registerSyscall(123, testHandler0) catch panic(@errorReturnTrace(), "FAILURE registering handler 0\n", .{});
|
registerSyscall(121, testHandler6) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 6: {}\n", .{e});
|
||||||
registerSyscall(124, testHandler1) catch panic(@errorReturnTrace(), "FAILURE registering handler 1\n", .{});
|
registerSyscall(122, testHandler0) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 0: {}\n", .{e});
|
||||||
registerSyscall(125, testHandler2) catch panic(@errorReturnTrace(), "FAILURE registering handler 2\n", .{});
|
registerSyscall(123, testHandler1) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 1: {}\n", .{e});
|
||||||
registerSyscall(126, testHandler3) catch panic(@errorReturnTrace(), "FAILURE registering handler 3\n", .{});
|
registerSyscall(124, testHandler2) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 2: {}\n", .{e});
|
||||||
registerSyscall(127, testHandler4) catch panic(@errorReturnTrace(), "FAILURE registering handler 4\n", .{});
|
registerSyscall(125, testHandler3) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 3: {}\n", .{e});
|
||||||
registerSyscall(128, testHandler5) catch panic(@errorReturnTrace(), "FAILURE registering handler 5\n", .{});
|
registerSyscall(126, testHandler4) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 4: {}\n", .{e});
|
||||||
|
registerSyscall(127, testHandler5) catch |e| panic(@errorReturnTrace(), "FAILURE registering handler 5: {}\n", .{e});
|
||||||
|
|
||||||
if (test_int != 0) {
|
if (test_int != 0) {
|
||||||
panic(@errorReturnTrace(), "FAILURE initial test_int not 0: {}\n", .{test_int});
|
panic(@errorReturnTrace(), "FAILURE initial test_int not 0: {}\n", .{test_int});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syscall0(123) != 0 or test_int != 1) {
|
if (syscall0(122)) |res| {
|
||||||
panic(@errorReturnTrace(), "FAILURE syscall0\n", .{});
|
if (res != 0 or test_int != 1) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall0\n", .{});
|
||||||
|
}
|
||||||
|
} else |e| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall0 errored: {}\n", .{e});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syscall1(124, 2) != 1 or test_int != 3) {
|
if (syscall1(123, 2)) |res| {
|
||||||
panic(@errorReturnTrace(), "FAILURE syscall2\n", .{});
|
if (res != 1 or test_int != 3) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall1\n", .{});
|
||||||
|
}
|
||||||
|
} else |e| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall1 errored: {}\n", .{e});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syscall2(125, 2, 3) != 2 or test_int != 8) {
|
if (syscall2(124, 2, 3)) |res| {
|
||||||
panic(@errorReturnTrace(), "FAILURE syscall2\n", .{});
|
if (res != 2 or test_int != 8) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall2\n", .{});
|
||||||
|
}
|
||||||
|
} else |e| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall2 errored: {}\n", .{e});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syscall3(126, 2, 3, 4) != 3 or test_int != 17) {
|
if (syscall3(125, 2, 3, 4)) |res| {
|
||||||
panic(@errorReturnTrace(), "FAILURE syscall3\n", .{});
|
if (res != 3 or test_int != 17) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall3\n", .{});
|
||||||
|
}
|
||||||
|
} else |e| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall3 errored: {}\n", .{e});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syscall4(127, 2, 3, 4, 5) != 4 or test_int != 31) {
|
if (syscall4(126, 2, 3, 4, 5)) |res| {
|
||||||
panic(@errorReturnTrace(), "FAILURE syscall4\n", .{});
|
if (res != 4 or test_int != 31) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall4\n", .{});
|
||||||
|
}
|
||||||
|
} else |e| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall4 errored: {}\n", .{e});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syscall5(128, 2, 3, 4, 5, 6) != 5 or test_int != 51) {
|
if (syscall5(127, 2, 3, 4, 5, 6)) |res| {
|
||||||
panic(@errorReturnTrace(), "FAILURE syscall5\n", .{});
|
if (res != 5 or test_int != 51) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall5\n", .{});
|
||||||
|
}
|
||||||
|
} else |e| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall5 errored: {}\n", .{e});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syscall0(121)) |res| {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall6\n", .{});
|
||||||
|
} else |e| {
|
||||||
|
if (e != syscalls.Error.OutOfMemory) {
|
||||||
|
panic(@errorReturnTrace(), "FAILURE syscall6 returned the wrong error: {}\n", .{e});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Tested all args\n", .{});
|
log.info("Tested all args\n", .{});
|
||||||
|
|
122
src/kernel/syscalls.zig
Normal file
122
src/kernel/syscalls.zig
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const scheduler = @import("scheduler.zig");
|
||||||
|
const panic = @import("panic.zig").panic;
|
||||||
|
const log = std.log.scoped(.syscalls);
|
||||||
|
|
||||||
|
/// A compilation of all errors that syscall handlers could return.
|
||||||
|
pub const Error = error{OutOfMemory};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert an error code to an instance of Error. The conversion must be synchronised with toErrorCode
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN code: usize - The erorr code to convert
|
||||||
|
///
|
||||||
|
/// Return: Error
|
||||||
|
/// The error corresponding to the error code
|
||||||
|
///
|
||||||
|
pub fn fromErrorCode(code: usize) Error {
|
||||||
|
return switch (code) {
|
||||||
|
1 => Error.OutOfMemory,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert an instance of Error to an error code. The conversion must be synchronised with fromErrorCode
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN err: Error - The erorr to convert
|
||||||
|
///
|
||||||
|
/// Return: usize
|
||||||
|
/// The error code corresponding to the error
|
||||||
|
///
|
||||||
|
pub fn toErrorCode(err: Error) usize {
|
||||||
|
return switch (err) {
|
||||||
|
Error.OutOfMemory => 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
// Make sure toErrorCode and fromErrorCode are synchronised, and that no errors share the same error code
|
||||||
|
inline for (@typeInfo(Error).ErrorSet.?) |err| {
|
||||||
|
const error_instance = @field(Error, err.name);
|
||||||
|
if (fromErrorCode(toErrorCode(error_instance)) != error_instance) {
|
||||||
|
@compileError("toErrorCode and fromErrorCode are not synchronised for syscall error '" ++ err.name ++ "'\n");
|
||||||
|
}
|
||||||
|
inline for (@typeInfo(Error).ErrorSet.?) |err2| {
|
||||||
|
const error2_instance = @field(Error, err2.name);
|
||||||
|
if (error_instance != error2_instance and toErrorCode(error_instance) == toErrorCode(error2_instance)) {
|
||||||
|
@compileError("Syscall errors '" ++ err.name ++ "' and '" ++ err2.name ++ "' share the same error code\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All implemented syscalls
|
||||||
|
pub const Syscall = enum {
|
||||||
|
Test1,
|
||||||
|
Test2,
|
||||||
|
Test3,
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Get the handler associated with the syscall
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN self: Syscall - The syscall to get the handler for
|
||||||
|
///
|
||||||
|
/// Return: Handler
|
||||||
|
/// The handler that takes care of this syscall
|
||||||
|
///
|
||||||
|
fn getHandler(self: @This()) Handler {
|
||||||
|
return switch (self) {
|
||||||
|
.Test1 => handleTest1,
|
||||||
|
.Test2 => handleTest2,
|
||||||
|
.Test3 => handleTest3,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A function that can handle a syscall and return a result or an error
|
||||||
|
pub const Handler = fn (arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) Error!usize;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Handle a syscall and return a result or error
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN syscall: Syscall - The syscall to handle
|
||||||
|
/// IN argX: usize - The xth argument that was passed to the syscall
|
||||||
|
///
|
||||||
|
/// Return: usize
|
||||||
|
/// The syscall result
|
||||||
|
///
|
||||||
|
/// Error: Error
|
||||||
|
/// The error raised by the handler
|
||||||
|
///
|
||||||
|
pub fn handle(syscall: Syscall, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) Error!usize {
|
||||||
|
return try syscall.getHandler()(arg1, arg2, arg3, arg4, arg5);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleTest1(arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) Error!usize {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleTest2(arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) Error!usize {
|
||||||
|
return arg1 + arg2 + arg3 + arg4 + arg5;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleTest3(arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) Error!usize {
|
||||||
|
return std.mem.Allocator.Error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "getHandler" {
|
||||||
|
std.testing.expectEqual(Syscall.Test1.getHandler(), handleTest1);
|
||||||
|
std.testing.expectEqual(Syscall.Test2.getHandler(), handleTest2);
|
||||||
|
std.testing.expectEqual(Syscall.Test3.getHandler(), handleTest3);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "handle" {
|
||||||
|
std.testing.expectEqual(@as(usize, 0), try handle(.Test1, 0, 0, 0, 0, 0));
|
||||||
|
std.testing.expectEqual(@as(usize, 1 + 2 + 3 + 4 + 5), try handle(.Test2, 1, 2, 3, 4, 5));
|
||||||
|
std.testing.expectError(Error.OutOfMemory, handle(.Test3, 0, 0, 0, 0, 0));
|
||||||
|
}
|
Loading…
Reference in a new issue