This commit is contained in:
trimill 2024-02-01 16:53:07 -05:00
parent b5b904cfc9
commit c8c8d009f6
No known key found for this signature in database
GPG key ID: 4F77A16E17E10BCB
8 changed files with 411 additions and 16 deletions

View file

@ -4,6 +4,7 @@
#define _PANIC_STR2(x) #x #define _PANIC_STR2(x) #x
#define panic(msg) _panic_impl(_PANIC_STR(__LINE__), __FILE__, msg) #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); _Noreturn void _panic_impl(char *line, char *file, char *msg);

343
src/arch/amd64/debugger.c Normal file
View file

@ -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));
}

View file

@ -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);

View file

@ -11,9 +11,9 @@ extern idt_pic_eoi
push rbx push rbx
push rcx push rcx
push rdx push rdx
push rbp
push rdi
push rsi push rsi
push rdi
push rbp
push r8 push r8
push r9 push r9
push r10 push r10
@ -33,9 +33,9 @@ extern idt_pic_eoi
pop r10 pop r10
pop r9 pop r9
pop r8 pop r8
pop rsi
pop rdi
pop rbp pop rbp
pop rdi
pop rsi
pop rdx pop rdx
pop rcx pop rcx
pop rbx pop rbx
@ -51,8 +51,7 @@ isr_stub_%+%1:
cld cld
mov rdi, %1 ; exception number mov rdi, %1 ; exception number
mov rsi, 0 ; placeholder error code mov rsi, 0 ; placeholder error code
mov rdx, [rsp + 15 * 8] ; instruction pointer mov rdx, rsp ; top of stack
mov rcx, rbp ; base pointer for stack trace
call idt_exception_handler call idt_exception_handler
POPALL POPALL
iretq iretq
@ -64,15 +63,18 @@ isr_stub_%+%1:
%macro ISRExceptionCode 1 %macro ISRExceptionCode 1
align 8 align 8
isr_stub_%+%1: 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 PUSHALL
cld cld
mov rdi, %1 ; exception number mov rdi, %1 ; exception number
mov rsi, [rsp + 15 * 8] ; error code mov rsi, [isr_err_code] ; error code
mov rdx, [rsp + 16 * 8] ; instruction pointer mov rdx, rsp ; top of stack
mov rcx, rbp ; base pointer for stack trace
call idt_exception_handler call idt_exception_handler
POPALL POPALL
sub rsp, 8 ; discard error code
iretq iretq
%endmacro %endmacro
@ -188,6 +190,10 @@ PICGeneric 47 ; 15
%assign i i+1 %assign i i+1
%endrep %endrep
section .data
isr_tmp: dq 0
isr_err_code: dq 0
; isr stub table ; isr stub table
section .rodata section .rodata
bits 64 bits 64

View file

@ -1,10 +1,10 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <panic.h>
#include <lib.h> #include <lib.h>
#include <serial.h> #include <serial.h>
#include <panic.h>
#include "backtrace.h" #include "debugger.h"
#include "idt.h" #include "idt.h"
#include "pic.h" #include "pic.h"
@ -108,7 +108,16 @@ char *EXCEPTIONS[] = {
"0x1F Reserved", "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 // TODO don't just panic
char buf[24]; char buf[24];
char msg[256] = "Exception "; 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); strcat(msg, buf);
} }
panic_interrupt(rip, rbp, msg); panic_interrupt((void *)state->rip, (void *)state->rbp, msg);
} }
void idt_pic_eoi(uint8_t exception) { void idt_pic_eoi(uint8_t exception) {

View file

@ -1,3 +1,29 @@
#pragma once #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(); void idt_init();

View file

@ -4,6 +4,7 @@
#include <serial.h> #include <serial.h>
#include <fb.h> #include <fb.h>
#include <shim.h> #include <shim.h>
#include <panic.h>
void kmain(struct boot_info *info) { void kmain(struct boot_info *info) {
memory_init(&info->map); memory_init(&info->map);

View file

@ -212,7 +212,7 @@ end:
if (n == 0) { \ if (n == 0) { \
buffer[0] = '0'; \ buffer[0] = '0'; \
buffer[1] = '\0'; \ buffer[1] = '\0'; \
return buffer; \ return buffer + 1; \
} \ } \
char *start = buffer; \ char *start = buffer; \
for (; n; n /= radix) { \ for (; n; n /= radix) { \
@ -237,7 +237,7 @@ UXTOA(long long int, ulltoa)
if (n == 0) { \ if (n == 0) { \
buffer[0] = '0'; \ buffer[0] = '0'; \
buffer[1] = '\0'; \ buffer[1] = '\0'; \
return buffer; \ return buffer + 1; \
} \ } \
if (n < 0) { \ if (n < 0) { \
*buffer++ = '-'; \ *buffer++ = '-'; \