summaryrefslogtreecommitdiff
path: root/src/arch/amd64/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/amd64/cpu')
-rw-r--r--src/arch/amd64/cpu/backtrace.c45
-rw-r--r--src/arch/amd64/cpu/debugger.c414
-rw-r--r--src/arch/amd64/cpu/debugger.h9
-rw-r--r--src/arch/amd64/cpu/idt.S207
-rw-r--r--src/arch/amd64/cpu/idt.c160
-rw-r--r--src/arch/amd64/cpu/idt.h29
6 files changed, 864 insertions, 0 deletions
diff --git a/src/arch/amd64/cpu/backtrace.c b/src/arch/amd64/cpu/backtrace.c
new file mode 100644
index 0000000..9e1c9d7
--- /dev/null
+++ b/src/arch/amd64/cpu/backtrace.c
@@ -0,0 +1,45 @@
+#include <backtrace.h>
+#include <lib.h>
+
+struct stackframe {
+ struct stackframe *rbp;
+ void *rip;
+};
+
+size_t backtrace(void **dst, size_t len) {
+ struct stackframe *rbp;
+ __asm__ volatile ("mov %%rbp, %0" : "=r"(rbp));
+ return backtrace_ex(dst, len, rbp->rip, rbp->rbp);
+}
+
+size_t backtrace_ex(void **dst, size_t len, void *ip, void *bp) {
+ struct stackframe *frame = bp;
+ __asm__ volatile ("mov %%rbp, %0" : "=r"(frame));
+ if (len > 0) {
+ dst[0] = ip;
+ }
+ size_t i;
+ for (i = 1; frame && i < len; i++) {
+ dst[i] = frame->rip;
+ frame = frame->rbp;
+ }
+ return i;
+}
+
+void log_backtrace() {
+ struct stackframe *rbp;
+ __asm__ volatile ("mov %%rbp, %0" : "=r"(rbp));
+ log_backtrace_ex(rbp->rip, rbp->rbp);
+}
+
+
+void log_backtrace_ex(void *ip, void *bp) {
+ struct stackframe *frame = bp;
+ kputs("Stack trace:\n");
+ kprintf(" %#lx\n", (size_t)ip);
+ while (frame) {
+ kprintf(" %#lx\n", (size_t)frame->rip);
+ frame = frame->rbp;
+ }
+
+}
diff --git a/src/arch/amd64/cpu/debugger.c b/src/arch/amd64/cpu/debugger.c
new file mode 100644
index 0000000..4d9d3c3
--- /dev/null
+++ b/src/arch/amd64/cpu/debugger.c
@@ -0,0 +1,414 @@
+#include <lib.h>
+#include <stddef.h>
+#include <backtrace.h>
+#include <serial.h>
+
+#include "debugger.h"
+
+struct dr6 {
+ uint64_t b0 : 1,
+ b1 : 1,
+ b2 : 1,
+ b3 : 1,
+ : 7,
+ bld : 1,
+ : 1,
+ bd : 1,
+ bs : 1,
+ bt : 1,
+ rtm : 1,
+ : 47;
+};
+
+struct dr7 {
+ uint64_t l0 : 1,
+ g0 : 1,
+ l1 : 1,
+ g1 : 1,
+ l2 : 1,
+ g2 : 1,
+ l3 : 1,
+ g3 : 1,
+ : 8,
+ rw0 : 2,
+ len0 : 2,
+ rw1 : 2,
+ len1 : 2,
+ rw2 : 2,
+ len2 : 2,
+ rw3 : 2,
+ len3 : 2,
+ : 32;
+};
+
+struct rflags {
+ uint64_t cf : 1,
+ : 1,
+ pf : 1,
+ : 1,
+ af : 1,
+ : 1,
+ zf : 1,
+ sf : 1,
+
+ tf : 1,
+ if_ : 1,
+ df : 1,
+ of : 1,
+ iopl : 2,
+ nt : 1,
+ md : 1,
+
+ rf : 1,
+ vm : 1,
+ ac : 1,
+ vif : 1,
+ vip : 1,
+ id : 1,
+ : 42;
+};
+
+struct breakpoint {
+ uint64_t addr;
+ uint8_t len;
+ uint8_t rw;
+ uint8_t used;
+ uint8_t enable;
+ uint8_t instr_len;
+};
+
+static int dbg_steps = 0;
+static int dbg_continue = 0;
+static struct breakpoint bkps[4] = {{0}, {0}, {0}, {0}};
+
+static size_t debugger_read(char *buf, size_t len) {
+ char *p = buf;
+ size_t result;
+ while (p < buf + len - 1) {
+ char ch = serial_in();
+ switch (ch) {
+ case '\r':
+ goto end;
+ case '\x7f':
+ if (p > buf) {
+ p--;
+ kputs("\x08 \x08");
+ }
+ break;
+ default:
+ kputc(ch);
+ *p++ = ch;
+ }
+ }
+end:
+ result = p - buf;
+ kputc('\n');
+ for(; p < buf + len; p++) {
+ *p = 0;
+ }
+ return result;
+}
+
+static void debugger_msg(int cause, struct dr6 dr6) {
+ switch (cause) {
+ case DEBUG_INT3:
+ kputs("dbg: reached int3");
+ break;
+ case DEBUG_FAULT:
+ kputs("dbg: a fault occured");
+ break;
+ case DEBUG_DBG:
+ if (dr6.bs)
+ kputs("dbg: finished steps");
+ if (dr6.b0)
+ kputs("dbg: reached breakpoint 0");
+ else if (dr6.b1)
+ kputs("dbg: reached breakpoint 1");
+ else if (dr6.b2)
+ kputs("dbg: reached breakpoint 2");
+ else if (dr6.b3)
+ kputs("dbg: reached breakpoint 3");
+ }
+ if (dbg_steps > 0) {
+ kprintf(" (%d steps left)", dbg_steps);
+ }
+ if (dbg_continue > 0) {
+ kputs(" (paused continue)");
+ }
+ kputs("\n");
+}
+
+static void debugger_print_regs(struct isr_regs *state) {
+ kprintf("rax: %#016lx (%lu)\n", state->rax, state->rax);
+ kprintf("rbx: %#016lx (%lu)\n", state->rbx, state->rbx);
+ kprintf("rcx: %#016lx (%lu)\n", state->rcx, state->rcx);
+ kprintf("rdx: %#016lx (%lu)\n", state->rdx, state->rdx);
+ kprintf("rsi: %#016lx (%lu)\n", state->rsi, state->rsi);
+ kprintf("rdi: %#016lx (%lu)\n", state->rdi, state->rdi);
+ kprintf("rsp: %#016lx (%lu)\n", state->rsp, state->rsp);
+ kprintf("rbp: %#016lx (%lu)\n", state->rbp, state->rbp);
+ kprintf("r8 : %#016lx (%lu)\n", state->r8 , state->r8 );
+ kprintf("r9 : %#016lx (%lu)\n", state->r9 , state->r9 );
+ kprintf("r10: %#016lx (%lu)\n", state->r10, state->r10);
+ kprintf("r11: %#016lx (%lu)\n", state->r11, state->r11);
+ kprintf("r12: %#016lx (%lu)\n", state->r12, state->r12);
+ kprintf("r13: %#016lx (%lu)\n", state->r13, state->r13);
+ kprintf("r14: %#016lx (%lu)\n", state->r14, state->r14);
+ kprintf("r15: %#016lx (%lu)\n", state->r15, state->r15);
+ kputs("---\n");
+ kprintf("rip: %#016lx (%lu)\n", state->rip, state->rip);
+ kputs("---\n");
+ kprintf("rflags: %#016lx (%lu)\n", state->rflags, state->rflags);
+ struct rflags *rflags = (struct rflags *)state->rflags;
+ kputs("rflags: ");
+ if(rflags->cf) kputs("CF ");
+ if(rflags->pf) kputs("PF ");
+ if(rflags->af) kputs("AF ");
+ if(rflags->zf) kputs("ZF ");
+ if(rflags->sf) kputs("SF ");
+ if(rflags->tf) kputs("TF ");
+ if(rflags->if_) kputs("IF ");
+ if(rflags->df) kputs("DF ");
+ if(rflags->of) kputs("OF ");
+ if(rflags->iopl) kputs("IOPL ");
+ if(rflags->nt) kputs("NT ");
+ if(rflags->md) kputs("MD ");
+ if(rflags->rf) kputs("RF ");
+ if(rflags->vm) kputs("VM ");
+ if(rflags->ac) kputs("AC ");
+ if(rflags->vif) kputs("VIF ");
+ if(rflags->vip) kputs("VIP ");
+ if(rflags->id) kputs("ID ");
+ kputs("\n");
+}
+
+#define PROMPT_LEN 60
+
+static int debugger_handle_bkp_cmd(char *msg) {
+ if (msg[0] == 'l') {
+ // list breakpoints
+ for (int i = 0; i < 4; i++) {
+ struct breakpoint bkp = bkps[i];
+ if(!bkp.used) {
+ kprintf("breakpoint %d: unset\n", i);
+ continue;
+ }
+ const char *lenstrs[] = {"1", "2", "8", "4"};
+ const char *rwstrs[] = {"x ", "w ", "io", "rw"};
+ kprintf(
+ "breakpoint %d: %#016lx len=%s trigger=%s instrlen=%01d %s\n",
+ i,
+ bkp.addr,
+ lenstrs[bkp.len],
+ rwstrs[bkp.rw],
+ bkp.instr_len,
+ bkp.enable ? " enabled" : " disabled"
+ );
+ }
+ return 1;
+ } else if (msg[0] == 'd') {
+ // disable breakpoints
+ bkps[0].enable = 0;
+ bkps[1].enable = 0;
+ bkps[2].enable = 0;
+ bkps[3].enable = 0;
+ kputs("disabled breakpoints\n");
+ return 1;
+ } else if (msg[0] == 'c') {
+ // clear breakpoints
+ bkps[0].used = 0;
+ bkps[1].used = 0;
+ bkps[2].used = 0;
+ bkps[3].used = 0;
+ kputs("cleared breakpoints\n");
+ return 1;
+ } else if (msg[0] <= '0' && msg[0] >= '3') {
+ kputs("invalid breakpoint command\n");
+ return 1;
+ }
+ char buf[64];
+ int id = msg[0] - '0';
+ switch (msg[1]) {
+ case 'e':
+ bkps[id].enable = bkps[id].used;
+ kprintf("enabled breakpoint %d\n", id);
+ return 1;
+ case 'd':
+ bkps[id].enable = 0;
+ kprintf("disabled breakpoint %d\n", id);
+ return 1;
+ case 'c':
+ bkps[id].used = 0;
+ kprintf("cleared breakpoint %d\n", id);
+ return 1;
+ case 'a':
+ kputs("addr> 0x");
+ debugger_read(buf, 64);
+ bkps[id].addr = strtoll(buf, NULL, 16);
+ kputs("len (1/2/4/8)> ");
+ debugger_read(buf, 64);
+ switch (buf[0]) {
+ case '1': bkps[id].len = 0x0; break;
+ case '2': bkps[id].len = 0x1; break;
+ case '8': bkps[id].len = 0x2; break;
+ case '4': bkps[id].len = 0x3; break;
+ default: bkps[id].len = 0x0; break;
+ }
+ kputs("cond (x/w/io/rw)> ");
+ debugger_read(buf, 64);
+ switch ((int)buf[0] * 256 + (int)buf[1]) {
+ case (int)'x'*256 + (int)0: bkps[id].rw = 0x0; break;
+ case (int)'w'*256 + (int)0: bkps[id].rw = 0x1; break;
+ case (int)'i'*256 + (int)'o': bkps[id].rw = 0x2; break;
+ case (int)'r'*256 + (int)'w': bkps[id].rw = 0x3; break;
+ default: bkps[id].len = 0x0; break;
+ }
+ kputs("instr len (1..15)> ");
+ debugger_read(buf, 64);
+ bkps[id].instr_len = strtoi(buf, NULL, 10);
+ kputs("enable (y/n)> ");
+ debugger_read(buf, 64);
+ bkps[id].used = 1;
+ bkps[id].enable = !(buf[0] == 'n' || buf[0] == 'N');
+ kprintf("added breakpoint %d\n", id);
+ return 1;
+ default:
+ kputs("invalid breakpoint command\n");
+ return 1;
+ }
+}
+
+static void debugger_load_bkps() {
+ struct dr7 dr7;
+ __asm__ volatile ("mov %%dr7, %0" : "=r"(dr7));
+ dr7.g0 = bkps[0].enable & bkps[0].used;
+ dr7.g1 = bkps[1].enable & bkps[1].used;
+ dr7.g2 = bkps[2].enable & bkps[2].used;
+ dr7.g3 = bkps[3].enable & bkps[3].used;
+ dr7.l0 = 0;
+ dr7.l1 = 0;
+ dr7.l2 = 0;
+ dr7.l3 = 0;
+ dr7.rw0 = bkps[0].rw;
+ dr7.rw1 = bkps[1].rw;
+ dr7.rw2 = bkps[2].rw;
+ dr7.rw3 = bkps[3].rw;
+ dr7.len0 = bkps[0].len;
+ dr7.len1 = bkps[1].len;
+ dr7.len2 = bkps[2].len;
+ dr7.len3 = bkps[3].len;
+ __asm__ volatile ("mov %0, %%dr7" : : "r"(dr7));
+ __asm__ volatile ("mov %0, %%dr0" : : "r"(bkps[0].addr));
+ __asm__ volatile ("mov %0, %%dr1" : : "r"(bkps[1].addr));
+ __asm__ volatile ("mov %0, %%dr2" : : "r"(bkps[2].addr));
+ __asm__ volatile ("mov %0, %%dr3" : : "r"(bkps[3].addr));
+}
+
+static int debugger_prompt(struct isr_regs *state) {
+ kputs("dbg> ");
+ char buf[64];
+ debugger_read(buf, 64);
+
+ struct rflags *rflags = (struct rflags *)&state->rflags;
+
+ switch (buf[0]) {
+ case '\0': // nothing entered
+ return 1;
+ case 'h': // help
+ kputs("see debugger.c for help information\n");
+ return 1;
+ case 'q': // quit
+ return 0;
+ case 'c': // continue
+ rflags->tf = 1;
+ dbg_continue = 1;
+ return 0;
+ case 'r': // print registers
+ debugger_print_regs(state);
+ return 1;
+ case 'k': // backtrace
+ log_backtrace_ex((void *)state->rip, (void *)state->rbp);
+ return 1;
+ case 's': // step (n)
+ rflags->tf = 1;
+ dbg_steps = atol(buf + 1) - 1;
+ //if (dbg_steps < 0) dbg_steps = 0;
+ return 0;
+ case 'm': // memory
+ {
+ kputs("addr> 0x");
+ debugger_read(buf, 64);
+ uint64_t start = (~0xF) & strtoll(buf, NULL, 16);
+
+ kputs("len> 0x");
+ debugger_read(buf, 64);
+ uint64_t len = (~0xF) & strtoll(buf, NULL, 16);
+
+ for (uint64_t a = start; a <= start + len; a += 0x10) {
+ kprintf("%08lx: ", a);
+ for (uint64_t b = 0; b < 0x10; b++) {
+ kprintf("%02x ", *(uint8_t *)(a + b));
+ if (b % 0x08 == 7) {
+ kputc(' ');
+ }
+ }
+ kputs("|");
+ for (uint64_t b = 0; b < 0x10; b++) {
+ uint8_t ch = *(uint8_t *)(a + b);
+ if (ch < 0x20 || ch >= 0x7f) {
+ ch = '.';
+ }
+ kputc(ch);
+ }
+ kputs("|\n");
+ }
+ return 1;
+ }
+ case 'b': // breakpoints
+ {
+ int res = debugger_handle_bkp_cmd(buf+1);
+ debugger_load_bkps();
+ return res;
+ }
+ default:
+ kputs("unknown command\n");
+ return 1;
+ }
+}
+
+void debugger(struct isr_regs *state, int cause) {
+ struct dr6 dr6;
+ __asm__ volatile ("mov %%dr6, %0" : "=r"(dr6));
+
+ if (!(dr6.b0 || dr6.b1 || dr6.b2 || dr6.b3)) {
+ if (dr6.bs && dbg_steps > 0) {
+ dbg_steps--;
+ return;
+ }
+
+ if (dr6.bs && dbg_continue) {
+ return;
+ }
+ }
+
+ debugger_msg(cause, dr6);
+
+ kputs("hi\n");
+
+ dbg_steps = 0;
+ dbg_continue = 0;
+
+ ((struct rflags *)&state->rflags)->tf = 0;
+
+ if (dr6.b0) {
+ state->rip += bkps[0].instr_len;
+ } else if (dr6.b1) {
+ state->rip += bkps[1].instr_len;
+ } else if (dr6.b2) {
+ state->rip += bkps[2].instr_len;
+ } else if (dr6.b3) {
+ state->rip += bkps[3].instr_len;
+ }
+
+ while (debugger_prompt(state));
+}
diff --git a/src/arch/amd64/cpu/debugger.h b/src/arch/amd64/cpu/debugger.h
new file mode 100644
index 0000000..14f73bf
--- /dev/null
+++ b/src/arch/amd64/cpu/debugger.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "idt.h"
+
+#define DEBUG_INT3 0x00
+#define DEBUG_DBG 0x01
+#define DEBUG_FAULT 0x02
+
+void debugger(struct isr_regs *state, int cause);
diff --git a/src/arch/amd64/cpu/idt.S b/src/arch/amd64/cpu/idt.S
new file mode 100644
index 0000000..cd8783e
--- /dev/null
+++ b/src/arch/amd64/cpu/idt.S
@@ -0,0 +1,207 @@
+extern idt_exception_handler
+global isr_stub_table
+
+extern idt_pic_timer
+extern idt_pic_keyboard
+extern idt_pic_mouse
+extern idt_pic_eoi
+
+%macro PUSHALL 0
+ push rax
+ push rbx
+ push rcx
+ push rdx
+ push rsi
+ push rdi
+ push rbp
+ push r8
+ push r9
+ push r10
+ push r11
+ push r12
+ push r13
+ push r14
+ push r15
+%endmacro
+
+%macro POPALL 0
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop rbp
+ pop rdi
+ pop rsi
+ pop rdx
+ pop rcx
+ pop rbx
+ pop rax
+%endmacro
+
+; call the exception handler with the interrupt number
+; args: interrupt number
+%macro ISRException 1
+align 8
+isr_stub_%+%1:
+ PUSHALL
+ cld
+ mov rdi, %1 ; exception number
+ mov rsi, 0 ; placeholder error code
+ mov rdx, rsp ; top of stack
+ call idt_exception_handler
+ POPALL
+ iretq
+%endmacro
+
+; call the exception handler with the interrupt number
+; these exceptions also put an error code on the stack
+; args: interrupt number
+%macro ISRExceptionCode 1
+align 8
+isr_stub_%+%1:
+ ; retrieve the error code without corrupting registers
+ mov [isr_tmp], rax
+ pop rax
+ mov [isr_err_code], rax
+ mov rax, [isr_tmp]
+ PUSHALL
+ cld
+ mov rdi, %1 ; exception number
+ mov rsi, [isr_err_code] ; error code
+ mov rdx, rsp ; top of stack
+ call idt_exception_handler
+ POPALL
+ iretq
+%endmacro
+
+%macro PICGeneric 1
+isr_stub_%+%1:
+ PUSHALL
+ cld
+ mov rdi, %1
+ call idt_pic_eoi
+ POPALL
+ iretq
+%endmacro
+
+%macro PICTimer 1
+isr_stub_%+%1:
+ PUSHALL
+ cld
+ call idt_pic_timer
+ mov rdi, %1
+ call idt_pic_eoi
+ POPALL
+ iretq
+%endmacro
+
+%macro PICKeyboard 1
+isr_stub_%+%1:
+ PUSHALL
+ cld
+ call idt_pic_keyboard
+ mov rdi, %1
+ call idt_pic_eoi
+ POPALL
+ iretq
+%endmacro
+
+%macro PICMouse 1
+isr_stub_%+%1:
+ PUSHALL
+ cld
+ call idt_pic_mouse
+ mov rdi, %1
+ call idt_pic_eoi
+ POPALL
+ iretq
+%endmacro
+
+; do nothing
+; args: interrupt number
+%macro ISRIgnore 1
+align 8
+isr_stub_%+%1:
+ iretq
+%endmacro
+
+; isr stubs
+section .text
+bits 64
+
+ISRException 0
+ISRException 1
+ISRException 2
+ISRException 3
+ISRException 4
+ISRException 5
+ISRException 6
+ISRException 7
+ISRExceptionCode 8
+ISRException 9
+ISRExceptionCode 10
+ISRExceptionCode 11
+ISRExceptionCode 12
+ISRExceptionCode 13
+ISRExceptionCode 14
+ISRException 15
+ISRException 16
+ISRExceptionCode 17
+ISRException 18
+ISRException 19
+ISRException 20
+ISRExceptionCode 21
+ISRException 22
+ISRException 23
+ISRException 24
+ISRException 25
+ISRException 26
+ISRException 27
+ISRException 28
+ISRExceptionCode 29
+ISRExceptionCode 30
+ISRException 31
+
+PICTimer 32 ; 0
+PICKeyboard 33 ; 1
+PICGeneric 34 ; 2
+PICGeneric 35 ; 3
+PICGeneric 36 ; 4
+PICGeneric 37 ; 5
+PICGeneric 38 ; 6
+PICGeneric 39 ; 7
+PICGeneric 40 ; 8
+PICGeneric 41 ; 9
+PICGeneric 42 ; 10
+PICGeneric 43 ; 11
+PICMouse 44 ; 12
+PICGeneric 45 ; 13
+PICGeneric 46 ; 14
+PICGeneric 47 ; 15
+
+; ignore other interrupts
+%assign i 48
+%rep 256 - i
+ ISRIgnore i
+ %assign i i+1
+%endrep
+
+section .data
+isr_tmp: dq 0
+isr_err_code: dq 0
+
+; isr stub table
+section .rodata
+bits 64
+align 16
+
+isr_stub_table:
+%assign i 0
+%rep 256
+ dq isr_stub_%+i
+%assign i i+1
+%endrep
diff --git a/src/arch/amd64/cpu/idt.c b/src/arch/amd64/cpu/idt.c
new file mode 100644
index 0000000..053f614
--- /dev/null
+++ b/src/arch/amd64/cpu/idt.c
@@ -0,0 +1,160 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <lib.h>
+#include <panic.h>
+#include "idt.h"
+#include "debugger.h"
+#include "../paging.h"
+#include "../drivers/pic.h"
+
+#define IDT_SIZE 256
+
+struct idt_entry {
+ uint16_t isr_low; // low 16 bits of isr
+ uint16_t kernel_cs; // kernel segment selector
+ uint8_t ist; // interrupt stack table
+ uint8_t flags; // gate type, privilege level, present bit
+ uint16_t isr_mid; // middle 16 bits of isr
+ uint32_t isr_high; // high 32 bits of isr
+ uint32_t reserved;
+} __attribute__((packed));
+
+struct idtr {
+ uint16_t size;
+ uint64_t address;
+} __attribute__((packed));
+
+// interrupt gate
+#define GATE_64BIT_INT 0x0E
+// trap gate
+#define GATE_64BIT_TRAP 0x0F
+
+// privilege ring allowed to call interrupt
+#define RING0 0x00
+#define RING1 0x20
+#define RING2 0x40
+#define RING3 0x60
+
+// interrupt is present in IDT
+#define PRESENT 0x80
+
+__attribute__((aligned(0x10)))
+static struct idt_entry idt[256];
+
+static struct idtr idtr;
+// from idt.S
+extern void *isr_stub_table[];
+
+// initialize and load the IDT
+void idt_init(void) {
+ // initialize idtr
+ idtr.address = (uint64_t)&idt;
+ idtr.size = (uint16_t)sizeof(struct idt_entry) * IDT_SIZE - 1;
+
+ // initialize idt
+ for (size_t vector = 0; vector < IDT_SIZE; vector++) {
+ struct idt_entry *entry = &idt[vector];
+
+ uint64_t isr = (uint64_t)isr_stub_table[vector];
+ // interrupts before 0x20 are for cpu exceptions
+ uint8_t gate_type = (vector < 0x20) ? GATE_64BIT_TRAP : GATE_64BIT_INT;
+
+ entry->kernel_cs = 0x08; // offset of 1 into GDT
+ entry->ist = 0;
+ entry->flags = PRESENT | RING0 | gate_type;
+ entry->isr_low = isr & 0xffff;
+ entry->isr_mid = (isr >> 16) & 0xffff;
+ entry->isr_high = (isr >> 32) & 0xffffffff;
+ entry->reserved = 0;
+ }
+
+ __asm__ volatile ("lidt %0" : : "m"(idtr));
+}
+
+// Intel manual vol 3 ch 6.3.1
+char *EXCEPTIONS[] = {
+ "Division Error",
+ "Debug",
+ "NMI",
+ "Breakpoint",
+ "Overflow",
+ "BOUND Range Exceeded",
+ "Invalid Opcode",
+ "Device Not Available",
+ "Double Fault",
+ "Coprocessor Segment Overrun",
+ "Invalid TSS",
+ "Segment Not Present",
+ "Stack-Segment Fault",
+ "General Protection Fault",
+ "Page Fault",
+ "Reserved",
+ "x87 Floating-Point Error",
+ "Alignment Check",
+ "Machine Check",
+ "SIMD Floaing-Point Exception",
+ "Virtualization Exception",
+ "Control Protection Exception",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Hypervisor Injection Exception",
+ "VMM Communication Exception",
+ "Security Exception",
+ "Reserved",
+};
+
+void idt_exception_handler(uint64_t exception, uint64_t code, struct isr_regs *state) {
+ switch (exception) {
+ case 0x01: // debug
+ debugger(state, DEBUG_DBG);
+ return;
+ case 0x03: // breakpoint
+ debugger(state, DEBUG_INT3);
+ return;
+ }
+
+ char custom[64];
+ *custom = '\0';
+
+ // page faults store the offending address in cr2
+ if (exception == 0x0E) {
+ strcat(custom, "\nPage fault address: 0x");
+ uint64_t cr2;
+ __asm__ volatile ("mov %%cr2, %0" : "=r"(cr2));
+ ultoa((size_t)cr2, custom + 23, 16);
+ }
+
+ panic_interrupt(
+ (void *)state->rip,
+ (void *)state->rbp,
+ "Exception %s\nError code 0x%lu%s",
+ EXCEPTIONS[exception],
+ code,
+ custom
+ );
+}
+
+void idt_pic_eoi(uint8_t exception) {
+ pic_eoi(exception - PIC_REMAP_OFFSET);
+}
+
+int counter = 0;
+
+void idt_pic_timer(void) {
+ // print a message once we know the timer works
+ // but avoid spamming the logs
+ if (counter == 3) {
+ //kputs("pic timer!\n");
+ }
+ if (counter <= 3) {
+ counter++;
+ }
+}
+
+void idt_pic_keyboard(void) {}
+
+void idt_pic_mouse(void) {}
diff --git a/src/arch/amd64/cpu/idt.h b/src/arch/amd64/cpu/idt.h
new file mode 100644
index 0000000..89a9dab
--- /dev/null
+++ b/src/arch/amd64/cpu/idt.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stdint.h>
+
+struct isr_regs {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rbp;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rdx;
+ uint64_t rcx;
+ uint64_t rbx;
+ uint64_t rax;
+
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+};
+
+void idt_init();