Added IDT unit and run-time tests

This commit is contained in:
ED 2019-09-17 18:24:27 +01:00
parent 9d9c5d6a39
commit 4d9b963310
12 changed files with 424 additions and 109 deletions

View file

@ -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 }); const script = b.addSystemCommand([_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe });
test_step.dependOn(&script.step); test_step.dependOn(&script.step);
} else { } else {
const mock_path = "\"" ++ "../../test/mock/kernel/" ++ "\""; const mock_path = "\"../../test/mock/kernel/\"";
const arch_mock_path = "\"" ++ "../../../../test/mock/kernel/" ++ "\""; const arch_mock_path = "\"../../../../test/mock/kernel/\"";
const modes = [_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }; const modes = [_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall };
inline for (modes) |test_mode| { inline for (modes) |test_mode| {
const mode_str = switch (test_mode) { const mode_str = switch (test_mode) {

View file

@ -6,3 +6,7 @@ pub const internals = if (is_test) @import(build_options.mock_path ++ "arch_mock
.i386 => @import("arch/x86/arch.zig"), .i386 => @import("arch/x86/arch.zig"),
else => unreachable, else => unreachable,
}; };
test "" {
_ = @import("arch/x86/arch.zig");
}

View file

@ -125,7 +125,7 @@ pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
/// The previously loaded GDT from the CPU. /// The previously loaded GDT from the CPU.
/// ///
pub fn sgdt() gdt.GdtPtr { 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]" asm volatile ("sgdt %[tab]"
: [tab] "=m" (gdt_ptr) : [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. /// Enable interrupts.
/// ///
@ -227,3 +241,8 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti
enableInterrupts(); enableInterrupts();
} }
test "" {
_ = @import("gdt.zig");
_ = @import("idt.zig");
}

View file

@ -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 build_options = @import("build_options");
const arch = @import("arch.zig"); const gdt = if (is_test) @import(build_options.arch_mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
const log = @import("../../log.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");
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;
/// The structure that contains all the information that each IDT entry needs. /// The structure that contains all the information that each IDT entry needs.
const IdtEntry = packed struct { const IdtEntry = packed struct {
@ -54,35 +44,89 @@ pub const IdtPtr = packed struct {
limit: u16, limit: u16,
/// The base address where the IDT is located. /// The base address where the IDT is located.
base: *IdtEntry, base: u32,
}; };
/// The IDT entry table of NUMBER_OF_ENTRIES entries. pub const InterruptHandler = extern fn () void;
var idt: [NUMBER_OF_ENTRIES]IdtEntry = [_]IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES;
/// 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 /// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the
/// size. /// size.
const idt_ptr: IdtPtr = IdtPtr{ var idt_ptr: IdtPtr = IdtPtr{
.limit = TABLE_SIZE, .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. /// Make a IDT entry.
/// ///
/// Arguments: /// Arguments:
/// IN base: u32 - The pointer to the interrupt handler. /// 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. /// 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 /// IN privilege: u2 - What privilege to call the interrupt in. This will usually be
/// the kernel ring level 0. /// the kernel ring level 0.
/// IN present: u1 - Whether a interrupt handler is present to be called..
/// ///
/// Return: /// Return: IdtEntry
/// A new IDT entry. /// 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{ return IdtEntry{
.base_low = @truncate(u16, base), .base_low = @truncate(u16, base),
.selector = selector, .selector = selector,
@ -90,30 +134,44 @@ fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1
.gate_type = gate_type, .gate_type = gate_type,
.storage_segment = 0, .storage_segment = 0,
.privilege = privilege, .privilege = privilege,
.present = present, // Creating a new entry, so is now present.
.present = 1,
.base_high = @truncate(u16, base >> 16), .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. /// Open a interrupt gate with a given index and a handler to call.
/// ///
/// Arguments: /// Arguments:
/// IN index: u8 - The interrupt number to close. /// IN index: u8 - The interrupt number to open.
/// IN base: extern fn()void - The function handler for the interrupt. /// IN handler: InterruptHandler - The interrupt handler for the interrupt.
/// ///
pub fn openInterruptGate(index: u8, base: extern fn () void) void { /// Errors:
idt[index] = makeEntry(@ptrToInt(base), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE_32BIT, PRIVILEGE_RING_0, 1); /// 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;
}
/// idt_entries[index] = makeEntry(@intCast(u32, @ptrToInt(handler)), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE, PRIVILEGE_RING_0);
/// 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);
} }
/// ///
@ -121,6 +179,159 @@ pub fn closeInterruptGate(index: u8) void {
/// ///
pub fn init() void { pub fn init() void {
log.logInfo("Init idt\n"); log.logInfo("Init idt\n");
idt_ptr.base = @intCast(u32, @ptrToInt(&idt_entries));
arch.lidt(&idt_ptr); arch.lidt(&idt_ptr);
log.logInfo("Done\n"); 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");
} }

View file

@ -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 /// 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. /// mask bit in the PIC so interrupts can happen.
@ -92,20 +107,20 @@ pub fn init() void {
pic.remapIrq(); pic.remapIrq();
// Open all the IRQ's // Open all the IRQ's
idt.openInterruptGate(32, irq0); openIrq(32, irq0);
idt.openInterruptGate(33, irq1); openIrq(33, irq1);
idt.openInterruptGate(34, irq2); openIrq(34, irq2);
idt.openInterruptGate(35, irq3); openIrq(35, irq3);
idt.openInterruptGate(36, irq4); openIrq(36, irq4);
idt.openInterruptGate(37, irq5); openIrq(37, irq5);
idt.openInterruptGate(38, irq6); openIrq(38, irq6);
idt.openInterruptGate(39, irq7); openIrq(39, irq7);
idt.openInterruptGate(40, irq8); openIrq(40, irq8);
idt.openInterruptGate(41, irq9); openIrq(41, irq9);
idt.openInterruptGate(42, irq10); openIrq(42, irq10);
idt.openInterruptGate(43, irq11); openIrq(43, irq11);
idt.openInterruptGate(44, irq12); openIrq(44, irq12);
idt.openInterruptGate(45, irq13); openIrq(45, irq13);
idt.openInterruptGate(46, irq14); openIrq(46, irq14);
idt.openInterruptGate(47, irq15); openIrq(47, irq15);
} }

View file

@ -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. /// 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. /// Initialise the exception and opening up all the IDT interrupt gates for each exception.
/// ///
pub fn init() void { pub fn init() void {
idt.openInterruptGate(0, isr0); openIsr(0, isr0);
idt.openInterruptGate(1, isr1); openIsr(1, isr1);
idt.openInterruptGate(2, isr2); openIsr(2, isr2);
idt.openInterruptGate(3, isr3); openIsr(3, isr3);
idt.openInterruptGate(4, isr4); openIsr(4, isr4);
idt.openInterruptGate(5, isr5); openIsr(5, isr5);
idt.openInterruptGate(6, isr6); openIsr(6, isr6);
idt.openInterruptGate(7, isr7); openIsr(7, isr7);
idt.openInterruptGate(8, isr8); openIsr(8, isr8);
idt.openInterruptGate(9, isr9); openIsr(9, isr9);
idt.openInterruptGate(10, isr10); openIsr(10, isr10);
idt.openInterruptGate(11, isr11); openIsr(11, isr11);
idt.openInterruptGate(12, isr12); openIsr(12, isr12);
idt.openInterruptGate(13, isr13); openIsr(13, isr13);
idt.openInterruptGate(14, isr14); openIsr(14, isr14);
idt.openInterruptGate(15, isr15); openIsr(15, isr15);
idt.openInterruptGate(16, isr16); openIsr(16, isr16);
idt.openInterruptGate(17, isr17); openIsr(17, isr17);
idt.openInterruptGate(18, isr18); openIsr(18, isr18);
idt.openInterruptGate(19, isr19); openIsr(19, isr19);
idt.openInterruptGate(20, isr20); openIsr(20, isr20);
idt.openInterruptGate(21, isr21); openIsr(21, isr21);
idt.openInterruptGate(22, isr22); openIsr(22, isr22);
idt.openInterruptGate(23, isr23); openIsr(23, isr23);
idt.openInterruptGate(24, isr24); openIsr(24, isr24);
idt.openInterruptGate(25, isr25); openIsr(25, isr25);
idt.openInterruptGate(26, isr26); openIsr(26, isr26);
idt.openInterruptGate(27, isr27); openIsr(27, isr27);
idt.openInterruptGate(28, isr28); openIsr(28, isr28);
idt.openInterruptGate(29, isr29); openIsr(29, isr29);
idt.openInterruptGate(30, isr30); openIsr(30, isr30);
idt.openInterruptGate(31, isr31); openIsr(31, isr31);
idt.openInterruptGate(syscalls.INTERRUPT, isr128); openIsr(syscalls.INTERRUPT, isr128);
} }

View file

@ -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");

View file

@ -3,6 +3,7 @@ def getTestCases(TestCase):
TestCase("GDT init", [r"Init gdt", r"Done"]), TestCase("GDT init", [r"Init gdt", r"Done"]),
TestCase("GDT tests", [r"GDT: Tested loading GDT"]), TestCase("GDT tests", [r"GDT: Tested loading GDT"]),
TestCase("IDT init", [r"Init idt", r"Done"]), 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("PIT init", [r"Init pit", r".+", "Done"]),
TestCase("Syscalls init", [r"Init syscalls", "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"]) 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"])

View file

@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
const mem = @import("mem_mock.zig"); const mem = @import("mem_mock.zig");
const MemProfile = mem.MemProfile; const MemProfile = mem.MemProfile;
const gdt = @import("gdt_mock.zig"); const gdt = @import("gdt_mock.zig");
const idt = @import("idt_mock.zig");
const mock_framework = @import("mock_framework.zig"); const mock_framework = @import("mock_framework.zig");
pub const initTest = mock_framework.initTest; 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); return mock_framework.performAction("lidt", void, idt_ptr);
} }
pub fn sidt() idt.IdtPtr {
return mock_framework.performAction("sidt", idt.IdtPtr);
}
pub fn enableInterrupts() void { pub fn enableInterrupts() void {
return mock_framework.performAction("enableInterrupts", void); return mock_framework.performAction("enableInterrupts", void);
} }

View file

@ -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);
}

