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 +#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)); +} 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 #include -#include #include #include +#include -#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 + +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 #include #include +#include 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++ = '-'; \