summaryrefslogtreecommitdiff
path: root/src/arch/amd64/cpu/debugger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/amd64/cpu/debugger.c')
-rw-r--r--src/arch/amd64/cpu/debugger.c414
1 files changed, 414 insertions, 0 deletions
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));
+}