/** * 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; 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 static enum ulong EXC_PAGE_FAULT = 14; /* The I/O APIC is configured to map IRQ 0 to interrupt 64 (0x40). */ public static enum ulong INT_APIC_BASE = 0x40u; /* IRQ 0 */ public static enum ulong INT_APIC_COUNT = 24u; public static enum ulong INT_APIC_TIMER = 0x70u; public static enum ulong INT_APIC_LINT0 = 0x71u; public static enum ulong INT_APIC_LINT1 = 0x72u; public static enum ulong INT_APIC_SPURIOUS = 0xFFu; struct idtr_t { ushort limit; align(2) ulong offset; } static assert(idtr_t.sizeof == 10u); alias descriptor_t = ulong[2]; static __gshared align(0x10) descriptor_t[256] idt; static assert(idt.sizeof == (256u * 16u)); static __gshared idtr_t idtr; 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 bool isr_has_error_code(size_t isr) { return (isr == 8) || ((10 <= isr) && (isr <= 14)) || (isr == 17) || (isr == 21) || (isr == 29) || (isr == 30); } private static bool isr_is_trap(size_t isr) { return (isr < 32u) && (isr != 2u); } private static void set_isrs() { ulong isr_address; static foreach(isr; 0 .. 256) { mixin("isr_address = __asm!ulong(` movabs $$1f, %rax jmp 2f 1: " ~ (isr_has_error_code(isr) ? "" : "pushq $$0") ~ " pushq %rax 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 $$", isr, ", %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 2:`, `={rax}`);"); mixin("set_descriptor(", isr, ", 0u, " ~ (isr_is_trap(isr) ? "true" : "false") ~ ", isr_address);"); } } private static void set_descriptor(size_t index, ulong dpl, bool trap, ulong offset) { idt[index][0] = 0x0000_8E_00_0000_0000u | ((offset & 0xFFFF_0000u) << 32u) | (dpl << 45u) | ((trap ? 1uL : 0uL) << 40u) | (Gdt.SELECTOR_KERNEL_CODE << 16u) | (offset & 0xFFFFu); idt[index][1] = offset >> 32u; } } /** * 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; }