269 lines
6.1 KiB
D
269 lines
6.1 KiB
D
/**
|
|
* 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;
|
|
}
|