diff options
Diffstat (limited to 'src/arch/amd64/cpu/debugger.c')
-rw-r--r-- | src/arch/amd64/cpu/debugger.c | 414 |
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)); +} |