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 });
|
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) {
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 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"])
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
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 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)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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