From 4d9b9633108aa89b2d3daaeb7c64a435ab4f79c7 Mon Sep 17 00:00:00 2001 From: ED Date: Tue, 17 Sep 2019 18:24:27 +0100 Subject: [PATCH] Added IDT unit and run-time tests --- build.zig | 4 +- src/kernel/arch.zig | 4 + src/kernel/arch/x86/arch.zig | 21 +- src/kernel/arch/x86/idt.zig | 299 ++++++++++++++++++++++++---- src/kernel/arch/x86/irq.zig | 47 +++-- src/kernel/arch/x86/isr.zig | 81 +++++--- src/kernel/kernel.zig | 7 - test/kernel/arch/x86/rt-test.py | 1 + test/mock/kernel/arch_mock.zig | 5 + test/mock/kernel/idt_mock.zig | 47 +++++ test/mock/kernel/mock_framework.zig | 11 + test/mock/kernel/mocking.zig | 6 - 12 files changed, 424 insertions(+), 109 deletions(-) delete mode 100644 src/kernel/kernel.zig create mode 100644 test/mock/kernel/idt_mock.zig delete mode 100644 test/mock/kernel/mocking.zig diff --git a/build.zig b/build.zig index 0890f00..6f62df0 100644 --- a/build.zig +++ b/build.zig @@ -91,8 +91,8 @@ pub fn build(b: *Builder) !void { const script = b.addSystemCommand([_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe }); test_step.dependOn(&script.step); } else { - const mock_path = "\"" ++ "../../test/mock/kernel/" ++ "\""; - const arch_mock_path = "\"" ++ "../../../../test/mock/kernel/" ++ "\""; + const mock_path = "\"../../test/mock/kernel/\""; + const arch_mock_path = "\"../../../../test/mock/kernel/\""; const modes = [_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }; inline for (modes) |test_mode| { const mode_str = switch (test_mode) { diff --git a/src/kernel/arch.zig b/src/kernel/arch.zig index fcf6d8f..b96300e 100644 --- a/src/kernel/arch.zig +++ b/src/kernel/arch.zig @@ -6,3 +6,7 @@ pub const internals = if (is_test) @import(build_options.mock_path ++ "arch_mock .i386 => @import("arch/x86/arch.zig"), else => unreachable, }; + +test "" { + _ = @import("arch/x86/arch.zig"); +} diff --git a/src/kernel/arch/x86/arch.zig b/src/kernel/arch/x86/arch.zig index b07b007..dc2e998 100644 --- a/src/kernel/arch/x86/arch.zig +++ b/src/kernel/arch/x86/arch.zig @@ -125,7 +125,7 @@ pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void { /// The previously loaded GDT from the CPU. /// pub fn sgdt() gdt.GdtPtr { - var gdt_ptr: gdt.GdtPtr = gdt.GdtPtr{ .limit = 0, .base = 0 }; + var gdt_ptr = gdt.GdtPtr{ .limit = 0, .base = 0 }; asm volatile ("sgdt %[tab]" : [tab] "=m" (gdt_ptr) ); @@ -158,6 +158,20 @@ pub fn lidt(idt_ptr: *const idt.IdtPtr) void { ); } +/// +/// Get the previously loaded IDT from the CPU. +/// +/// Return: idt.IdtPtr +/// The previously loaded IDT from the CPU. +/// +pub fn sidt() idt.IdtPtr { + var idt_ptr = idt.IdtPtr{ .limit = 0, .base = 0 }; + asm volatile ("sidt %[tab]" + : [tab] "=m" (idt_ptr) + ); + return idt_ptr; +} + /// /// Enable interrupts. /// @@ -227,3 +241,8 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti enableInterrupts(); } + +test "" { + _ = @import("gdt.zig"); + _ = @import("idt.zig"); +} diff --git a/src/kernel/arch/x86/idt.zig b/src/kernel/arch/x86/idt.zig index 3ea6193..cfd2364 100644 --- a/src/kernel/arch/x86/idt.zig +++ b/src/kernel/arch/x86/idt.zig @@ -1,24 +1,14 @@ -// Zig version: 0.4.0 +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const expectError = std.testing.expectError; +const builtin = @import("builtin"); +const is_test = builtin.is_test; -const gdt = @import("gdt.zig"); -const arch = @import("arch.zig"); -const log = @import("../../log.zig"); - -const NUMBER_OF_ENTRIES: u16 = 256; -const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1; - -// The different gate types -const TASK_GATE_32BIT: u4 = 0x5; -const INTERRUPT_GATE_16BIT: u4 = 0x6; -const TRAP_GATE_16BIT: u4 = 0x7; -const INTERRUPT_GATE_32BIT: u4 = 0xE; -const TRAP_GATE_32BIT: u4 = 0xF; - -// Privilege levels -const PRIVILEGE_RING_0: u2 = 0x0; -const PRIVILEGE_RING_1: u2 = 0x1; -const PRIVILEGE_RING_2: u2 = 0x2; -const PRIVILEGE_RING_3: u2 = 0x3; +const build_options = @import("build_options"); +const gdt = if (is_test) @import(build_options.arch_mock_path ++ "gdt_mock.zig") else @import("gdt.zig"); +const arch = if (is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig"); +const log = if (is_test) @import(build_options.arch_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 { @@ -54,35 +44,89 @@ pub const IdtPtr = packed struct { limit: u16, /// The base address where the IDT is located. - base: *IdtEntry, + base: u32, }; -/// The IDT entry table of NUMBER_OF_ENTRIES entries. -var idt: [NUMBER_OF_ENTRIES]IdtEntry = [_]IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES; +pub const InterruptHandler = extern fn () void; + +/// The error set for the IDT +pub const IdtError = error{ + /// A IDT entry already exists for the provided index. + IdtEntryExists, +}; + +// ---------- +// Task gates +// ---------- + +/// The base addresses aren't used, so set these to 0. When a interrupt happens, interrupts are not +/// automatically disabled. This is used for referencing the TSS descriptor in the GDT. +const TASK_GATE: u4 = 0x5; + +/// Used to specify a interrupt service routine (ISR). When a interrupt happens, interrupts are +/// automatically disabled then enabled upon the IRET instruction which restores the saved EFLAGS. +const INTERRUPT_GATE: u4 = 0xE; + +/// Used to specify a interrupt service routine (ISR). When a interrupt happens, interrupts are not +/// automatically disabled and doesn't restores the saved EFLAGS upon the IRET instruction. +const TRAP_GATE: u4 = 0xF; + +// ---------- +// Privilege levels +// ---------- + +/// Privilege level 0. Kernel land. The privilege level the calling descriptor minimum will have. +const PRIVILEGE_RING_0: u2 = 0x0; + +/// Privilege level 1. The privilege level the calling descriptor minimum will have. +const PRIVILEGE_RING_1: u2 = 0x1; + +/// Privilege level 2. The privilege level the calling descriptor minimum will have. +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 IDT pointer that the CPU is loaded with that contains the base address of the IDT and the /// size. -const idt_ptr: IdtPtr = IdtPtr{ +var idt_ptr: IdtPtr = IdtPtr{ .limit = TABLE_SIZE, - .base = &idt[0], + .base = 0, }; +/// The IDT entry table of NUMBER_OF_ENTRIES entries. Initially all zero'ed. +var idt_entries: [NUMBER_OF_ENTRIES]IdtEntry = [_]IdtEntry{IdtEntry{ + .base_low = 0, + .selector = 0, + .zero = 0, + .gate_type = 0, + .storage_segment = 0, + .privilege = 0, + .present = 0, + .base_high = 0, +}} ** NUMBER_OF_ENTRIES; + /// /// Make a IDT entry. /// /// Arguments: /// IN base: u32 - The pointer to the interrupt handler. -/// IN selector: u16 - The segment the interrupt is in. This will usually be the +/// IN selector: u16 - The descriptor segment the interrupt is in. This will usually be the /// kernels code segment. -/// IN gate_type: u4 - The type of interrupt. +/// IN gate_type: u4 - The type of interrupt. This will usually be the INTERRUPT_GATE. /// IN privilege: u2 - What privilege to call the interrupt in. This will usually be /// the kernel ring level 0. -/// IN present: u1 - Whether a interrupt handler is present to be called.. /// -/// Return: +/// Return: IdtEntry /// A new IDT entry. /// -fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1) IdtEntry { +fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2) IdtEntry { return IdtEntry{ .base_low = @truncate(u16, base), .selector = selector, @@ -90,30 +134,44 @@ fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1 .gate_type = gate_type, .storage_segment = 0, .privilege = privilege, - .present = present, + // Creating a new entry, so is now present. + .present = 1, .base_high = @truncate(u16, base >> 16), }; } +/// +/// Check whether a IDT gate is open. +/// +/// Arguments: +/// IN entry: IdtEntry - The IDT entry to check. +/// +/// Return: bool +/// Whether the provided IDT entry is open or not. +/// +fn isIdtOpen(entry: IdtEntry) bool { + return entry.present == 1; +} + /// /// Open a interrupt gate with a given index and a handler to call. /// /// Arguments: -/// IN index: u8 - The interrupt number to close. -/// IN base: extern fn()void - The function handler for the interrupt. +/// IN index: u8 - The interrupt number to open. +/// IN handler: InterruptHandler - The interrupt handler for the interrupt. /// -pub fn openInterruptGate(index: u8, base: extern fn () void) void { - idt[index] = makeEntry(@ptrToInt(base), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE_32BIT, PRIVILEGE_RING_0, 1); -} +/// Errors: +/// IdtError.InvalidIdtEntry - If the interrupt number is invalid, see isValidInterruptNumber. +/// IdtError.IdtEntryExists - If the interrupt has already been registered. +/// +pub fn openInterruptGate(index: u8, handler: InterruptHandler) IdtError!void { + // As the IDT is a u8, that maximum can only be 255 which is the maximum IDT entries. + // So there can't be a out of bounds. + if (isIdtOpen(idt_entries[index])) { + return IdtError.IdtEntryExists; + } -/// -/// Close a interrupt gate with a given index -/// -/// Arguments: -/// IN index: u8 - The interrupt number to close. -/// -pub fn closeInterruptGate(index: u8) void { - idt[index] = makeEntry(0, 0, 0, 0, 0); + idt_entries[index] = makeEntry(@intCast(u32, @ptrToInt(handler)), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE, PRIVILEGE_RING_0); } /// @@ -121,6 +179,159 @@ pub fn closeInterruptGate(index: u8) void { /// pub fn init() void { log.logInfo("Init idt\n"); + + idt_ptr.base = @intCast(u32, @ptrToInt(&idt_entries)); + arch.lidt(&idt_ptr); log.logInfo("Done\n"); + + if (build_options.rt_test) runtimeTests(); +} + +extern fn testHandler0() void {} +extern fn testHandler1() void {} + +fn mock_lidt(ptr: *const IdtPtr) void { + expectEqual(TABLE_SIZE, ptr.limit); + expectEqual(@intCast(u32, @ptrToInt(&idt_entries[0])), ptr.base); +} + +test "IDT entries" { + expectEqual(u32(8), @sizeOf(IdtEntry)); + expectEqual(u32(6), @sizeOf(IdtPtr)); + expectEqual(TABLE_SIZE, idt_ptr.limit); + expectEqual(u32(0), idt_ptr.base); +} + +test "makeEntry alternating bit pattern" { + const actual = makeEntry(u32(0b01010101010101010101010101010101), u16(0b0101010101010101), u4(0b0101), u2(0b01)); + + const expected = u64(0b0101010101010101101001010000000001010101010101010101010101010101); + + expectEqual(expected, @bitCast(u64, actual)); +} + +test "isIdtOpen" { + const not_open = IdtEntry{ + .base_low = 0, + .selector = 0, + .zero = 0, + .gate_type = 0, + .storage_segment = 0, + .privilege = 0, + .present = 0, + .base_high = 0, + }; + + const open = IdtEntry{ + .base_low = 0, + .selector = 0, + .zero = 0, + .gate_type = 0, + .storage_segment = 0, + .privilege = 0, + .present = 1, + .base_high = 0, + }; + + expectEqual(false, isIdtOpen(not_open)); + expectEqual(true, isIdtOpen(open)); +} + +test "openInterruptGate" { + const index = u8(100); + openInterruptGate(index, testHandler0) catch unreachable; + expectError(IdtError.IdtEntryExists, openInterruptGate(index, testHandler0)); + + const test_fn_0_addr = @intCast(u32, @ptrToInt(testHandler0)); + const test_fn_1_addr = @intCast(u32, @ptrToInt(testHandler1)); + + const expected_entry0 = IdtEntry{ + .base_low = @truncate(u16, test_fn_0_addr), + .selector = gdt.KERNEL_CODE_OFFSET, + .zero = 0, + .gate_type = INTERRUPT_GATE, + .storage_segment = 0, + .privilege = PRIVILEGE_RING_0, + .present = 1, + .base_high = @truncate(u16, test_fn_0_addr >> 16), + }; + + expectEqual(expected_entry0, idt_entries[index]); + + // Reset + idt_entries[index] = IdtEntry{ + .base_low = 0, + .selector = 0, + .zero = 0, + .gate_type = 0, + .storage_segment = 0, + .privilege = 0, + .present = 0, + .base_high = 0, + }; + + openInterruptGate(index, testHandler0) catch unreachable; + // With different handler + expectError(IdtError.IdtEntryExists, openInterruptGate(index, testHandler1)); + + const expected_entry1 = IdtEntry{ + .base_low = @truncate(u16, test_fn_0_addr), + .selector = gdt.KERNEL_CODE_OFFSET, + .zero = 0, + .gate_type = INTERRUPT_GATE, + .storage_segment = 0, + .privilege = PRIVILEGE_RING_0, + .present = 1, + .base_high = @truncate(u16, test_fn_0_addr >> 16), + }; + + expectEqual(expected_entry1, idt_entries[index]); + + // Reset + idt_entries[index] = IdtEntry{ + .base_low = 0, + .selector = 0, + .zero = 0, + .gate_type = 0, + .storage_segment = 0, + .privilege = 0, + .present = 0, + .base_high = 0, + }; +} + +test "init" { + // Set up + arch.initTest(); + defer arch.freeTest(); + + arch.addConsumeFunction("lidt", mock_lidt); + + // Call function + init(); + + // Post testing + expectEqual(@intCast(u32, @ptrToInt(&idt_entries)), idt_ptr.base); + + // Reset + idt_ptr.base = 0; +} + +/// +/// Check that the IDT table was loaded properly by getting the previously loaded table and +/// compare the limit and base address. +/// +fn rt_loadedIDTSuccess() void { + const loaded_idt = arch.sidt(); + expect(idt_ptr.limit == loaded_idt.limit); + expect(idt_ptr.base == loaded_idt.base); +} + +/// +/// Run all the runtime tests. +/// +fn runtimeTests() void { + rt_loadedIDTSuccess(); + log.logInfo("IDT: Tested loading IDT\n"); } diff --git a/src/kernel/arch/x86/irq.zig b/src/kernel/arch/x86/irq.zig index 5dbdf67..eb18f66 100644 --- a/src/kernel/arch/x86/irq.zig +++ b/src/kernel/arch/x86/irq.zig @@ -59,6 +59,21 @@ export fn irqHandler(context: *arch.InterruptContext) void { } } +/// +/// Open an IDT entry with index and handler. This will also handle the errors. +/// +/// Arguments: +/// IN index: u8 - The IDT interrupt number. +/// IN handler: idt.InterruptHandler - The IDT handler. +/// +fn openIrq(index: u8, handler: idt.InterruptHandler) void { + idt.openInterruptGate(index, handler) catch |err| switch (err) { + error.IdtEntryExists => { + panic.panicFmt(@errorReturnTrace(), "Error opening IRQ number: {} exists", index); + }, + }; +} + /// /// Register a IRQ by setting its interrupt handler to the given function. This will also clear the /// mask bit in the PIC so interrupts can happen. @@ -92,20 +107,20 @@ pub fn init() void { pic.remapIrq(); // Open all the IRQ's - idt.openInterruptGate(32, irq0); - idt.openInterruptGate(33, irq1); - idt.openInterruptGate(34, irq2); - idt.openInterruptGate(35, irq3); - idt.openInterruptGate(36, irq4); - idt.openInterruptGate(37, irq5); - idt.openInterruptGate(38, irq6); - idt.openInterruptGate(39, irq7); - idt.openInterruptGate(40, irq8); - idt.openInterruptGate(41, irq9); - idt.openInterruptGate(42, irq10); - idt.openInterruptGate(43, irq11); - idt.openInterruptGate(44, irq12); - idt.openInterruptGate(45, irq13); - idt.openInterruptGate(46, irq14); - idt.openInterruptGate(47, irq15); + openIrq(32, irq0); + openIrq(33, irq1); + openIrq(34, irq2); + openIrq(35, irq3); + openIrq(36, irq4); + openIrq(37, irq5); + openIrq(38, irq6); + openIrq(39, irq7); + openIrq(40, irq8); + openIrq(41, irq9); + openIrq(42, irq10); + openIrq(43, irq11); + openIrq(44, irq12); + openIrq(45, irq13); + openIrq(46, irq14); + openIrq(47, irq15); } diff --git a/src/kernel/arch/x86/isr.zig b/src/kernel/arch/x86/isr.zig index e4b4ea9..42f4d3d 100644 --- a/src/kernel/arch/x86/isr.zig +++ b/src/kernel/arch/x86/isr.zig @@ -130,6 +130,21 @@ export fn isrHandler(context: *arch.InterruptContext) void { } } +/// +/// Open an IDT entry with index and handler. This will also handle the errors. +/// +/// Arguments: +/// IN index: u8 - The IDT interrupt number. +/// IN handler: idt.InterruptHandler - The IDT handler. +/// +fn openIsr(index: u8, handler: idt.InterruptHandler) void { + idt.openInterruptGate(index, handler) catch |err| switch (err) { + error.IdtEntryExists => { + panic.panicFmt(@errorReturnTrace(), "Error opening ISR number: {} exists", index); + }, + }; +} + /// /// Register an exception by setting its exception handler to the given function. /// @@ -164,37 +179,37 @@ pub fn unregisterIsr(isr_num: u16) void { /// Initialise the exception and opening up all the IDT interrupt gates for each exception. /// pub fn init() void { - idt.openInterruptGate(0, isr0); - idt.openInterruptGate(1, isr1); - idt.openInterruptGate(2, isr2); - idt.openInterruptGate(3, isr3); - idt.openInterruptGate(4, isr4); - idt.openInterruptGate(5, isr5); - idt.openInterruptGate(6, isr6); - idt.openInterruptGate(7, isr7); - idt.openInterruptGate(8, isr8); - idt.openInterruptGate(9, isr9); - idt.openInterruptGate(10, isr10); - idt.openInterruptGate(11, isr11); - idt.openInterruptGate(12, isr12); - idt.openInterruptGate(13, isr13); - idt.openInterruptGate(14, isr14); - idt.openInterruptGate(15, isr15); - idt.openInterruptGate(16, isr16); - idt.openInterruptGate(17, isr17); - idt.openInterruptGate(18, isr18); - idt.openInterruptGate(19, isr19); - idt.openInterruptGate(20, isr20); - idt.openInterruptGate(21, isr21); - idt.openInterruptGate(22, isr22); - idt.openInterruptGate(23, isr23); - idt.openInterruptGate(24, isr24); - idt.openInterruptGate(25, isr25); - idt.openInterruptGate(26, isr26); - idt.openInterruptGate(27, isr27); - idt.openInterruptGate(28, isr28); - idt.openInterruptGate(29, isr29); - idt.openInterruptGate(30, isr30); - idt.openInterruptGate(31, isr31); - idt.openInterruptGate(syscalls.INTERRUPT, isr128); + openIsr(0, isr0); + openIsr(1, isr1); + openIsr(2, isr2); + openIsr(3, isr3); + openIsr(4, isr4); + openIsr(5, isr5); + openIsr(6, isr6); + openIsr(7, isr7); + openIsr(8, isr8); + openIsr(9, isr9); + openIsr(10, isr10); + openIsr(11, isr11); + openIsr(12, isr12); + openIsr(13, isr13); + openIsr(14, isr14); + openIsr(15, isr15); + openIsr(16, isr16); + openIsr(17, isr17); + openIsr(18, isr18); + openIsr(19, isr19); + openIsr(20, isr20); + openIsr(21, isr21); + openIsr(22, isr22); + openIsr(23, isr23); + openIsr(24, isr24); + openIsr(25, isr25); + openIsr(26, isr26); + openIsr(27, isr27); + openIsr(28, isr28); + openIsr(29, isr29); + openIsr(30, isr30); + openIsr(31, isr31); + openIsr(syscalls.INTERRUPT, isr128); } diff --git a/src/kernel/kernel.zig b/src/kernel/kernel.zig deleted file mode 100644 index 63b3199..0000000 --- a/src/kernel/kernel.zig +++ /dev/null @@ -1,7 +0,0 @@ -pub const arch = @import("arch.zig").internals; -pub const log = @import("log.zig"); -pub const mem = @import("mem.zig"); -pub const panic = @import("panic.zig"); -pub const serial = @import("serial.zig"); -pub const tty = @import("tty.zig"); -pub const vga = @import("vga.zig"); diff --git a/test/kernel/arch/x86/rt-test.py b/test/kernel/arch/x86/rt-test.py index a2e103b..3fee314 100644 --- a/test/kernel/arch/x86/rt-test.py +++ b/test/kernel/arch/x86/rt-test.py @@ -3,6 +3,7 @@ def getTestCases(TestCase): TestCase("GDT init", [r"Init gdt", r"Done"]), TestCase("GDT tests", [r"GDT: Tested loading GDT"]), TestCase("IDT init", [r"Init idt", r"Done"]), + TestCase("IDT tests", [r"IDT: Tested loading IDT"]), 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"]) diff --git a/test/mock/kernel/arch_mock.zig b/test/mock/kernel/arch_mock.zig index 30b3a0f..2b7dc5e 100644 --- a/test/mock/kernel/arch_mock.zig +++ b/test/mock/kernel/arch_mock.zig @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator; const mem = @import("mem_mock.zig"); const MemProfile = mem.MemProfile; const gdt = @import("gdt_mock.zig"); +const idt = @import("idt_mock.zig"); const mock_framework = @import("mock_framework.zig"); pub const initTest = mock_framework.initTest; @@ -61,6 +62,10 @@ pub fn lidt(idt_ptr: *const idt.IdtPtr) void { return mock_framework.performAction("lidt", void, idt_ptr); } +pub fn sidt() idt.IdtPtr { + return mock_framework.performAction("sidt", idt.IdtPtr); +} + pub fn enableInterrupts() void { return mock_framework.performAction("enableInterrupts", void); } diff --git a/test/mock/kernel/idt_mock.zig b/test/mock/kernel/idt_mock.zig new file mode 100644 index 0000000..6c1fca4 --- /dev/null +++ b/test/mock/kernel/idt_mock.zig @@ -0,0 +1,47 @@ +const src_idt = @import("../../../src/kernel/arch/x86/idt.zig"); + +const mock_framework = @import("mock_framework.zig"); +pub const initTest = mock_framework.initTest; +pub const freeTest = mock_framework.freeTest; +pub const addTestParams = mock_framework.addTestParams; +pub const addConsumeFunction = mock_framework.addConsumeFunction; +pub const addRepeatFunction = mock_framework.addRepeatFunction; + +const IdtEntry = packed struct { + base_low: u16, + selector: u16, + zero: u8, + gate_type: u4, + storage_segment: u1, + privilege: u2, + present: u1, + base_high: u16, +}; + +// 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 IdtError = error{IdtEntryExists}; + +const TASK_GATE: u4 = 0x5; +const INTERRUPT_GATE: u4 = 0xE; +const TRAP_GATE: u4 = 0xF; + +const PRIVILEGE_RING_0: u2 = 0x0; +const PRIVILEGE_RING_1: u2 = 0x1; +const PRIVILEGE_RING_2: u2 = 0x2; +const PRIVILEGE_RING_3: u2 = 0x3; + +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); +} + +pub fn init() void { + return mock_framework.performAction("init", void); +} diff --git a/test/mock/kernel/mock_framework.zig b/test/mock/kernel/mock_framework.zig index cc8eb81..d80a831 100644 --- a/test/mock/kernel/mock_framework.zig +++ b/test/mock/kernel/mock_framework.zig @@ -6,6 +6,7 @@ const GlobalAllocator = std.debug.global_allocator; const TailQueue = std.TailQueue; const warn = std.debug.warn; const gdt = @import("gdt_mock.zig"); +const idt = @import("idt_mock.zig"); /// /// The enumeration of types that the mocking framework supports. These include basic types like u8 @@ -17,6 +18,7 @@ const DataElementType = enum { U16, U32, PTR_CONST_GdtPtr, + PTR_CONST_IdtPtr, FN_OVOID, FN_OUSIZE, FN_OU16, @@ -27,6 +29,7 @@ const DataElementType = enum { FN_IU16_IU8_OVOID, FN_IU16_IU16_OVOID, FN_IPTRCONSTGDTPTR_OVOID, + FN_IPTRCONSTIDTPTR_OVOID, }; /// @@ -40,6 +43,7 @@ const DataElement = union(DataElementType) { U16: u16, U32: u32, PTR_CONST_GdtPtr: *const gdt.GdtPtr, + PTR_CONST_IdtPtr: *const idt.IdtPtr, FN_OVOID: fn () void, FN_OUSIZE: fn () usize, FN_OU16: fn () u16, @@ -50,6 +54,7 @@ const DataElement = union(DataElementType) { FN_IU16_IU8_OVOID: fn (u16, u8) void, FN_IU16_IU16_OVOID: fn (u16, u16) void, FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void, + FN_IPTRCONSTIDTPTR_OVOID: fn (*const idt.IdtPtr) void, }; /// @@ -130,6 +135,7 @@ fn Mock() type { u16 => DataElement{ .U16 = arg }, u32 => DataElement{ .U32 = arg }, *const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg }, + *const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg }, fn () void => DataElement{ .FN_OVOID = arg }, fn () usize => DataElement{ .FN_OUSIZE = arg }, fn () u16 => DataElement{ .FN_OU16 = arg }, @@ -140,6 +146,7 @@ fn Mock() type { fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg }, 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 }, else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))), }; } @@ -160,6 +167,7 @@ fn Mock() type { u16 => DataElementType.U16, u32 => DataElementType.U32, *const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr, + *const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr, fn () void => DataElementType.FN_OVOID, fn () u16 => DataElementType.FN_OU16, fn (u16) void => DataElementType.FN_IU16_OVOID, @@ -169,6 +177,7 @@ fn Mock() type { fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID, 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, else => @compileError("Type not supported: " ++ @typeName(T)), }; } @@ -191,6 +200,7 @@ fn Mock() type { u16 => element.U16, u32 => element.U32, *const gdt.GdtPtr => element.PTR_CONST_GdtPtr, + *const idt.IdtPtr => element.PTR_CONST_IdtPtr, fn () void => element.FN_OVOID, fn () u16 => element.FN_OU16, fn (u16) void => element.FN_IU16_OVOID, @@ -200,6 +210,7 @@ fn Mock() type { fn (u16, u8) void => element.FN_IU16_IU8_OVOID, 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, else => @compileError("Type not supported: " ++ @typeName(T)), }; } diff --git a/test/mock/kernel/mocking.zig b/test/mock/kernel/mocking.zig deleted file mode 100644 index 22d8523..0000000 --- a/test/mock/kernel/mocking.zig +++ /dev/null @@ -1,6 +0,0 @@ -pub const arch = @import("arch_mock.zig"); -pub const gdt = @import("gdt_mock.zig"); -pub const log = @import("log_mock.zig"); -pub const mem = @import("mem_mock.zig"); -pub const panic = @import("panic_mock.zig"); -pub const vga = @import("vga_mock.zig");