pluto/src/kernel/arch/x86/syscalls.zig

287 lines
8.8 KiB
Zig
Raw Normal View History

2019-07-31 23:41:22 +02:00
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");
/// 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) u32;
/// Errors that syscall utility functions can throw
pub const SyscallError = error{
2019-07-31 23:41:22 +02:00
SyscallExists,
InvalidSyscall,
2019-07-31 23:41:22 +02:00
};
/// The array of registered syscalls
var handlers: [NUM_HANDLERS]?SyscallHandler = [_]?SyscallHandler{null} ** NUM_HANDLERS;
2019-07-31 23:41:22 +02:00
///
/// 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
///
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);
} 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.
///
inline fn syscall0(syscall: u32) u32 {
return asm volatile (
\\int $0x80
: [ret] "={eax}" (-> u32)
: [syscall] "{eax}" (syscall)
2019-07-31 23:41:22 +02:00
);
}
///
/// 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.
///
inline fn syscall1(syscall: u32, arg: u32) u32 {
return asm volatile (
\\int $0x80
: [ret] "={eax}" (-> u32)
: [syscall] "{eax}" (syscall),
[arg1] "{ebx}" (arg)
2019-07-31 23:41:22 +02:00
);
}
///
/// 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.
///
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)
2019-07-31 23:41:22 +02:00
);
}
///
/// 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.
///
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)
2019-07-31 23:41:22 +02:00
);
}
///
/// 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.
///
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)
2019-07-31 23:41:22 +02:00
);
}
///
/// 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.
///
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)
2019-07-31 23:41:22 +02:00
);
}
///
/// 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.
///
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"),
2019-07-31 23:41:22 +02:00
};
}
///
/// Initialise syscalls. Registers the isr associated with INTERRUPT.
///
pub fn init(comptime options: type) void {
log.logInfo("Init syscalls\n");
isr.registerIsr(INTERRUPT, handle) catch unreachable;
log.logInfo("Done\n");
if (options.rt_test) runtimeTests();
}
/// Tests
var testInt: u32 = 0;
fn testHandler0(ctx: *arch.InterruptContext) u32 {
testInt += 1;
return 0;
}
fn testHandler1(ctx: *arch.InterruptContext) u32 {
testInt += syscallArg(ctx, 0);
return 1;
}
fn testHandler2(ctx: *arch.InterruptContext) u32 {
testInt += syscallArg(ctx, 1);
return testHandler1(ctx) + 1;
}
fn testHandler3(ctx: *arch.InterruptContext) u32 {
testInt += syscallArg(ctx, 2);
return testHandler2(ctx) + 1;
}
fn testHandler4(ctx: *arch.InterruptContext) u32 {
testInt += syscallArg(ctx, 3);
return testHandler3(ctx) + 1;
}
fn testHandler5(ctx: *arch.InterruptContext) u32 {
testInt += syscallArg(ctx, 4);
return testHandler4(ctx) + 1;
}
test "registerSyscall returns SyscallExists" {
2019-09-18 00:04:01 +02:00
registerSyscall(123, testHandler0) catch unreachable;
registerSyscall(123, testHandler0) catch |err| {
2019-07-31 23:41:22 +02:00
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");
}