View file

@ -6,6 +6,7 @@ const GlobalAllocator = std.debug.global_allocator;
const TailQueue = std.TailQueue; const TailQueue = std.TailQueue;
const warn = std.debug.warn; const warn = std.debug.warn;
const gdt = @import("gdt_mock.zig"); 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 /// The enumeration of types that the mocking framework supports. These include basic types like u8
@ -17,6 +18,7 @@ const DataElementType = enum {
U16, U16,
U32, U32,
PTR_CONST_GdtPtr, PTR_CONST_GdtPtr,
PTR_CONST_IdtPtr,
FN_OVOID, FN_OVOID,
FN_OUSIZE, FN_OUSIZE,
FN_OU16, FN_OU16,
@ -27,6 +29,7 @@ const DataElementType = enum {
FN_IU16_IU8_OVOID, FN_IU16_IU8_OVOID,
FN_IU16_IU16_OVOID, FN_IU16_IU16_OVOID,
FN_IPTRCONSTGDTPTR_OVOID, FN_IPTRCONSTGDTPTR_OVOID,
FN_IPTRCONSTIDTPTR_OVOID,
}; };
/// ///
@ -40,6 +43,7 @@ const DataElement = union(DataElementType) {
U16: u16, U16: u16,
U32: u32, U32: u32,
PTR_CONST_GdtPtr: *const gdt.GdtPtr, PTR_CONST_GdtPtr: *const gdt.GdtPtr,
PTR_CONST_IdtPtr: *const idt.IdtPtr,
FN_OVOID: fn () void, FN_OVOID: fn () void,
FN_OUSIZE: fn () usize, FN_OUSIZE: fn () usize,
FN_OU16: fn () u16, FN_OU16: fn () u16,
@ -50,6 +54,7 @@ const DataElement = union(DataElementType) {
FN_IU16_IU8_OVOID: fn (u16, u8) void, FN_IU16_IU8_OVOID: fn (u16, u8) void,
FN_IU16_IU16_OVOID: fn (u16, u16) void, FN_IU16_IU16_OVOID: fn (u16, u16) void,
FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) 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 }, u16 => DataElement{ .U16 = arg },
u32 => DataElement{ .U32 = arg }, u32 => DataElement{ .U32 = arg },
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg }, *const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
*const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg },
fn () void => DataElement{ .FN_OVOID = arg }, fn () void => DataElement{ .FN_OVOID = arg },
fn () usize => DataElement{ .FN_OUSIZE = arg }, fn () usize => DataElement{ .FN_OUSIZE = arg },
fn () u16 => DataElement{ .FN_OU16 = 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, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg }, fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_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))), else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))),
}; };
} }
@ -160,6 +167,7 @@ fn Mock() type {
u16 => DataElementType.U16, u16 => DataElementType.U16,
u32 => DataElementType.U32, u32 => DataElementType.U32,
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr, *const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
*const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr,
fn () void => DataElementType.FN_OVOID, fn () void => DataElementType.FN_OVOID,
fn () u16 => DataElementType.FN_OU16, fn () u16 => DataElementType.FN_OU16,
fn (u16) void => DataElementType.FN_IU16_OVOID, 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, u8) void => DataElementType.FN_IU16_IU8_OVOID,
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID, fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_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)), else => @compileError("Type not supported: " ++ @typeName(T)),
}; };
} }
@ -191,6 +200,7 @@ fn Mock() type {
u16 => element.U16, u16 => element.U16,
u32 => element.U32, u32 => element.U32,
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr, *const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
*const idt.IdtPtr => element.PTR_CONST_IdtPtr,
fn () void => element.FN_OVOID, fn () void => element.FN_OVOID,
fn () u16 => element.FN_OU16, fn () u16 => element.FN_OU16,
fn (u16) void => element.FN_IU16_OVOID, 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, u8) void => element.FN_IU16_IU8_OVOID,
fn (u16, u16) void => element.FN_IU16_IU16_OVOID, fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_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)), else => @compileError("Type not supported: " ++ @typeName(T)),
}; };
} }

View file

@ -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");