summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/panic.h1
-rw-r--r--src/arch/amd64/debugger.c343
-rw-r--r--src/arch/amd64/debugger.h9
-rw-r--r--src/arch/amd64/idt.S26
-rw-r--r--src/arch/amd64/idt.c17
-rw-r--r--src/arch/amd64/idt.h26
-rw-r--r--src/kmain.c1
-rw-r--r--src/lib.c4
8 files changed, 411 insertions, 16 deletions
diff --git a/include/panic.h b/include/panic.h
index 4f8592e..dc96da1 100644
--- a/include/panic.h
+++ b/include/panic.h
@@ -4,6 +4,7 @@
#define _PANIC_STR2(x) #x
#define panic(msg) _panic_impl(_PANIC_STR(__LINE__), __FILE__, msg)
+#define kassert(val, msg) do { if (!(val)) { panic(msg); } } while(0)
_Noreturn void _panic_impl(char *line, char *file, char *msg);
diff --git a/src/arch/amd64/debugger.c b/src/arch/amd64/debugger.c
new file mode 100644
index 0000000..4df59f7
--- /dev/null
+++ b/src/arch/amd64/debugger.c
@@ -0,0 +1,343 @@
+#include <lib.h>
+#include <stddef.h>
+#include <serial.h>
+#include <backtrace.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;
+};
+
+static size_t dbg_steps = 0;
+static int dbg_continue = 0;
+static struct breakpoint bkps[4] = {{0}, {0}, {0}, {0}};
+
+static void dbg_putc(char c) {
+ serial_out(c);
+}
+
+static void dbg_puts(char *msg) {
+ serial_out_str(msg);
+}
+
+static char dbg_getc() {
+ return serial_in();
+}
+
+static void debugger_msg(int cause, struct dr6 dr6) {
+ char buf[24];
+ switch (cause) {
+ case DEBUG_INT3:
+ dbg_puts("dbg: reached int3");
+ break;
+ case DEBUG_FAULT:
+ dbg_puts("dbg: a fault occured");
+ break;
+ case DEBUG_DBG:
+ if (dr6.bs)
+ dbg_puts("dbg: finished steps");
+ else if (dr6.b0)
+ dbg_puts("dbg: reached breakpoint 0");
+ else if (dr6.b1)
+ dbg_puts("dbg: reached breakpoint 1");
+ else if (dr6.b2)
+ dbg_puts("dbg: reached breakpoint 2");
+ else if (dr6.b3)
+ dbg_puts("dbg: reached breakpoint 3");
+ }
+ if (dbg_steps > 0) {
+ ultoa(dbg_steps, buf, 10);
+ dbg_puts(" (");
+ dbg_puts(buf);
+ dbg_puts(" steps left)");
+ }
+ if (dbg_continue > 0) {
+ dbg_puts(" (paused continue)");
+ }
+ dbg_puts("\n");
+}
+
+static void debugger_print_reg(char *name, uint64_t val) {
+ char buf[24];
+ char *end;
+ dbg_puts(name);
+ dbg_puts(": 0x");
+
+ end = ultoa(val, buf, 16);
+ for (int i = 0; i < 16 - (end - buf); i++) {
+ dbg_putc('0');
+ }
+ dbg_puts(buf);
+
+ dbg_puts(" (");
+ end = ultoa(val, buf, 10);
+ dbg_puts(buf);
+ dbg_puts(")\n");
+}
+
+static void debugger_print_regs(struct isr_regs *state) {
+ debugger_print_reg("rax", state->rax);
+ debugger_print_reg("rbx", state->rbx);
+ debugger_print_reg("rcx", state->rcx);
+ debugger_print_reg("rdx", state->rdx);
+ debugger_print_reg("rsi", state->rsi);
+ debugger_print_reg("rdi", state->rdi);
+ debugger_print_reg("rsp", state->rsp);
+ debugger_print_reg("rbp", state->rbp);
+ debugger_print_reg("r8 ", state->r8 );
+ debugger_print_reg("r9 ", state->r9 );
+ debugger_print_reg("r10", state->r10);
+ debugger_print_reg("r11", state->r11);
+ debugger_print_reg("r12", state->r12);
+ debugger_print_reg("r13", state->r13);
+ debugger_print_reg("r14", state->r14);
+ debugger_print_reg("r15", state->r15);
+ dbg_puts("---\n");
+ debugger_print_reg("rip", state->rip);
+ dbg_puts("---\n");
+ debugger_print_reg("rflags", state->rflags);
+ struct rflags *rflags = (struct rflags *)state->rflags;
+ dbg_puts("rflags: ");
+ if(rflags->cf) dbg_puts("CF ");
+ if(rflags->pf) dbg_puts("PF ");
+ if(rflags->af) dbg_puts("AF ");
+ if(rflags->zf) dbg_puts("ZF ");
+ if(rflags->sf) dbg_puts("SF ");
+ if(rflags->tf) dbg_puts("TF ");
+ if(rflags->if_) dbg_puts("IF ");
+ if(rflags->df) dbg_puts("DF ");
+ if(rflags->of) dbg_puts("OF ");
+ if(rflags->iopl) dbg_puts("IOPL ");
+ if(rflags->nt) dbg_puts("NT ");
+ if(rflags->md) dbg_puts("MD ");
+ if(rflags->rf) dbg_puts("RF ");
+ if(rflags->vm) dbg_puts("VM ");
+ if(rflags->ac) dbg_puts("AC ");
+ if(rflags->vif) dbg_puts("VIF ");
+ if(rflags->vip) dbg_puts("VIP ");
+ if(rflags->id) dbg_puts("ID ");
+ dbg_putc('\n');
+}
+
+#define PROMPT_LEN 60
+
+static int debugger_handle_bkp_cmd(char *buf) {
+ if (buf[0] == 'l') {
+ // list breakpoints
+ char buf[20];
+ for (int i = 0; i < 4; i++) {
+ dbg_puts("breakpoint ");
+ dbg_putc('0' + i);
+ dbg_puts(": ");
+ struct breakpoint bkp = bkps[i];
+ if(!bkp.used) {
+ dbg_puts("unset\n");
+ continue;
+ }
+ dbg_puts(" 0x");
+ ultoa(bkp.addr, buf, 16);
+ dbg_puts(buf);
+ dbg_puts(" len=");
+ switch (bkp.len) {
+ case 0x0: dbg_putc('1'); break;
+ case 0x1: dbg_putc('2'); break;
+ case 0x2: dbg_putc('8'); break;
+ case 0x3: dbg_putc('4'); break;
+ }
+ dbg_puts(" trigger=");
+ switch (bkp.rw) {
+ case 0x0: dbg_puts("x "); break;
+ case 0x1: dbg_puts("w "); break;
+ case 0x2: dbg_puts("io"); break;
+ case 0x3: dbg_puts("rw"); break;
+ }
+ dbg_puts(bkp.enable ? " enabled" : " disabled");
+ }
+ return 1;
+ } else if (buf[0] == 'd') {
+ // disable breakpoints
+ bkps[0].enable = 0;
+ bkps[1].enable = 0;
+ bkps[2].enable = 0;
+ bkps[3].enable = 0;
+ return 1;
+ } else if (buf[0] == 'c') {
+ // clear breakpoints
+ bkps[0].used = 0;
+ bkps[1].used = 0;
+ bkps[2].used = 0;
+ bkps[3].used = 0;
+ return 1;
+ } else if (buf[0] <= '0' && buf[0] >= '3') {
+ dbg_puts("invalid breakpoint command\n");
+ return 1;
+ }
+ // TODO set breakpoints
+ 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[0].used;
+ dr7.g2 = bkps[2].enable & bkps[0].used;
+ dr7.g3 = bkps[3].enable & bkps[0].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));
+}
+
+static int debugger_prompt(struct isr_regs *state) {
+ char buf[PROMPT_LEN];
+ char *p = buf;
+ dbg_puts("dbg> ");
+ while ((*p = dbg_getc()) != '\r' && p < buf + PROMPT_LEN - 1) {
+ dbg_putc(*p++);
+ }
+ dbg_putc('\n');
+ for(; p < buf + PROMPT_LEN; p++) {
+ *p = 0;
+ }
+
+ struct rflags *rflags = (struct rflags *)&state->rflags;
+
+ switch (buf[0]) {
+ case '\0': // nothing entered
+ return 1;
+ case 'h': // help
+ dbg_puts("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 'b': // breakpoints
+ {
+ int res = debugger_handle_bkp_cmd(buf+1);
+ debugger_load_bkps();
+ return res;
+ }
+ default:
+ dbg_puts("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) {
+
+ } else {
+ if (dr6.bs && dbg_steps > 0) {
+ dbg_steps--;
+ return;
+ }
+
+ if (dr6.bs && dbg_continue) {
+ return;
+ }
+ }
+
+ debugger_msg(cause, dr6);
+
+ dbg_steps = 0;
+ dbg_continue = 0;
+
+ struct rflags *rflags = (struct rflags *)&state->rflags;
+ rflags->tf = 0;
+
+ while (debugger_prompt(state));
+}
diff --git a/src/arch/amd64/debugger.h b/src/arch/amd64/debugger.h
new file mode 100644
index 0000000..e679c07
--- /dev/null
+++ b/src/arch/amd64/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/idt.S b/src/arch/amd64/idt.S
index 33d488a..cd8783e 100644
--- a/src/arch/amd64/idt.S
+++ b/src/arch/amd64/idt.S
@@ -11,9 +11,9 @@ extern idt_pic_eoi
push rbx
push rcx
push rdx
- push rbp
- push rdi
push rsi
+ push rdi
+ push rbp
push r8
push r9
push r10
@@ -33,9 +33,9 @@ extern idt_pic_eoi
pop r10
pop r9
pop r8
- pop rsi
- pop rdi
pop rbp
+ pop rdi
+ pop rsi
pop rdx
pop rcx
pop rbx
@@ -51,8 +51,7 @@ isr_stub_%+%1:
cld
mov rdi, %1 ; exception number
mov rsi, 0 ; placeholder error code
- mov rdx, [rsp + 15 * 8] ; instruction pointer
- mov rcx, rbp ; base pointer for stack trace
+ mov rdx, rsp ; top of stack
call idt_exception_handler
POPALL
iretq
@@ -64,15 +63,18 @@ isr_stub_%+%1:
%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, [rsp + 15 * 8] ; error code
- mov rdx, [rsp + 16 * 8] ; instruction pointer
- mov rcx, rbp ; base pointer for stack trace
+ mov rsi, [isr_err_code] ; error code
+ mov rdx, rsp ; top of stack
call idt_exception_handler
POPALL
- sub rsp, 8 ; discard error code
iretq
%endmacro
@@ -188,6 +190,10 @@ PICGeneric 47 ; 15
%assign i i+1
%endrep
+section .data
+isr_tmp: dq 0
+isr_err_code: dq 0
+
; isr stub table
section .rodata
bits 64
diff --git a/src/arch/amd64/idt.c b/src/arch/amd64/idt.c
index 583b8a9..c0ea5a8 100644
--- a/src/arch/amd64/idt.c
+++ b/src/arch/amd64/idt.c
@@ -1,10 +1,10 @@
#include <stdint.h>
#include <stddef.h>
-#include <panic.h>
#include <lib.h>
#include <serial.h>
+#include <panic.h>
-#include "backtrace.h"
+#include "debugger.h"
#include "idt.h"
#include "pic.h"
@@ -108,7 +108,16 @@ char *EXCEPTIONS[] = {
"0x1F Reserved",
};
-void idt_exception_handler(uint64_t exception, uint64_t code, void *rip, void *rbp) {
+void idt_exception_handler(uint64_t exception, uint64_t code, struct isr_regs *state) {
+ // breakpoint interrupt
+ if (exception == 0x03) {
+ debugger(state, DEBUG_INT3);
+ return;
+ } else if (exception == 0x01) {
+ debugger(state, DEBUG_DBG);
+ return;
+ }
+
// TODO don't just panic
char buf[24];
char msg[256] = "Exception ";
@@ -128,7 +137,7 @@ void idt_exception_handler(uint64_t exception, uint64_t code, void *rip, void *r
strcat(msg, buf);
}
- panic_interrupt(rip, rbp, msg);
+ panic_interrupt((void *)state->rip, (void *)state->rbp, msg);
}
void idt_pic_eoi(uint8_t exception) {
diff --git a/src/arch/amd64/idt.h b/src/arch/amd64/idt.h
index 5627657..89a9dab 100644
--- a/src/arch/amd64/idt.h
+++ b/src/arch/amd64/idt.h
@@ -1,3 +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();
diff --git a/src/kmain.c b/src/kmain.c
index bb0584f..07244bf 100644
--- a/src/kmain.c
+++ b/src/kmain.c
@@ -4,6 +4,7 @@
#include <serial.h>
#include <fb.h>
#include <shim.h>
+#include <panic.h>
void kmain(struct boot_info *info) {
memory_init(&info->map);
diff --git a/src/lib.c b/src/lib.c
index 6c17161..ee4afcb 100644
--- a/src/lib.c
+++ b/src/lib.c
@@ -212,7 +212,7 @@ end:
if (n == 0) { \
buffer[0] = '0'; \
buffer[1] = '\0'; \
- return buffer; \
+ return buffer + 1; \
} \
char *start = buffer; \
for (; n; n /= radix) { \
@@ -237,7 +237,7 @@ UXTOA(long long int, ulltoa)
if (n == 0) { \
buffer[0] = '0'; \
buffer[1] = '\0'; \
- return buffer; \
+ return buffer + 1; \
} \
if (n < 0) { \
*buffer++ = '-'; \