#include #include #include #include #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)); }