/** * IDT (Interrupt Descriptor Table) functionality. */ module hulk.idt; import hulk.gdt; import ldc.llvmasm; import hulk.fb; import hulk.console; import hulk.klog; import hulk.apic; import hulk.cpu; import hulk.kfont; import hulk.thread; static foreach(isr; 0 .. Idt.N_ISRS) { mixin(`private extern(C) void isr_`, isr, `();`); } private string isrs_list(int index = 0)() { static if (index < 256) { return "&isr_" ~ index.stringof ~ ",\n" ~ isrs_list!(index + 1)(); } else { return ""; } } mixin(`private static __gshared extern(C) void function()[Idt.N_ISRS] isrs = [`, isrs_list(), `];`); struct Idt { /** Interrupt stack frame indices. */ public static enum { ISF_RBP, ISF_R15, ISF_R14, ISF_R13, ISF_R12, ISF_R11, ISF_R10, ISF_R9, ISF_R8, ISF_RDI, ISF_RSI, ISF_RDX, ISF_RCX, ISF_RBX, ISF_RAX, ISF_ERROR_CODE, ISF_RIP, ISF_CS, ISF_RFLAGS, ISF_RSP, ISF_SS, ISF_COUNT, } private static __gshared string[ISF_COUNT] ISF_NAMES = [ "RBP", "R15", "R14", "R13", "R12", "R11", "R10", "R9", "R8", "RDI", "RSI", "RDX", "RCX", "RBX", "RAX", "Error Code", "RIP", "CS", "RFLAGS", "RSP", "SS", ]; public enum ulong EXC_PAGE_FAULT = 14; /* The I/O APIC is configured to map IRQ 0 to interrupt 64 (0x40). */ public enum ulong INT_APIC_BASE = 0x40u; /* IRQ 0 */ public enum ulong INT_APIC_COUNT = 24u; public enum ulong INT_APIC_TIMER = 0x70u; public enum ulong INT_APIC_LINT0 = 0x71u; public enum ulong INT_APIC_LINT1 = 0x72u; public enum ulong INT_KERNEL_SWINT = 0x80u; public enum ulong INT_APIC_SPURIOUS = 0xFFu; private enum N_ISRS = 256; /** * IDT descriptor entry type. */ struct descriptor_t { ulong[2] descriptor; this(T)(size_t index, ulong dpl, bool trap, T offset) { descriptor[0] = 0x0000_8E_00_0000_0000u | ((cast(ulong)offset & 0xFFFF_0000u) << 32u) | (dpl << 45u) | ((trap ? 1uL : 0uL) << 40u) | (Gdt.SELECTOR_KERNEL_CODE << 16u) | (cast(ulong)offset & 0xFFFFu); descriptor[1] = cast(ulong)offset >> 32u; } } /** * IDTR register format. */ struct idtr_t { ushort limit; align(2) ulong offset; } static assert(idtr_t.sizeof == 10u); private static __gshared idtr_t idtr; private static __gshared align(0x10) descriptor_t[N_ISRS] idt; static assert(idt.sizeof == (N_ISRS * 16u)); public static void initialize() { set_isrs(); idtr.limit = idt.sizeof - 1u; idtr.offset = cast(ulong)&idt; lidt(&idtr); } private static void lidt(idtr_t * idtr) { __asm("lidt $0", "*m", idtr); } private static void set_isrs() { static bool isr_has_error_code(size_t isr) { return (isr == 8) || ((10 <= isr) && (isr <= 14)) || (isr == 17) || (isr == 21) || (isr == 29) || (isr == 30); } static bool isr_is_trap(size_t isr) { return (isr < 32u) && (isr != 2u); } __asm("jmp 1f", ""); static foreach(isr; 0 .. N_ISRS) { mixin("__asm(`isr_", isr, ": " ~ (isr_has_error_code(isr) ? "" : "pushq $$0") ~ " pushq %rax mov $$", isr, ", %rax jmp isr_common`, ``);"); } __asm(`isr_common: pushq %rbx pushq %rcx pushq %rdx pushq %rsi pushq %rdi pushq %r8 pushq %r9 pushq %r10 pushq %r11 pushq %r12 pushq %r13 pushq %r14 pushq %r15 pushq %rbp mov %rsp, %rbp mov %rsp, %rsi and $$0xFFFFFFFFFFFFFFF0, %rsp mov %rax, %rdi movabs $$isr, %rax callq *%rax mov %rax, %rsp popq %rbp popq %r15 popq %r14 popq %r13 popq %r12 popq %r11 popq %r10 popq %r9 popq %r8 popq %rdi popq %rsi popq %rdx popq %rcx popq %rbx popq %rax add $$8, %rsp iretq 1:`, ``); for (size_t isr = 0; isr < N_ISRS; isr++) { idt[isr] = descriptor_t(isr, 0u, isr_is_trap(isr), isrs[isr]); } } } /** * Interrupt service routine. * * This function returns the stack pointer of the thread to execute when * returning from this function. */ public extern(C) ulong * isr(ulong vector, ulong * stack_frame) { Thread.current_thread.stack_pointer = stack_frame; if ((vector >= Idt.INT_APIC_BASE) && (vector < (Idt.INT_APIC_BASE + Idt.INT_APIC_COUNT))) { Apic.isr(vector); } else { switch (vector) { case Idt.INT_APIC_TIMER: case Idt.INT_APIC_LINT0: case Idt.INT_APIC_LINT1: case Idt.INT_APIC_SPURIOUS: Apic.isr(vector); break; default: Fb.rect(0, 0, Fb.width, Kfont.line_height, 0xCC0000u); Klog.writefln("\n **** Unhandled ISR %u ****", vector); for (size_t i = 0u; i < Idt.ISF_COUNT; i++) { Klog.writef(Idt.ISF_NAMES[i]); Klog.writefln(" = 0x%x", stack_frame[i]); } Klog.writefln("CR2 = 0x%p", read_cr2()); __asm("cli", ""); for (;;) { __asm("hlt", ""); } } } Thread.current_thread = Thread.current_thread.list_next; return Thread.current_thread.stack_pointer; }