Added unit and runtime tests
Also fixed some typos Also changed the panic a bit Removed changed to unnecessary files Fixed merge Feedback Fixed tests
This commit is contained in:
		
							parent
							
								
									d306078c62
								
							
						
					
					
						commit
						42bdbf6b7f
					
				
					 10 changed files with 438 additions and 159 deletions
				
			
		|  | @ -238,6 +238,7 @@ test "" { | |||
|     _ = @import("gdt.zig"); | ||||
|     _ = @import("idt.zig"); | ||||
|     _ = @import("pic.zig"); | ||||
|     _ = @import("isr.zig"); | ||||
|     _ = @import("syscalls.zig"); | ||||
|     _ = @import("paging.zig"); | ||||
| } | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("ar | |||
| const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig"); | ||||
| 
 | ||||
| /// The structure that contains all the information that each IDT entry needs. | ||||
| const IdtEntry = packed struct { | ||||
| pub const IdtEntry = packed struct { | ||||
|     /// The lower 16 bits of the base address of the interrupt handler offset. | ||||
|     base_low: u16, | ||||
| 
 | ||||
|  | @ -88,12 +88,12 @@ const PRIVILEGE_RING_2: u2 = 0x2; | |||
| /// Privilege level 3. User land. The privilege level the calling descriptor minimum will have. | ||||
| const PRIVILEGE_RING_3: u2 = 0x3; | ||||
| 
 | ||||
| /// The total number of entries the IDT can have (2^8). | ||||
| const NUMBER_OF_ENTRIES: u16 = 256; | ||||
| 
 | ||||
| /// The total size of all the IDT entries (minus 1). | ||||
| const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1; | ||||
| 
 | ||||
| /// The total number of entries the IDT can have (2^8). | ||||
| pub const NUMBER_OF_ENTRIES: u16 = 256; | ||||
| 
 | ||||
| /// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the | ||||
| /// size. | ||||
| var idt_ptr: IdtPtr = IdtPtr{ | ||||
|  | @ -150,7 +150,7 @@ fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2) IdtEntry { | |||
| /// Return: bool | ||||
| ///     Whether the provided IDT entry is open or not. | ||||
| /// | ||||
| fn isIdtOpen(entry: IdtEntry) bool { | ||||
| pub fn isIdtOpen(entry: IdtEntry) bool { | ||||
|     return entry.present == 1; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,46 +1,31 @@ | |||
| // Zig version: 0.4.0 | ||||
| 
 | ||||
| const panic = @import("../../panic.zig").panic; | ||||
| const idt = @import("idt.zig"); | ||||
| const arch = @import("arch.zig"); | ||||
| const std = @import("std"); | ||||
| const builtin = @import("builtin"); | ||||
| const is_test = builtin.is_test; | ||||
| const expect = std.testing.expect; | ||||
| const expectEqual = std.testing.expectEqual; | ||||
| const expectError = std.testing.expectError; | ||||
| const build_options = @import("build_options"); | ||||
| const mock_path = build_options.arch_mock_path; | ||||
| const syscalls = @import("syscalls.zig"); | ||||
| const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic; | ||||
| const idt = if (is_test) @import(mock_path ++ "idt_mock.zig") else @import("idt.zig"); | ||||
| const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig"); | ||||
| const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig"); | ||||
| 
 | ||||
| const NUMBER_OF_ENTRIES: u16 = 32; | ||||
| /// The error set for the ISR. This will be from installing a ISR handler. | ||||
| pub const IsrError = error{ | ||||
|     /// The ISR index is invalid. | ||||
|     InvalidIsr, | ||||
| 
 | ||||
| // The external assembly that is fist called to set up the exception handler. | ||||
| extern fn isr0() void; | ||||
| extern fn isr1() void; | ||||
| extern fn isr2() void; | ||||
| extern fn isr3() void; | ||||
| extern fn isr4() void; | ||||
| extern fn isr5() void; | ||||
| extern fn isr6() void; | ||||
| extern fn isr7() void; | ||||
| extern fn isr8() void; | ||||
| extern fn isr9() void; | ||||
| extern fn isr10() void; | ||||
| extern fn isr11() void; | ||||
| extern fn isr12() void; | ||||
| extern fn isr13() void; | ||||
| extern fn isr14() void; | ||||
| extern fn isr15() void; | ||||
| extern fn isr16() void; | ||||
| extern fn isr17() void; | ||||
| extern fn isr18() void; | ||||
| extern fn isr19() void; | ||||
| extern fn isr20() void; | ||||
| extern fn isr21() void; | ||||
| extern fn isr22() void; | ||||
| extern fn isr23() void; | ||||
| extern fn isr24() void; | ||||
| extern fn isr25() void; | ||||
| extern fn isr26() void; | ||||
| extern fn isr27() void; | ||||
| extern fn isr28() void; | ||||
| extern fn isr29() void; | ||||
| extern fn isr30() void; | ||||
| extern fn isr31() void; | ||||
| extern fn isr128() void; | ||||
|     /// A ISR handler already exists. | ||||
|     IsrExists, | ||||
| }; | ||||
| 
 | ||||
| /// The type of a ISR handler. A function that takes a interrupt context and returns void. | ||||
| const IsrHandler = fn (*arch.InterruptContext) void; | ||||
| 
 | ||||
| /// The number of ISR entries. | ||||
| const NUMBER_OF_ENTRIES: u8 = 32; | ||||
| 
 | ||||
| /// The exception messaged that is printed when a exception happens | ||||
| const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8{ | ||||
|  | @ -78,55 +63,139 @@ 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}; | ||||
| /// Divide By Zero exception. | ||||
| pub const DIVIDE_BY_ZERO: u8 = 0; | ||||
| 
 | ||||
| /// 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; | ||||
| /// Single Step (Debugger) exception. | ||||
| pub const SINGLE_STEP_DEBUG: u8 = 1; | ||||
| 
 | ||||
| // The of exception handlers initialised to unhandled. | ||||
| var isr_handlers: [NUMBER_OF_ENTRIES]IsrHandler = [_]IsrHandler{unhandled} ** NUMBER_OF_ENTRIES; | ||||
| var syscall_handler: IsrHandler = unhandled; | ||||
| /// Non Maskable Interrupt exception. | ||||
| pub const NON_MASKABLE_INTERRUPT: u8 = 2; | ||||
| 
 | ||||
| /// | ||||
| /// A dummy handler that will make a call to panic as it is a unhandled exception. | ||||
| /// | ||||
| /// Arguments: | ||||
| ///     IN context: *arch.InterruptContext - Pointer to the exception context containing the | ||||
| ///                                          contents of the register at the time of the exception. | ||||
| /// | ||||
| fn unhandled(context: *arch.InterruptContext) void { | ||||
|     const interrupt_num = context.int_num; | ||||
|     panic(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num); | ||||
| } | ||||
| /// Breakpoint (Debugger) exception. | ||||
| pub const BREAKPOINT_DEBUG: u8 = 3; | ||||
| 
 | ||||
| /// | ||||
| /// 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; | ||||
| } | ||||
| /// Overflow exception. | ||||
| pub const OVERFLOW: u8 = 4; | ||||
| 
 | ||||
| /// Bound Range Exceeded exception. | ||||
| pub const BOUND_RANGE_EXCEEDED: u8 = 5; | ||||
| 
 | ||||
| /// Invalid Opcode exception. | ||||
| pub const INVALID_OPCODE: u8 = 6; | ||||
| 
 | ||||
| /// No Coprocessor, Device Not Available exception. | ||||
| pub const DEVICE_NOT_AVAILABLE: u8 = 7; | ||||
| 
 | ||||
| /// Double Fault exception. | ||||
| pub const DOUBLE_FAULT: u8 = 8; | ||||
| 
 | ||||
| /// Coprocessor Segment Overrun exception. | ||||
| pub const COPROCESSOR_SEGMENT_OVERRUN: u8 = 9; | ||||
| 
 | ||||
| /// Invalid Task State Segment (TSS) exception. | ||||
| pub const INVALID_TASK_STATE_SEGMENT: u8 = 10; | ||||
| 
 | ||||
| /// Segment Not Present exception. | ||||
| pub const SEGMENT_NOT_PRESENT: u8 = 11; | ||||
| 
 | ||||
| /// Stack Segment Overrun exception. | ||||
| pub const STACK_SEGMENT_FAULT: u8 = 12; | ||||
| 
 | ||||
| /// General Protection Fault exception. | ||||
| pub const GENERAL_PROTECTION_FAULT: u8 = 13; | ||||
| 
 | ||||
| /// Page Fault exception. | ||||
| pub const PAGE_FAULT: u8 = 14; | ||||
| 
 | ||||
| /// x87 FPU Floating Point Error exception. | ||||
| pub const X87_FLOAT_POINT: u8 = 16; | ||||
| 
 | ||||
| /// Alignment Check exception. | ||||
| pub const ALIGNMENT_CHECK: u8 = 17; | ||||
| 
 | ||||
| /// Machine Check exception. | ||||
| pub const MACHINE_CHECK: u8 = 18; | ||||
| 
 | ||||
| /// SIMD Floating Point exception. | ||||
| pub const SIMD_FLOAT_POINT: u8 = 19; | ||||
| 
 | ||||
| /// Virtualisation exception. | ||||
| pub const VIRTUALISATION: u8 = 20; | ||||
| 
 | ||||
| /// Security exception. | ||||
| pub const SECURITY: u8 = 30; | ||||
| 
 | ||||
| /// The of exception handlers initialised to null. Need to open a ISR for these to be valid. | ||||
| var isr_handlers: [NUMBER_OF_ENTRIES]?IsrHandler = [_]?IsrHandler{null} ** NUMBER_OF_ENTRIES; | ||||
| 
 | ||||
| /// The syscall hander. | ||||
| var syscall_handler: ?IsrHandler = null; | ||||
| 
 | ||||
| // The external assembly that is fist called to set up the exception handler. | ||||
| extern fn isr0() void; | ||||
| extern fn isr1() void; | ||||
| extern fn isr2() void; | ||||
| extern fn isr3() void; | ||||
| extern fn isr4() void; | ||||
| extern fn isr5() void; | ||||
| extern fn isr6() void; | ||||
| extern fn isr7() void; | ||||
| extern fn isr8() void; | ||||
| extern fn isr9() void; | ||||
| extern fn isr10() void; | ||||
| extern fn isr11() void; | ||||
| extern fn isr12() void; | ||||
| extern fn isr13() void; | ||||
| extern fn isr14() void; | ||||
| extern fn isr15() void; | ||||
| extern fn isr16() void; | ||||
| extern fn isr17() void; | ||||
| extern fn isr18() void; | ||||
| extern fn isr19() void; | ||||
| extern fn isr20() void; | ||||
| extern fn isr21() void; | ||||
| extern fn isr22() void; | ||||
| extern fn isr23() void; | ||||
| extern fn isr24() void; | ||||
| extern fn isr25() void; | ||||
| extern fn isr26() void; | ||||
| extern fn isr27() void; | ||||
| extern fn isr28() void; | ||||
| extern fn isr29() void; | ||||
| extern fn isr30() void; | ||||
| extern fn isr31() void; | ||||
| extern fn isr128() void; | ||||
| 
 | ||||
| /// | ||||
| /// The exception handler that each of the exceptions will call when a exception happens. | ||||
| /// | ||||
| /// Arguments: | ||||
| ///     IN context: *arch.InterruptContext - Pointer to the exception context containing the | ||||
| ///                                          contents of the register at the time of the exception. | ||||
| ///     IN ctx: *arch.InterruptContext - Pointer to the exception context containing the contents | ||||
| ///                                      of the register at the time of the exception. | ||||
| /// | ||||
| export fn isrHandler(context: *arch.InterruptContext) void { | ||||
|     const isr_num = context.int_num; | ||||
| export fn isrHandler(ctx: *arch.InterruptContext) void { | ||||
|     // Get the interrupt number | ||||
|     const isr_num = ctx.int_num; | ||||
| 
 | ||||
|     if (isValidIsr(isr_num)) { | ||||
|         if (isr_num == syscalls.INTERRUPT) { | ||||
|         syscall_handler(context); | ||||
|     } else if (isValidIsr(isr_num)) { | ||||
|         isr_handlers[isr_num](context); | ||||
|             // A syscall, so use the syscall handler | ||||
|             if (syscall_handler) |handler| { | ||||
|                 handler(ctx); | ||||
|             } else { | ||||
|         panic(null, "Unrecognised isr: {}\n", isr_num); | ||||
|                 panic(@errorReturnTrace(), "Syscall handler not registered\n"); | ||||
|             } | ||||
|         } else { | ||||
|             if (isr_handlers[isr_num]) |handler| { | ||||
|                 // Regular ISR exception, if there is one registered. | ||||
|                 handler(ctx); | ||||
|             } else { | ||||
|                 panic(@errorReturnTrace(), "ISR not registered to: {}-{}\n", isr_num, exception_msg[isr_num]); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         panic(@errorReturnTrace(), "Invalid ISR index: {}\n", isr_num); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -140,45 +209,67 @@ export fn isrHandler(context: *arch.InterruptContext) void { | |||
| fn openIsr(index: u8, handler: idt.InterruptHandler) void { | ||||
|     idt.openInterruptGate(index, handler) catch |err| switch (err) { | ||||
|         error.IdtEntryExists => { | ||||
|             panic(@errorReturnTrace(), "Error opening ISR number: {} exists", index); | ||||
|             panic(@errorReturnTrace(), "Error opening ISR number: {} exists\n", index); | ||||
|         }, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// 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 | ||||
| /// | ||||
| /// Return: bool | ||||
| ///     Whether a ISR hander index if valid. | ||||
| /// | ||||
| pub fn isValidIsr(isr_num: u32) bool { | ||||
|     return isr_num < NUMBER_OF_ENTRIES or isr_num == syscalls.INTERRUPT; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Register an exception by setting its exception handler to the given function. | ||||
| /// | ||||
| /// Arguments: | ||||
| ///     IN irq_num: u16 - The exception number to register. | ||||
| /// | ||||
| /// Errors: | ||||
| ///     IsrError.UnrecognisedIsr - If `isr_num` is invalid (see isValidIsr) | ||||
| /// Errors: IsrError | ||||
| ///     IsrError.InvalidIsr - If the ISR index is invalid (see isValidIsr). | ||||
| ///     IsrError.IsrExists  - If the ISR handler has already been registered. | ||||
| /// | ||||
| pub fn registerIsr(isr_num: u16, handler: fn (*arch.InterruptContext) void) !void { | ||||
| pub fn registerIsr(isr_num: u16, handler: IsrHandler) IsrError!void { | ||||
|     // Check if a valid ISR index | ||||
|     if (isValidIsr(isr_num)) { | ||||
|         if (isr_num == syscalls.INTERRUPT) { | ||||
|         syscall_handler = handler; | ||||
|     } else if (isValidIsr(isr_num)) { | ||||
|         isr_handlers[isr_num] = handler; | ||||
|             // Syscall handler | ||||
|             if (syscall_handler) |_| { | ||||
|                 // One already registered | ||||
|                 return IsrError.IsrExists; | ||||
|             } else { | ||||
|         return IsrError.UnrecognisedIsr; | ||||
|                 // Register a handler | ||||
|                 syscall_handler = handler; | ||||
|             } | ||||
|         } else { | ||||
|             if (isr_handlers[isr_num]) |_| { | ||||
|                 // One already registered | ||||
|                 return IsrError.IsrExists; | ||||
|             } else { | ||||
|                 // Register a handler | ||||
|                 isr_handlers[isr_num] = handler; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| /// | ||||
| /// Unregister an exception by setting its exception handler to the unhandled function call to | ||||
| /// panic. | ||||
| /// | ||||
| /// Arguments: | ||||
| ///     IN irq_num: u16 - The exception number to unregister. | ||||
| /// | ||||
| pub fn unregisterIsr(isr_num: u16) void { | ||||
|     isr_handlers[isr_num] = unhandled; | ||||
|     } else { | ||||
|         return IsrError.InvalidIsr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Initialise the exception and opening up all the IDT interrupt gates for each exception. | ||||
| /// | ||||
| pub fn init() void { | ||||
|     log.logInfo("Init isr\n"); | ||||
| 
 | ||||
|     openIsr(0, isr0); | ||||
|     openIsr(1, isr1); | ||||
|     openIsr(2, isr2); | ||||
|  | @ -212,4 +303,160 @@ pub fn init() void { | |||
|     openIsr(30, isr30); | ||||
|     openIsr(31, isr31); | ||||
|     openIsr(syscalls.INTERRUPT, isr128); | ||||
| 
 | ||||
|     log.logInfo("Done\n"); | ||||
| 
 | ||||
|     if (build_options.rt_test) runtimeTests(); | ||||
| } | ||||
| 
 | ||||
| extern fn testFunction0() void {} | ||||
| fn testFunction1(ctx: *arch.InterruptContext) void {} | ||||
| fn testFunction2(ctx: *arch.InterruptContext) void {} | ||||
| fn testFunction3(ctx: *arch.InterruptContext) void {} | ||||
| fn testFunction4(ctx: *arch.InterruptContext) void {} | ||||
| 
 | ||||
| test "openIsr" { | ||||
|     idt.initTest(); | ||||
|     defer idt.freeTest(); | ||||
| 
 | ||||
|     const index = u8(0); | ||||
|     const handler = testFunction0; | ||||
|     const ret: idt.IdtError!void = {}; | ||||
| 
 | ||||
|     idt.addTestParams("openInterruptGate", index, handler, ret); | ||||
| 
 | ||||
|     openIsr(index, handler); | ||||
| } | ||||
| 
 | ||||
| test "isValidIsr" { | ||||
|     comptime var i = 0; | ||||
|     inline while (i < NUMBER_OF_ENTRIES) : (i += 1) { | ||||
|         expectEqual(true, isValidIsr(i)); | ||||
|     } | ||||
| 
 | ||||
|     expectEqual(true, isValidIsr(syscalls.INTERRUPT)); | ||||
| 
 | ||||
|     expectEqual(false, isValidIsr(200)); | ||||
| } | ||||
| 
 | ||||
| test "registerIsr re-register syscall handler" { | ||||
|     // Pre testing | ||||
|     expect(null == syscall_handler); | ||||
| 
 | ||||
|     // Call function | ||||
|     try registerIsr(syscalls.INTERRUPT, testFunction3); | ||||
|     expectError(IsrError.IsrExists, registerIsr(syscalls.INTERRUPT, testFunction4)); | ||||
| 
 | ||||
|     // Post testing | ||||
|     expectEqual(testFunction3, syscall_handler.?); | ||||
| 
 | ||||
|     // Clean up | ||||
|     syscall_handler = null; | ||||
| } | ||||
| 
 | ||||
| test "registerIsr register syscall handler" { | ||||
|     // Pre testing | ||||
|     expect(null == syscall_handler); | ||||
| 
 | ||||
|     // Call function | ||||
|     try registerIsr(syscalls.INTERRUPT, testFunction3); | ||||
| 
 | ||||
|     // Post testing | ||||
|     expectEqual(testFunction3, syscall_handler.?); | ||||
| 
 | ||||
|     // Clean up | ||||
|     syscall_handler = null; | ||||
| } | ||||
| 
 | ||||
| test "registerIsr re-register isr handler" { | ||||
|     // Pre testing | ||||
|     for (isr_handlers) |h| { | ||||
|         expect(null == h); | ||||
|     } | ||||
| 
 | ||||
|     // Call function | ||||
|     try registerIsr(0, testFunction1); | ||||
|     expectError(IsrError.IsrExists, registerIsr(0, testFunction2)); | ||||
| 
 | ||||
|     // Post testing | ||||
|     for (isr_handlers) |h, i| { | ||||
|         if (i != 0) { | ||||
|             expect(null == h); | ||||
|         } else { | ||||
|             expectEqual(testFunction1, h.?); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Clean up | ||||
|     isr_handlers[0] = null; | ||||
| } | ||||
| 
 | ||||
| test "registerIsr register isr handler" { | ||||
|     // Pre testing | ||||
|     for (isr_handlers) |h| { | ||||
|         expect(null == h); | ||||
|     } | ||||
| 
 | ||||
|     // Call function | ||||
|     try registerIsr(0, testFunction1); | ||||
| 
 | ||||
|     // Post testing | ||||
|     for (isr_handlers) |h, i| { | ||||
|         if (i != 0) { | ||||
|             expect(null == h); | ||||
|         } else { | ||||
|             expectEqual(testFunction1, h.?); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Clean up | ||||
|     isr_handlers[0] = null; | ||||
| } | ||||
| 
 | ||||
| test "registerIsr invalid isr index" { | ||||
|     expectError(IsrError.InvalidIsr, registerIsr(200, testFunction1)); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Test that all handers are null at initialisation. | ||||
| /// | ||||
| fn rt_unregisteredHandlers() void { | ||||
|     // Ensure all ISR are not registered yet | ||||
|     for (isr_handlers) |h, i| { | ||||
|         if (h) |_| { | ||||
|             panic(@errorReturnTrace(), "Handler found for ISR: {}-{}\n", i, h); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (syscall_handler) |h| { | ||||
|         panic(@errorReturnTrace(), "Pre-testing failed for syscall: {}\n", h); | ||||
|     } | ||||
| 
 | ||||
|     log.logInfo("ISR: Tested registered handlers\n"); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Test that all IDT entries for the ISRs are open. | ||||
| /// | ||||
| fn rt_openedIdtEntries() void { | ||||
|     const loaded_idt = arch.sidt(); | ||||
|     const idt_entries = @intToPtr([*]idt.IdtEntry, loaded_idt.base)[0..idt.NUMBER_OF_ENTRIES]; | ||||
| 
 | ||||
|     for (idt_entries) |entry, i| { | ||||
|         if (isValidIsr(i)) { | ||||
|             if (!idt.isIdtOpen(entry)) { | ||||
|                 panic(@errorReturnTrace(), "IDT entry for {} is not open\n", i); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     log.logInfo("ISR: Tested opened IDT entries\n"); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Run all the runtime tests. | ||||
| /// | ||||
| fn runtimeTests() void { | ||||
|     rt_unregisteredHandlers(); | ||||
|     rt_openedIdtEntries(); | ||||
| } | ||||
|  |  | |||
|  | @ -37,10 +37,10 @@ const PagingError = error{ | |||
|     /// Physical and virtual addresses don't cover spaces of the same size. | ||||
|     PhysicalVirtualMismatch, | ||||
| 
 | ||||
|     /// Physical addressses aren't aligned by page size. | ||||
|     /// Physical addresses aren't aligned by page size. | ||||
|     UnalignedPhysAddresses, | ||||
| 
 | ||||
|     /// Virtual addressses aren't aligned by page size. | ||||
|     /// Virtual addresses aren't aligned by page size. | ||||
|     UnalignedVirtAddresses, | ||||
| }; | ||||
| 
 | ||||
|  | @ -301,7 +301,7 @@ fn mapDir(dir: *Directory, virt_start: usize, virt_end: usize, phys_start: usize | |||
| /// Called when a page fault occurs. | ||||
| /// | ||||
| /// Arguments: | ||||
| ///     IN state: *arch.InterruptContext - The CPU's state when the fault occured. | ||||
| ///     IN state: *arch.InterruptContext - The CPU's state when the fault occurred. | ||||
| /// | ||||
| fn pageFault(state: *arch.InterruptContext) void { | ||||
|     @panic("Page fault"); | ||||
|  | @ -334,7 +334,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void | |||
|         panic(@errorReturnTrace(), "Failed to map kernel directory: {}\n", e); | ||||
|     }; | ||||
|     const tty_addr = tty.getVideoBufferAddress(); | ||||
|     // If the previous mappping space didn't cover the tty buffer, do so now | ||||
|     // If the previous mapping space didn't cover the tty buffer, do so now | ||||
|     if (v_start > tty_addr or v_end <= tty_addr) { | ||||
|         const tty_phys = virtToPhys(tty_addr); | ||||
|         const tty_buff_size = 32 * 1024; | ||||
|  | @ -348,7 +348,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void | |||
|         : | ||||
|         : [addr] "{eax}" (dir_physaddr) | ||||
|     ); | ||||
|     isr.registerIsr(14, if (options.rt_test) rt_pageFault else pageFault) catch |e| { | ||||
|     isr.registerIsr(isr.PAGE_FAULT, if (options.rt_test) rt_pageFault else pageFault) catch |e| { | ||||
|         panic(@errorReturnTrace(), "Failed to register page fault ISR: {}\n", e); | ||||
|     }; | ||||
|     log.logInfo("Done\n"); | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| // Zig version: 0.4.0 | ||||
| 
 | ||||
| const arch = @import("arch.zig"); | ||||
| const assert = @import("std").debug.assert; | ||||
| const irq = @import("irq.zig"); | ||||
| const pic = @import("pic.zig"); | ||||
| const log = @import("../../log.zig"); | ||||
| const panic = @import("../../panic.zig").panic; | ||||
| 
 | ||||
| // The port addresses of the PIT registers | ||||
| 
 | ||||
| /// The port address for the PIT data register for counter 0. This is going to be used as the | ||||
| /// system clock. | ||||
| const COUNTER_0_REGISTER: u16 = 0x40; | ||||
|  |  | |||
|  | @ -29,17 +29,21 @@ var handlers: [NUM_HANDLERS]?SyscallHandler = [_]?SyscallHandler{null} ** NUM_HA | |||
| /// 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. | ||||
| /// 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. | ||||
| ///     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 | ||||
|  | @ -80,6 +84,9 @@ pub fn registerSyscall(syscall: u8, handler: SyscallHandler) SyscallError!void { | |||
| /// 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 | ||||
|  | @ -95,6 +102,9 @@ inline fn syscall0(syscall: u32) u32 { | |||
| ///     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 | ||||
|  | @ -112,6 +122,9 @@ inline fn syscall1(syscall: u32, arg: u32) u32 { | |||
| ///     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 | ||||
|  | @ -131,6 +144,9 @@ inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 { | |||
| ///     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 | ||||
|  | @ -152,6 +168,9 @@ inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 { | |||
| ///     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 | ||||
|  | @ -175,6 +194,9 @@ inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32 | |||
| ///     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 | ||||
|  | @ -189,12 +211,16 @@ inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg | |||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Gets the syscall argument according to the given index. 0 => ebx, 1 => ecx, 2 => edx, 3 => esi and 4 => edi. | ||||
| /// 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,13 +1,16 @@ | |||
| const std = @import("std"); | ||||
| const builtin = @import("builtin"); | ||||
| const is_test = builtin.is_test; | ||||
| const build_options = @import("build_options"); | ||||
| const mock_path = build_options.mock_path; | ||||
| const arch = @import("arch.zig").internals; | ||||
| const multiboot = @import("multiboot.zig"); | ||||
| const tty = @import("tty.zig"); | ||||
| const vga = @import("vga.zig"); | ||||
| const log = @import("log.zig"); | ||||
| const serial = @import("serial.zig"); | ||||
| const mem = if (builtin.is_test) @import(build_options.mock_path ++ "mem_mock.zig") else @import("mem.zig"); | ||||
| const mem = if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig"); | ||||
| const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic; | ||||
| 
 | ||||
| comptime { | ||||
|     switch (builtin.arch) { | ||||
|  | @ -20,10 +23,6 @@ comptime { | |||
| // from the linker script | ||||
| export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefined; | ||||
| 
 | ||||
| // Need to import this as we need the panic to be in the root source file, or zig will just use the | ||||
| // builtin panic and just loop, which is what we don't want | ||||
| const panic_root = @import("panic.zig").panic; | ||||
| 
 | ||||
| // Just call the panic function, as this need to be in the root source file | ||||
| pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | ||||
|     @setCold(true); | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ def get_test_cases(TestCase): | |||
|             TestCase("IDT tests", [r"IDT: Tested loading IDT"]), | ||||
|             TestCase("PIC init", [r"Init pic", r"Done"]), | ||||
|             TestCase("PIC tests", [r"PIC: Tested masking"]), | ||||
|             TestCase("ISR init", [r"Init isr", r"Done"]), | ||||
|             TestCase("ISR tests", [r"ISR: Tested registered handlers", r"ISR: Tested opened IDT entries"]), | ||||
|             TestCase("PIT init", [r"Init pit", r".+", r"Done"]), | ||||
|             TestCase("Paging init", [r"Init paging", r"Done"]), | ||||
|             TestCase("Paging tests", [r"Paging: Tested accessing unmapped memory", r"Paging: Tested accessing mapped memory"]), | ||||
|  |  | |||
|  | @ -21,9 +21,9 @@ const IdtEntry = packed struct { | |||
| // Need to use the type from the source file so that types match | ||||
| pub const IdtPtr = src_idt.IdtPtr; | ||||
| 
 | ||||
| pub const InterruptHandler = extern fn () void; | ||||
| pub const InterruptHandler = src_idt.InterruptHandler; | ||||
| 
 | ||||
| pub const IdtError = error{IdtEntryExists}; | ||||
| pub const IdtError = src_idt.IdtError; | ||||
| 
 | ||||
| const TASK_GATE: u4 = 0x5; | ||||
| const INTERRUPT_GATE: u4 = 0xE; | ||||
|  | @ -39,7 +39,7 @@ const NUMBER_OF_ENTRIES: u16 = 256; | |||
| const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1; | ||||
| 
 | ||||
| pub fn openInterruptGate(index: u8, handler: InterruptHandler) IdtError!void { | ||||
|     return mock_framework.performAction("openInterruptGate", IdtError!void, port); | ||||
|     return mock_framework.performAction("openInterruptGate", IdtError!void, index, handler); | ||||
| } | ||||
| 
 | ||||
| pub fn init() void { | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ const DataElementType = enum { | |||
|     U32, | ||||
|     PTR_CONST_GdtPtr, | ||||
|     PTR_CONST_IdtPtr, | ||||
|     ERROR_IDTERROR_VOID, | ||||
|     EFN_OVOID, | ||||
|     FN_OVOID, | ||||
|     FN_OUSIZE, | ||||
|     FN_OU16, | ||||
|  | @ -28,6 +30,7 @@ const DataElementType = enum { | |||
|     FN_IU8_IU8_OU16, | ||||
|     FN_IU16_IU8_OVOID, | ||||
|     FN_IU16_IU16_OVOID, | ||||
|     FN_IU8_IEFNOVOID_OERRORIDTERRORVOID, | ||||
|     FN_IPTRCONSTGDTPTR_OVOID, | ||||
|     FN_IPTRCONSTIDTPTR_OVOID, | ||||
| }; | ||||
|  | @ -44,6 +47,8 @@ const DataElement = union(DataElementType) { | |||
|     U32: u32, | ||||
|     PTR_CONST_GdtPtr: *const gdt.GdtPtr, | ||||
|     PTR_CONST_IdtPtr: *const idt.IdtPtr, | ||||
|     ERROR_IDTERROR_VOID: idt.IdtError!void, | ||||
|     EFN_OVOID: extern fn () void, | ||||
|     FN_OVOID: fn () void, | ||||
|     FN_OUSIZE: fn () usize, | ||||
|     FN_OU16: fn () u16, | ||||
|  | @ -53,6 +58,7 @@ const DataElement = union(DataElementType) { | |||
|     FN_IU8_IU8_OU16: fn (u8, u8) u16, | ||||
|     FN_IU16_IU8_OVOID: fn (u16, u8) void, | ||||
|     FN_IU16_IU16_OVOID: fn (u16, u16) void, | ||||
|     FN_IU8_IEFNOVOID_OERRORIDTERRORVOID: fn (u8, extern fn () void) idt.IdtError!void, | ||||
|     FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void, | ||||
|     FN_IPTRCONSTIDTPTR_OVOID: fn (*const idt.IdtPtr) void, | ||||
| }; | ||||
|  | @ -136,6 +142,8 @@ fn Mock() type { | |||
|                 u32 => DataElement{ .U32 = arg }, | ||||
|                 *const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg }, | ||||
|                 *const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg }, | ||||
|                 idt.IdtError!void => DataElement{ .ERROR_IDTERROR_VOID = arg }, | ||||
|                 extern fn () void => DataElement{ .EFN_OVOID = arg }, | ||||
|                 fn () void => DataElement{ .FN_OVOID = arg }, | ||||
|                 fn () usize => DataElement{ .FN_OUSIZE = arg }, | ||||
|                 fn () u16 => DataElement{ .FN_OU16 = arg }, | ||||
|  | @ -147,6 +155,7 @@ fn Mock() type { | |||
|                 fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg }, | ||||
|                 fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg }, | ||||
|                 fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg }, | ||||
|                 fn (u8, extern fn () void) idt.IdtError!void => DataElement{ .FN_IU8_IEFNOVOID_OERRORIDTERRORVOID = arg }, | ||||
|                 else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))), | ||||
|             }; | ||||
|         } | ||||
|  | @ -168,6 +177,8 @@ fn Mock() type { | |||
|                 u32 => DataElementType.U32, | ||||
|                 *const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr, | ||||
|                 *const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr, | ||||
|                 idt.IdtError!void => DataElement.ERROR_IDTERROR_VOID, | ||||
|                 extern fn () void => DataElementType.EFN_OVOID, | ||||
|                 fn () void => DataElementType.FN_OVOID, | ||||
|                 fn () u16 => DataElementType.FN_OU16, | ||||
|                 fn (u16) void => DataElementType.FN_IU16_OVOID, | ||||
|  | @ -178,6 +189,7 @@ fn Mock() type { | |||
|                 fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID, | ||||
|                 fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID, | ||||
|                 fn (*const idt.IdtPtr) void => DataElementType.FN_IPTRCONSTIDTPTR_OVOID, | ||||
|                 fn (u8, extern fn () void) idt.IdtError!void => DataElementType.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID, | ||||
|                 else => @compileError("Type not supported: " ++ @typeName(T)), | ||||
|             }; | ||||
|         } | ||||
|  | @ -201,6 +213,8 @@ fn Mock() type { | |||
|                 u32 => element.U32, | ||||
|                 *const gdt.GdtPtr => element.PTR_CONST_GdtPtr, | ||||
|                 *const idt.IdtPtr => element.PTR_CONST_IdtPtr, | ||||
|                 idt.IdtError!void => element.ERROR_IDTERROR_VOID, | ||||
|                 extern fn () void => element.EFN_OVOID, | ||||
|                 fn () void => element.FN_OVOID, | ||||
|                 fn () u16 => element.FN_OU16, | ||||
|                 fn (u16) void => element.FN_IU16_OVOID, | ||||
|  | @ -211,6 +225,7 @@ fn Mock() type { | |||
|                 fn (u16, u16) void => element.FN_IU16_IU16_OVOID, | ||||
|                 fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID, | ||||
|                 fn (*const idt.IdtPtr) void => element.FN_IPTRCONSTIDTPTR_OVOID, | ||||
|                 fn (u8, extern fn () void) idt.IdtError!void => element.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID, | ||||
|                 else => @compileError("Type not supported: " ++ @typeName(T)), | ||||
|             }; | ||||
|         } | ||||
|  | @ -248,8 +263,7 @@ fn Mock() type { | |||
|         fn expectTest(comptime ExpectedType: type, expected_value: ExpectedType, elem: DataElement) void { | ||||
|             if (ExpectedType == void) { | ||||
|                 // Can't test void as it has no value | ||||
|                 warn("Can not test a value for void\n"); | ||||
|                 expect(false); | ||||
|                 std.debug.panic("Can not test a value for void\n"); | ||||
|             } | ||||
| 
 | ||||
|             // Test that the types match | ||||
|  | @ -291,9 +305,7 @@ fn Mock() type { | |||
| 
 | ||||
|                 return ret; | ||||
|             } else { | ||||
|                 warn("No more test values for the return of function: " ++ fun_name ++ "\n"); | ||||
|                 expect(false); | ||||
|                 unreachable; | ||||
|                 std.debug.panic("No more test values for the return of function: " ++ fun_name ++ "\n"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -328,9 +340,7 @@ fn Mock() type { | |||
|             } else { | ||||
|                 // Shouldn't get here as we would have just added a new mapping | ||||
|                 // But just in case ;) | ||||
|                 warn("No function name: " ++ fun_name ++ "\n"); | ||||
|                 expect(false); | ||||
|                 unreachable; | ||||
|                 std.debug.panic("No function name: " ++ fun_name ++ "\n"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -448,14 +458,10 @@ fn Mock() type { | |||
|                     kv_actions_list.value = action_list; | ||||
|                     return ret; | ||||
|                 } else { | ||||
|                     warn("No action list elements for function: " ++ fun_name ++ "\n"); | ||||
|                     expect(false); | ||||
|                     unreachable; | ||||
|                     std.debug.panic("No action list elements for function: " ++ fun_name ++ "\n"); | ||||
|                 } | ||||
|             } else { | ||||
|                 warn("No function name: " ++ fun_name ++ "\n"); | ||||
|                 expect(false); | ||||
|                 unreachable; | ||||
|                 std.debug.panic("No function name: " ++ fun_name ++ "\n"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -489,9 +495,7 @@ fn Mock() type { | |||
|                     switch (action.action) { | ||||
|                         ActionType.TestValue, ActionType.ConsumeFunctionCall => { | ||||
|                             // These need to be all consumed | ||||
|                             warn("Unused testing value: Type: {}, value: {} for function '{}'\n", action.action, DataElementType(action.data), next.key); | ||||
|                             expect(false); | ||||
|                             unreachable; | ||||
|                             std.debug.panic("Unused testing value: Type: {}, value: {} for function '{}'\n", action.action, DataElementType(action.data), next.key); | ||||
|                         }, | ||||
|                         ActionType.RepeatFunctionCall => { | ||||
|                             // As this is a repeat action, the function will still be here | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 ED
						ED