Merge pull request #56 from SamTebbs33/feature/syscalls
Add x86 syscalls
This commit is contained in:
commit
b2ce5f6310
6 changed files with 339 additions and 7 deletions
|
@ -7,6 +7,7 @@ const irq = @import("irq.zig");
|
|||
const isr = @import("isr.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const pit = @import("pit.zig");
|
||||
const syscalls = @import("syscalls.zig");
|
||||
|
||||
pub const InterruptContext = struct {
|
||||
// Extra segments
|
||||
|
@ -58,6 +59,8 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator, compt
|
|||
|
||||
paging.init(mem_profile, allocator);
|
||||
|
||||
syscalls.init(options);
|
||||
|
||||
// Enable interrupts
|
||||
enableInterrupts();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
const panic = @import("../../panic.zig");
|
||||
const idt = @import("idt.zig");
|
||||
const arch = @import("arch.zig");
|
||||
const syscalls = @import("syscalls.zig");
|
||||
|
||||
const NUMBER_OF_ENTRIES: u16 = 32;
|
||||
|
||||
|
@ -39,6 +40,7 @@ extern fn isr28() void;
|
|||
extern fn isr29() void;
|
||||
extern fn isr30() void;
|
||||
extern fn isr31() void;
|
||||
extern fn isr128() void;
|
||||
|
||||
/// The exception messaged that is printed when a exception happens
|
||||
const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8 {
|
||||
|
@ -76,8 +78,18 @@ const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const
|
|||
"Reserved"
|
||||
};
|
||||
|
||||
/// Errors that an isr function can return
|
||||
pub const IsrError = error {
|
||||
UnrecognisedIsr
|
||||
};
|
||||
|
||||
/// An isr handler. Takes an interrupt context and returns void.
|
||||
/// Should finish quickly to avoid delaying further interrupts and the previously running code
|
||||
pub const IsrHandler = fn(*arch.InterruptContext)void;
|
||||
|
||||
// The of exception handlers initialised to unhandled.
|
||||
var isr_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void{unhandled} ** NUMBER_OF_ENTRIES;
|
||||
var isr_handlers: [NUMBER_OF_ENTRIES]IsrHandler = []IsrHandler{unhandled} ** NUMBER_OF_ENTRIES;
|
||||
var syscall_handler: IsrHandler = unhandled;
|
||||
|
||||
///
|
||||
/// A dummy handler that will make a call to panic as it is a unhandled exception.
|
||||
|
@ -91,6 +103,17 @@ fn unhandled(context: *arch.InterruptContext) void {
|
|||
panic.panicFmt(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num);
|
||||
}
|
||||
|
||||
///
|
||||
/// Checks if the isr is valid and returns true if it is, else false.
|
||||
/// To be valid it must be greater than or equal to 0 and less than NUMBER_OF_ENTRIES.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN isr_num: u16 - The isr number to check
|
||||
///
|
||||
pub fn isValidIsr(isr_num: u32) bool {
|
||||
return isr_num >= 0 and isr_num < NUMBER_OF_ENTRIES;
|
||||
}
|
||||
|
||||
///
|
||||
/// The exception handler that each of the exceptions will call when a exception happens.
|
||||
///
|
||||
|
@ -100,7 +123,13 @@ fn unhandled(context: *arch.InterruptContext) void {
|
|||
///
|
||||
export fn isrHandler(context: *arch.InterruptContext) void {
|
||||
const isr_num = context.int_num;
|
||||
isr_handlers[isr_num](context);
|
||||
if (isr_num == syscalls.INTERRUPT) {
|
||||
syscall_handler(context);
|
||||
} else if (isValidIsr(isr_num)) {
|
||||
isr_handlers[isr_num](context);
|
||||
} else {
|
||||
panic.panicFmt(null, "Unrecognised isr: {}\n", isr_num);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -109,8 +138,17 @@ export fn isrHandler(context: *arch.InterruptContext) void {
|
|||
/// Arguments:
|
||||
/// IN irq_num: u16 - The exception number to register.
|
||||
///
|
||||
pub fn registerIsr(isr_num: u16, handler: fn(*arch.InterruptContext)void) void {
|
||||
isr_handlers[isr_num] = handler;
|
||||
/// Errors:
|
||||
/// IsrError.UnrecognisedIsr - If `isr_num` is invalid (see isValidIsr)
|
||||
///
|
||||
pub fn registerIsr(isr_num: u16, handler: fn(*arch.InterruptContext)void) !void {
|
||||
if (isr_num == syscalls.INTERRUPT) {
|
||||
syscall_handler = handler;
|
||||
} else if (isValidIsr(isr_num)) {
|
||||
isr_handlers[isr_num] = handler;
|
||||
} else {
|
||||
return IsrError.UnrecognisedIsr;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -160,4 +198,5 @@ pub fn init() void {
|
|||
idt.openInterruptGate(29, isr29);
|
||||
idt.openInterruptGate(30, isr30);
|
||||
idt.openInterruptGate(31, isr31);
|
||||
}
|
||||
idt.openInterruptGate(syscalls.INTERRUPT, isr128);
|
||||
}
|
||||
|
|
|
@ -87,3 +87,4 @@ isrGenerator 28
|
|||
isrGenerator 29
|
||||
isrGenerator 30
|
||||
isrGenerator 31
|
||||
isrGenerator 128
|
||||
|
|
|
@ -142,7 +142,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void
|
|||
mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch unreachable;
|
||||
const dir_physaddr = @ptrToInt(kernel_directory) - constants.KERNEL_ADDR_OFFSET;
|
||||
asm volatile ("mov %[addr], %%cr3" :: [addr] "{eax}" (dir_physaddr));
|
||||
isr.registerIsr(14, pageFault);
|
||||
isr.registerIsr(14, pageFault) catch unreachable;
|
||||
}
|
||||
|
||||
test "isAligned" {
|
||||
|
|
287
src/kernel/arch/x86/syscalls.zig
Normal file
287
src/kernel/arch/x86/syscalls.zig
Normal file
|
@ -0,0 +1,287 @@
|
|||
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 {
|
||||
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
|
||||
///
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// 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")
|
||||
};
|
||||
}
|
||||
|
||||
///
|
||||
/// 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" {
|
||||
registerSyscall(123, testHandler) catch unreachable;
|
||||
registerSyscall(123, testHandler) 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");
|
||||
}
|
|
@ -2,5 +2,7 @@ def getTestCases(TestCase):
|
|||
return [
|
||||
TestCase("GDT init", [r"Init gdt", r"Done"]),
|
||||
TestCase("IDT init", [r"Init idt", r"Done"]),
|
||||
TestCase("PIT init", [r"Init pit", r".+", "Done"])
|
||||
TestCase("PIT init", [r"Init pit", r".+", "Done"]),
|
||||
TestCase("Syscalls init", [r"Init syscalls", "Done"]),
|
||||
TestCase("Syscall tests", [r"Syscalls: Tested no args", r"Syscalls: Tested 1 arg", r"Syscalls: Tested 2 args", r"Syscalls: Tested 3 args", r"Syscalls: Tested 4 args", r"Syscalls: Tested 5 args"])
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue