1f97a5c6c8
Added cpu model Moved logging to defer done log Moved mem.init to bottom Updated again to new zig master
314 lines
9.8 KiB
Zig
314 lines
9.8 KiB
Zig
const arch = @import("arch.zig");
|
|
const testing = @import("std").testing;
|
|
const assert = @import("std").debug.assert;
|
|
const isr = @import("isr.zig");
|
|
const log = @import("../../log.zig");
|
|
const options = @import("build_options");
|
|
|
|
/// The isr number associated with syscalls
|
|
pub const INTERRUPT: u16 = 0x80;
|
|
|
|
/// The maximum number of syscall handlers that can be registered
|
|
pub const NUM_HANDLERS: u16 = 256;
|
|
|
|
/// A syscall handler
|
|
pub const SyscallHandler = fn (ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32;
|
|
|
|
/// Errors that syscall utility functions can throw
|
|
pub const SyscallError = error{
|
|
SyscallExists,
|
|
InvalidSyscall,
|
|
};
|
|
|
|
/// The array of registered syscalls
|
|
var handlers: [NUM_HANDLERS]?SyscallHandler = [_]?SyscallHandler{null} ** NUM_HANDLERS;
|
|
|
|
///
|
|
/// Returns true if the syscall is valid, else false.
|
|
/// A syscall is valid if it's less than NUM_HANDLERS.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to check
|
|
///
|
|
/// Return: bool
|
|
/// Whether the syscall number is valid.
|
|
///
|
|
pub fn isValidSyscall(syscall: u32) bool {
|
|
return syscall < NUM_HANDLERS;
|
|
}
|
|
|
|
///
|
|
/// 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
|
|
/// warning is logged.
|
|
///
|
|
/// Arguments:
|
|
/// IN ctx: *arch.InterruptContext - The cpu context when the syscall was triggered. The
|
|
/// syscall number is stored in eax.
|
|
///
|
|
fn handle(ctx: *arch.InterruptContext) void {
|
|
// The syscall number is put in eax
|
|
const syscall = ctx.eax;
|
|
if (isValidSyscall(syscall)) {
|
|
if (handlers[syscall]) |handler| {
|
|
ctx.eax = handler(ctx, syscallArg(ctx, 0), syscallArg(ctx, 1), syscallArg(ctx, 2), syscallArg(ctx, 3), syscallArg(ctx, 4));
|
|
} else {
|
|
log.logWarning("Syscall {} triggered but not registered\n", .{syscall});
|
|
}
|
|
} else {
|
|
log.logWarning("Syscall {} is invalid\n", .{syscall});
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Register a syscall so it can be called by triggering interrupt 128.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u8 - The syscall number to register the handler with.
|
|
/// IN handler: SyscallHandler - The handler to register the syscall with.
|
|
///
|
|
/// Errors:
|
|
/// SyscallError.InvalidSyscall - If the syscall is invalid (see isValidSyscall).
|
|
/// SyscallError.SyscallExists - If the syscall has already been registered.
|
|
///
|
|
pub fn registerSyscall(syscall: u8, handler: SyscallHandler) SyscallError!void {
|
|
if (!isValidSyscall(syscall))
|
|
return SyscallError.InvalidSyscall;
|
|
if (handlers[syscall]) |_|
|
|
return SyscallError.SyscallExists;
|
|
handlers[syscall] = handler;
|
|
}
|
|
|
|
///
|
|
/// Trigger a syscall with no arguments. Returns the value put in eax by the syscall.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
|
///
|
|
/// Return: u32
|
|
/// The return value from the syscall.
|
|
///
|
|
inline fn syscall0(syscall: u32) u32 {
|
|
return asm volatile (
|
|
\\int $0x80
|
|
: [ret] "={eax}" (-> u32)
|
|
: [syscall] "{eax}" (syscall)
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Trigger a syscall with one argument. Returns the value put in eax by the syscall.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
|
/// IN arg: u32 - The argument to pass. Put in ebx.
|
|
///
|
|
/// Return: u32
|
|
/// The return value from the syscall.
|
|
///
|
|
inline fn syscall1(syscall: u32, arg: u32) u32 {
|
|
return asm volatile (
|
|
\\int $0x80
|
|
: [ret] "={eax}" (-> u32)
|
|
: [syscall] "{eax}" (syscall),
|
|
[arg1] "{ebx}" (arg)
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Trigger a syscall with two arguments. Returns the value put in eax by the syscall.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
|
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
|
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
|
///
|
|
/// Return: u32
|
|
/// The return value from the syscall.
|
|
///
|
|
inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 {
|
|
return asm volatile (
|
|
\\int $0x80
|
|
: [ret] "={eax}" (-> u32)
|
|
: [syscall] "{eax}" (syscall),
|
|
[arg1] "{ebx}" (arg1),
|
|
[arg2] "{ecx}" (arg2)
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Trigger a syscall with three arguments. Returns the value put in eax by the syscall.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
|
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
|
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
|
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
|
///
|
|
/// Return: u32
|
|
/// The return value from the syscall.
|
|
///
|
|
inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 {
|
|
return asm volatile (
|
|
\\int $0x80
|
|
: [ret] "={eax}" (-> u32)
|
|
: [syscall] "{eax}" (syscall),
|
|
[arg1] "{ebx}" (arg1),
|
|
[arg2] "{ecx}" (arg2),
|
|
[arg3] "{edx}" (arg3)
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Trigger a syscall with four arguments. Returns the value put in eax by the syscall.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
|
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
|
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
|
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
|
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
|
///
|
|
/// Return: u32
|
|
/// The return value from the syscall.
|
|
///
|
|
inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32 {
|
|
return asm volatile (
|
|
\\int $0x80
|
|
: [ret] "={eax}" (-> u32)
|
|
: [syscall] "{eax}" (syscall),
|
|
[arg1] "{ebx}" (arg1),
|
|
[arg2] "{ecx}" (arg2),
|
|
[arg3] "{edx}" (arg3),
|
|
[arg4] "{esi}" (arg4)
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Trigger a syscall with five arguments. Returns the value put in eax by the syscall.
|
|
///
|
|
/// Arguments:
|
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
|
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
|
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
|
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
|
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
|
/// IN arg5: u32 - The fifth argument to pass. Put in edi.
|
|
///
|
|
/// Return: u32
|
|
/// The return value from the syscall.
|
|
///
|
|
inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
return asm volatile (
|
|
\\int $0x80
|
|
: [ret] "={eax}" (-> u32)
|
|
: [syscall] "{eax}" (syscall),
|
|
[arg1] "{ebx}" (arg1),
|
|
[arg2] "{ecx}" (arg2),
|
|
[arg3] "{edx}" (arg3),
|
|
[arg4] "{esi}" (arg4),
|
|
[arg5] "{edi}" (arg5)
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Gets the syscall argument according to the given index. 0 => ebx, 1 => ecx, 2 => edx,
|
|
/// 3 => esi and 4 => edi.
|
|
///
|
|
/// Arguments:
|
|
/// IN ctx: *arch.InterruptContext - The interrupt context from which to get the argument
|
|
/// IN arg_idx: comptime u32 - The argument index to get. Between 0 and 4.
|
|
///
|
|
/// Return: u32
|
|
/// The syscall argument from the given index.
|
|
///
|
|
inline fn syscallArg(ctx: *arch.InterruptContext, comptime arg_idx: u32) u32 {
|
|
return switch (arg_idx) {
|
|
0 => ctx.ebx,
|
|
1 => ctx.ecx,
|
|
2 => ctx.edx,
|
|
3 => ctx.esi,
|
|
4 => ctx.edi,
|
|
else => @compileError("Arg index must be between 0 and 4"),
|
|
};
|
|
}
|
|
|
|
///
|
|
/// Initialise syscalls. Registers the isr associated with INTERRUPT.
|
|
///
|
|
pub fn init() void {
|
|
log.logInfo("Init syscalls\n", .{});
|
|
defer log.logInfo("Done syscalls\n", .{});
|
|
|
|
isr.registerIsr(INTERRUPT, handle) catch unreachable;
|
|
if (options.rt_test) runtimeTests();
|
|
}
|
|
|
|
/// Tests
|
|
var testInt: u32 = 0;
|
|
|
|
fn testHandler0(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
testInt += 1;
|
|
return 0;
|
|
}
|
|
|
|
fn testHandler1(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
testInt += arg1;
|
|
return 1;
|
|
}
|
|
|
|
fn testHandler2(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
testInt += arg1 + arg2;
|
|
return 2;
|
|
}
|
|
|
|
fn testHandler3(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
testInt += arg1 + arg2 + arg3;
|
|
return 3;
|
|
}
|
|
|
|
fn testHandler4(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
testInt += arg1 + arg2 + arg3 + arg4;
|
|
return 4;
|
|
}
|
|
|
|
fn testHandler5(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
|
testInt += arg1 + arg2 + arg3 + arg4 + arg5;
|
|
return 5;
|
|
}
|
|
|
|
test "registerSyscall returns SyscallExists" {
|
|
registerSyscall(123, testHandler0) catch unreachable;
|
|
registerSyscall(123, testHandler0) catch |err| {
|
|
return;
|
|
};
|
|
assert(false);
|
|
}
|
|
|
|
fn runtimeTests() void {
|
|
registerSyscall(123, testHandler0) catch unreachable;
|
|
registerSyscall(124, testHandler1) catch unreachable;
|
|
registerSyscall(125, testHandler2) catch unreachable;
|
|
registerSyscall(126, testHandler3) catch unreachable;
|
|
registerSyscall(127, testHandler4) catch unreachable;
|
|
registerSyscall(128, testHandler5) catch unreachable;
|
|
assert(testInt == 0);
|
|
|
|
if (syscall0(123) == 0 and testInt == 1)
|
|
log.logInfo("Syscalls: Tested no args\n", .{});
|
|
|
|
if (syscall1(124, 2) == 1 and testInt == 3)
|
|
log.logInfo("Syscalls: Tested 1 arg\n", .{});
|
|
|
|
if (syscall2(125, 2, 3) == 2 and testInt == 8)
|
|
log.logInfo("Syscalls: Tested 2 args\n", .{});
|
|
|
|
if (syscall3(126, 2, 3, 4) == 3 and testInt == 17)
|
|
log.logInfo("Syscalls: Tested 3 args\n", .{});
|
|
|
|
if (syscall4(127, 2, 3, 4, 5) == 4 and testInt == 31)
|
|
log.logInfo("Syscalls: Tested 4 args\n", .{});
|
|
|
|
if (syscall5(128, 2, 3, 4, 5, 6) == 5 and testInt == 51)
|
|
log.logInfo("Syscalls: Tested 5 args\n", .{});
|
|
}
|