2019-09-17 18:24:27 +01:00
|
|
|
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;
|
2020-06-23 12:43:52 +01:00
|
|
|
const panic = @import("../../panic.zig").panic;
|
2019-09-17 18:24:27 +01:00
|
|
|
const build_options = @import("build_options");
|
2019-10-01 17:59:42 +01:00
|
|
|
const mock_path = build_options.arch_mock_path;
|
|
|
|
const gdt = if (is_test) @import(mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
|
|
|
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
|
|
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
2019-05-31 07:41:28 +01:00
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
/// The structure that contains all the information that each IDT entry needs.
|
2019-10-05 20:46:31 +01:00
|
|
|
pub const IdtEntry = packed struct {
|
2019-05-31 07:41:28 +01:00
|
|
|
/// The lower 16 bits of the base address of the interrupt handler offset.
|
|
|
|
base_low: u16,
|
|
|
|
|
|
|
|
/// The code segment in the GDT which the handlers will be held.
|
|
|
|
selector: u16,
|
|
|
|
|
|
|
|
/// Must be zero, unused.
|
|
|
|
zero: u8,
|
|
|
|
|
|
|
|
/// The IDT gate type.
|
|
|
|
gate_type: u4,
|
|
|
|
|
|
|
|
/// Must be 0 for interrupt and trap gates.
|
|
|
|
storage_segment: u1,
|
|
|
|
|
|
|
|
/// The minimum ring level that the calling code must have to run the handler. So user code may not be able to run some interrupts.
|
|
|
|
privilege: u2,
|
|
|
|
|
|
|
|
/// Whether the IDT entry is present.
|
|
|
|
present: u1,
|
|
|
|
|
|
|
|
/// The upper 16 bits of the base address of the interrupt handler offset.
|
|
|
|
base_high: u16,
|
|
|
|
};
|
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
/// The IDT pointer structure that contains the pointer to the beginning of the IDT and the number
|
|
|
|
/// of the table (minus 1). Used to load the IST with LIDT instruction.
|
2019-05-31 07:41:28 +01:00
|
|
|
pub const IdtPtr = packed struct {
|
|
|
|
/// The total size of the IDT (minus 1) in bytes.
|
|
|
|
limit: u16,
|
|
|
|
|
|
|
|
/// The base address where the IDT is located.
|
2019-09-17 18:24:27 +01:00
|
|
|
base: u32,
|
|
|
|
};
|
|
|
|
|
2020-01-07 13:30:54 +00:00
|
|
|
pub const InterruptHandler = fn () callconv(.Naked) void;
|
2019-09-17 18:24:27 +01:00
|
|
|
|
|
|
|
/// The error set for the IDT
|
|
|
|
pub const IdtError = error{
|
|
|
|
/// A IDT entry already exists for the provided index.
|
|
|
|
IdtEntryExists,
|
2019-05-31 07:41:28 +01:00
|
|
|
};
|
|
|
|
|
2019-09-17 18:24:27 +01:00
|
|
|
// ----------
|
|
|
|
// 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 size of all the IDT entries (minus 1).
|
|
|
|
const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1;
|
2019-05-31 07:41:28 +01:00
|
|
|
|
2019-10-05 20:46:31 +01:00
|
|
|
/// The total number of entries the IDT can have (2^8).
|
|
|
|
pub const NUMBER_OF_ENTRIES: u16 = 256;
|
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
/// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the
|
|
|
|
/// size.
|
2019-09-17 18:24:27 +01:00
|
|
|
var idt_ptr: IdtPtr = IdtPtr{
|
2019-05-31 07:41:28 +01:00
|
|
|
.limit = TABLE_SIZE,
|
2019-09-17 18:24:27 +01:00
|
|
|
.base = 0,
|
2019-05-31 07:41:28 +01:00
|
|
|
};
|
|
|
|
|
2020-06-23 12:43:52 +01:00
|
|
|
/// The IDT entry table of NUMBER_OF_ENTRIES entries. Initially all zeroed.
|
2019-09-17 18:24:27 +01:00
|
|
|
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;
|
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
|
|
|
/// Make a IDT entry.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// IN base: u32 - The pointer to the interrupt handler.
|
2019-09-17 18:24:27 +01:00
|
|
|
/// IN selector: u16 - The descriptor segment the interrupt is in. This will usually be the
|
2019-06-02 17:14:02 +01:00
|
|
|
/// kernels code segment.
|
2019-09-17 18:24:27 +01:00
|
|
|
/// IN gate_type: u4 - The type of interrupt. This will usually be the INTERRUPT_GATE.
|
2019-06-02 17:14:02 +01:00
|
|
|
/// IN privilege: u2 - What privilege to call the interrupt in. This will usually be
|
|
|
|
/// the kernel ring level 0.
|
|
|
|
///
|
2019-09-17 18:24:27 +01:00
|
|
|
/// Return: IdtEntry
|
2019-06-02 17:14:02 +01:00
|
|
|
/// A new IDT entry.
|
|
|
|
///
|
2019-09-17 18:24:27 +01:00
|
|
|
fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2) IdtEntry {
|
2019-09-03 14:13:26 -04:00
|
|
|
return IdtEntry{
|
2019-05-31 07:41:28 +01:00
|
|
|
.base_low = @truncate(u16, base),
|
|
|
|
.selector = selector,
|
|
|
|
.zero = 0,
|
|
|
|
.gate_type = gate_type,
|
|
|
|
.storage_segment = 0,
|
|
|
|
.privilege = privilege,
|
2019-09-17 18:24:27 +01:00
|
|
|
// Creating a new entry, so is now present.
|
|
|
|
.present = 1,
|
2019-05-31 07:41:28 +01:00
|
|
|
.base_high = @truncate(u16, base >> 16),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
2019-09-17 18:24:27 +01:00
|
|
|
/// Check whether a IDT gate is open.
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
|
|
|
/// Arguments:
|
2019-09-17 18:24:27 +01:00
|
|
|
/// IN entry: IdtEntry - The IDT entry to check.
|
|
|
|
///
|
|
|
|
/// Return: bool
|
|
|
|
/// Whether the provided IDT entry is open or not.
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
2019-10-05 20:46:31 +01:00
|
|
|
pub fn isIdtOpen(entry: IdtEntry) bool {
|
2019-09-17 18:24:27 +01:00
|
|
|
return entry.present == 1;
|
2019-05-31 07:41:28 +01:00
|
|
|
}
|
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
2019-09-17 18:24:27 +01:00
|
|
|
/// Open a interrupt gate with a given index and a handler to call.
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
|
|
|
/// Arguments:
|
2019-09-17 18:24:27 +01:00
|
|
|
/// IN index: u8 - The interrupt number to open.
|
|
|
|
/// IN handler: InterruptHandler - The interrupt handler for the interrupt.
|
|
|
|
///
|
|
|
|
/// Errors:
|
|
|
|
/// IdtError.InvalidIdtEntry - If the interrupt number is invalid, see isValidInterruptNumber.
|
|
|
|
/// IdtError.IdtEntryExists - If the interrupt has already been registered.
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
2019-09-17 18:24:27 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-09 13:08:00 +00:00
|
|
|
idt_entries[index] = makeEntry(@ptrToInt(handler), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE, PRIVILEGE_RING_0);
|
2019-05-31 07:41:28 +01:00
|
|
|
}
|
|
|
|
|
2019-06-02 17:14:02 +01:00
|
|
|
///
|
|
|
|
/// Initialise the Interrupt descriptor table
|
|
|
|
///
|
2019-05-31 07:41:28 +01:00
|
|
|
pub fn init() void {
|
2020-01-01 19:12:36 +00:00
|
|
|
log.logInfo("Init idt\n", .{});
|
2020-04-12 22:26:34 +01:00
|
|
|
defer log.logInfo("Done idt\n", .{});
|
2019-09-17 18:24:27 +01:00
|
|
|
|
2020-01-09 13:08:00 +00:00
|
|
|
idt_ptr.base = @ptrToInt(&idt_entries);
|
2019-09-17 18:24:27 +01:00
|
|
|
|
2019-05-31 07:41:28 +01:00
|
|
|
arch.lidt(&idt_ptr);
|
2019-09-17 18:24:27 +01:00
|
|
|
|
2020-06-23 12:43:52 +01:00
|
|
|
switch (build_options.test_mode) {
|
|
|
|
.Initialisation => runtimeTests(),
|
|
|
|
else => {},
|
|
|
|
}
|
2019-09-17 18:24:27 +01:00
|
|
|
}
|
|
|
|
|
2020-01-07 13:30:54 +00:00
|
|
|
fn testHandler0() callconv(.Naked) void {}
|
|
|
|
fn testHandler1() callconv(.Naked) void {}
|
2019-09-17 18:24:27 +01:00
|
|
|
|
|
|
|
fn mock_lidt(ptr: *const IdtPtr) void {
|
|
|
|
expectEqual(TABLE_SIZE, ptr.limit);
|
2020-01-09 13:08:00 +00:00
|
|
|
expectEqual(@ptrToInt(&idt_entries[0]), ptr.base);
|
2019-09-17 18:24:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
test "IDT entries" {
|
2019-11-10 12:35:08 +00:00
|
|
|
expectEqual(@as(u32, 8), @sizeOf(IdtEntry));
|
|
|
|
expectEqual(@as(u32, 6), @sizeOf(IdtPtr));
|
2019-09-17 18:24:27 +01:00
|
|
|
expectEqual(TABLE_SIZE, idt_ptr.limit);
|
2019-11-10 12:35:08 +00:00
|
|
|
expectEqual(@as(u32, 0), idt_ptr.base);
|
2019-09-17 18:24:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
test "makeEntry alternating bit pattern" {
|
2019-11-10 12:35:08 +00:00
|
|
|
const actual = makeEntry(0b01010101010101010101010101010101, 0b0101010101010101, 0b0101, 0b01);
|
2019-09-17 18:24:27 +01:00
|
|
|
|
2019-11-10 12:35:08 +00:00
|
|
|
const expected: u64 = 0b0101010101010101101001010000000001010101010101010101010101010101;
|
2019-09-17 18:24:27 +01:00
|
|
|
|
|
|
|
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" {
|
2019-11-10 12:35:08 +00:00
|
|
|
const index: u8 = 100;
|
2019-09-17 18:24:27 +01:00
|
|
|
openInterruptGate(index, testHandler0) catch unreachable;
|
|
|
|
expectError(IdtError.IdtEntryExists, openInterruptGate(index, testHandler0));
|
|
|
|
|
2020-01-09 13:08:00 +00:00
|
|
|
const test_fn_0_addr = @ptrToInt(testHandler0);
|
|
|
|
const test_fn_1_addr = @ptrToInt(testHandler1);
|
2019-09-17 18:24:27 +01:00
|
|
|
|
|
|
|
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
|
2020-01-09 13:08:00 +00:00
|
|
|
expectEqual(@ptrToInt(&idt_entries), idt_ptr.base);
|
2019-09-17 18:24:27 +01:00
|
|
|
|
|
|
|
// 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();
|
2020-06-23 12:43:52 +01:00
|
|
|
if (idt_ptr.limit != loaded_idt.limit) {
|
|
|
|
panic(@errorReturnTrace(), "FAILURE: IDT not loaded properly: 0x{X} != 0x{X}\n", .{ idt_ptr.limit, loaded_idt.limit });
|
|
|
|
}
|
|
|
|
if (idt_ptr.base != loaded_idt.base) {
|
|
|
|
panic(@errorReturnTrace(), "FAILURE: IDT not loaded properly: 0x{X} != {X}\n", .{ idt_ptr.base, loaded_idt.base });
|
|
|
|
}
|
2020-01-01 19:12:36 +00:00
|
|
|
log.logInfo("IDT: Tested loading IDT\n", .{});
|
2019-09-17 18:24:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Run all the runtime tests.
|
|
|
|
///
|
|
|
|
fn runtimeTests() void {
|
|
|
|
rt_loadedIDTSuccess();
|
2019-06-22 10:00:57 +01:00
|
|
|
}
|