Added IDT unit and run-time tests
This commit is contained in:
parent
9d9c5d6a39
commit
4d9b963310
12 changed files with 424 additions and 109 deletions
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -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"])
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
47
test/mock/kernel/idt_mock.zig
Normal file
47
test/mock/kernel/idt_mock.zig
Normal 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);
|
||||
}
|
|
@ -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)),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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");
|
Loading…
Reference in a new issue