Add x86 syscalls
This commit is contained in:
		
							parent
							
								
									190f21f907
								
							
						
					
					
						commit
						60ba451aec
					
				
					 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Tebbs
						Sam Tebbs