diff --git a/i386/i386/db_interface.c b/i386/i386/db_interface.c index d149adc..660da72 100644 --- a/i386/i386/db_interface.c +++ b/i386/i386/db_interface.c @@ -27,8 +27,6 @@ * Interface to new debugger. */ -#if MACH_KDB - #include #include @@ -57,12 +55,149 @@ #include #include +#if MACH_KDB +/* Whether the kernel uses any debugging register. */ +static int kernel_dr; +#endif + +void db_load_context(pcb_t pcb) +{ +#if MACH_KDB + int s = splhigh(); + + if (kernel_dr) { + splx(s); + return; + } +#endif + + /* Else set user debug registers */ + set_dr0(pcb->ims.ids.dr[0]); + set_dr1(pcb->ims.ids.dr[1]); + set_dr2(pcb->ims.ids.dr[2]); + set_dr3(pcb->ims.ids.dr[3]); + set_dr7(pcb->ims.ids.dr[7]); +#if MACH_KDB + splx(s); +#endif +} + +void db_get_debug_state( + pcb_t pcb, + struct i386_debug_state *state) +{ + *state = pcb->ims.ids; +} + +kern_return_t db_set_debug_state( + pcb_t pcb, + const struct i386_debug_state *state) +{ + int i; + + for (i = 0; i <= 3; i++) + if (state->dr[i] < VM_MIN_ADDRESS + || state->dr[i] >= VM_MAX_ADDRESS) + return KERN_INVALID_ARGUMENT; + + pcb->ims.ids = *state; + + if (pcb == current_thread()->pcb) + db_load_context(pcb); + + return KERN_SUCCESS; +} + +#if MACH_KDB + struct i386_saved_state *i386_last_saved_statep; struct i386_saved_state i386_nested_saved_state; unsigned i386_last_kdb_sp; extern thread_t db_default_thread; +static struct i386_debug_state ids; + +void db_dr ( + int num, + vm_offset_t linear_addr, + int type, + int len, + int persistence) +{ + int s = splhigh(); + unsigned long dr7; + + if (!kernel_dr) { + if (!linear_addr) { + splx(s); + return; + } + kernel_dr = 1; + /* Clear user debugging registers */ + set_dr7(0); + set_dr0(0); + set_dr1(0); + set_dr2(0); + set_dr3(0); + } + + ids.dr[num] = linear_addr; + switch (num) { + case 0: set_dr0(linear_addr); break; + case 1: set_dr1(linear_addr); break; + case 2: set_dr2(linear_addr); break; + case 3: set_dr3(linear_addr); break; + } + + /* Replace type/len/persistence for DRnum in dr7 */ + dr7 = get_dr7 (); + dr7 &= ~(0xfUL << (4*num+16)) & ~(0x3UL << (2*num)); + dr7 |= (((len << 2) | type) << (4*num+16)) | (persistence << (2*num)); + set_dr7 (dr7); + + if (kernel_dr) { + if (!ids.dr[0] && !ids.dr[1] && !ids.dr[2] && !ids.dr[3]) { + /* Not used any more, switch back to user debugging registers */ + kernel_dr = 0; + db_load_context(current_thread()->pcb); + } + } + splx(s); +} + +void db_set_hw_watchpoint( + int num, + task_t task, + db_addr_t addr, + vm_size_t size) +{ + unsigned int kern_addr; + + if (size != 1 && size != 2 && size != 4) + return; + + if (addr & (size-1)) + /* Unaligned */ + return; + + if (!addr) { + db_dr (num, 0, 0, 0, 0); + db_printf("Hardware watchpoint %d deleted\n", num); + } + + if (task) { + if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) + return; + addr = kern_addr; + } + addr = kvtolin(addr); + + db_dr (num, addr, I386_DB_TYPE_W, size-1, I386_DB_LOCAL|I386_DB_GLOBAL); + + db_printf("Hardware watchpoint %d set for %x\n", num, addr); +} + /* * Print trap reason. */ @@ -95,15 +230,14 @@ kdb_trap( switch (type) { case T_DEBUG: /* single_step */ { - extern int dr_addr[]; int addr; - int status = dr6(); + int status = get_dr6(); if (status & 0xf) { /* hmm hdw break */ - addr = status & 0x8 ? dr_addr[3] : - status & 0x4 ? dr_addr[2] : - status & 0x2 ? dr_addr[1] : - dr_addr[0]; + addr = status & 0x8 ? get_dr3() : + status & 0x4 ? get_dr2() : + status & 0x2 ? get_dr1() : + get_dr0(); regs->efl |= EFL_RF; db_single_step_cmd(addr, 0, 1, "p"); } diff --git a/i386/i386/db_interface.h b/i386/i386/db_interface.h index 10a02e2..a8dfdce 100644 --- a/i386/i386/db_interface.h +++ b/i386/i386/db_interface.h @@ -53,6 +53,12 @@ extern boolean_t db_phys_eq ( task_t task2, vm_offset_t addr2); +extern int db_user_to_kernel_address( + task_t task, + vm_offset_t addr, + unsigned int *kaddr, + int flag); + extern void db_task_name (task_t task); #define I386_DB_TYPE_X 0 @@ -67,9 +73,26 @@ extern void db_task_name (task_t task); #define I386_DB_LOCAL 1 #define I386_DB_GLOBAL 2 -extern unsigned long dr0 (vm_offset_t linear_addr, int type, int len, int persistence); -extern unsigned long dr1 (vm_offset_t linear_addr, int type, int len, int persistence); -extern unsigned long dr2 (vm_offset_t linear_addr, int type, int len, int persistence); -extern unsigned long dr3 (vm_offset_t linear_addr, int type, int len, int persistence); +extern void db_set_hw_watchpoint( + int num, + task_t task, + vm_offset_t addr, + vm_size_t size); + +extern void db_dr ( + int num, + vm_offset_t linear_addr, + int type, + int len, + int persistence); + +extern void db_get_debug_state( + pcb_t pcb, + struct i386_debug_state *state); +extern kern_return_t db_set_debug_state( + pcb_t pcb, + const struct i386_debug_state *state); + +extern void db_load_context(pcb_t pcb); #endif /* _I386_DB_INTERFACE_H_ */ diff --git a/i386/i386/locore.S b/i386/i386/locore.S index 379b219..21bec0a 100644 --- a/i386/i386/locore.S +++ b/i386/i386/locore.S @@ -1424,128 +1424,6 @@ _inst_fetch_fault: -ENTRY(dr6) -#ifdef MACH_XEN - pushl %ebx - movl $6, %ebx - call __hyp_get_debugreg - popl %ebx -#else /* MACH_XEN */ - movl %db6, %eax -#endif /* MACH_XEN */ - ret - -/* dr(address, type, len, persistence) - */ -ENTRY(dr0) - movl S_ARG0, %eax - movl %eax,EXT(dr_addr) -#ifdef MACH_XEN - pushl %ebx - movl $0,%ebx - movl %eax,%ecx - call __hyp_set_debugreg -#else /* MACH_XEN */ - movl %eax, %db0 -#endif /* MACH_XEN */ - movl $0, %ecx - jmp 0f -ENTRY(dr1) - movl S_ARG0, %eax - movl %eax,EXT(dr_addr)+1*4 -#ifdef MACH_XEN - pushl %ebx - movl $1,%ebx - movl %eax,%ecx - call __hyp_set_debugreg -#else /* MACH_XEN */ - movl %eax, %db1 -#endif /* MACH_XEN */ - movl $2, %ecx - jmp 0f -ENTRY(dr2) - movl S_ARG0, %eax - movl %eax,EXT(dr_addr)+2*4 -#ifdef MACH_XEN - pushl %ebx - movl $2,%ebx - movl %eax,%ecx - call __hyp_set_debugreg -#else /* MACH_XEN */ - movl %eax, %db2 -#endif /* MACH_XEN */ - movl $4, %ecx - jmp 0f - -ENTRY(dr3) - movl S_ARG0, %eax - movl %eax,EXT(dr_addr)+3*4 -#ifdef MACH_XEN - pushl %ebx - movl $3,%ebx - movl %eax,%ecx - call __hyp_set_debugreg -#else /* MACH_XEN */ - movl %eax, %db3 -#endif /* MACH_XEN */ - movl $6, %ecx - -0: - pushl %ebp - movl %esp, %ebp - -#ifdef MACH_XEN - movl $7,%ebx - call __hyp_get_debugreg - movl %eax, %edx -#else /* MACH_XEN */ - movl %db7, %edx -#endif /* MACH_XEN */ - movl %edx,EXT(dr_addr)+4*4 - andl dr_msk(,%ecx,2),%edx /* clear out new entry */ - movl %edx,EXT(dr_addr)+5*4 - movzbl B_ARG3, %eax - andb $3, %al - shll %cl, %eax - orl %eax, %edx - - movzbl B_ARG1, %eax - andb $3, %al - addb %cl, %cl - addb $0x10, %cl - shll %cl, %eax - orl %eax, %edx - - movzbl B_ARG2, %eax - andb $3, %al - addb $0x2, %cl - shll %cl, %eax - orl %eax, %edx - -#ifdef MACH_XEN - movl $7,%ebx - movl %edx, %ecx - call __hyp_set_debugreg - popl %ebx -#else /* MACH_XEN */ - movl %edx, %db7 -#endif /* MACH_XEN */ - movl %edx,EXT(dr_addr)+7*4 - movl %edx, %eax - leave - ret - - .data -dr_msk: - .long ~0x000f0003 - .long ~0x00f0000c - .long ~0x0f000030 - .long ~0xf00000c0 -ENTRY(dr_addr) - .long 0,0,0,0 - .long 0,0,0,0 - .text - /* * cpu_shutdown() * Force reboot diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c index f687db1..2b41577 100644 --- a/i386/i386/pcb.c +++ b/i386/i386/pcb.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "eflags.h" #include "gdt.h" @@ -213,6 +214,8 @@ void switch_ktss(pcb) pcb->ims.user_gdt, sizeof pcb->ims.user_gdt); #endif /* MACH_XEN */ + db_load_context(pcb); + /* * Load the floating-point context, if necessary. */ @@ -621,6 +624,21 @@ kern_return_t thread_setstatus(thread, flavor, tstate, count) break; } + case i386_DEBUG_STATE: + { + register struct i386_debug_state *state; + kern_return_t ret; + + if (count < i386_DEBUG_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_debug_state *) tstate; + ret = db_set_debug_state(thread->pcb, state); + if (ret) + return ret; + break; + } + default: return(KERN_INVALID_ARGUMENT); } @@ -760,6 +778,20 @@ kern_return_t thread_getstatus(thread, flavor, tstate, count) break; } + case i386_DEBUG_STATE: + { + register struct i386_debug_state *state; + + if (*count < i386_DEBUG_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_debug_state *) tstate; + db_get_debug_state(thread->pcb, state); + + *count = i386_DEBUG_STATE_COUNT; + break; + } + default: return(KERN_INVALID_ARGUMENT); } diff --git a/i386/i386/proc_reg.h b/i386/i386/proc_reg.h index 64d8c43..3e81d70 100644 --- a/i386/i386/proc_reg.h +++ b/i386/i386/proc_reg.h @@ -234,6 +234,132 @@ extern unsigned long cr3; asm("jmp 0f\n" \ "0:\n") +#ifdef MACH_HYP +#define get_dr0() hyp_get_debugreg(0) +#else +#define get_dr0() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("movl %%dr0, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_HYP +#define set_dr0(value) hyp_set_debugreg(0, value) +#else +#define set_dr0(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("movl %0,%%dr0" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_HYP +#define get_dr1() hyp_get_debugreg(1) +#else +#define get_dr1() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("movl %%dr1, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_HYP +#define set_dr1(value) hyp_set_debugreg(1, value) +#else +#define set_dr1(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("movl %0,%%dr1" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_HYP +#define get_dr2() hyp_get_debugreg(2) +#else +#define get_dr2() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("movl %%dr2, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_HYP +#define set_dr2(value) hyp_set_debugreg(2, value) +#else +#define set_dr2(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("movl %0,%%dr2" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_HYP +#define get_dr3() hyp_get_debugreg(3) +#else +#define get_dr3() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("movl %%dr3, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_HYP +#define set_dr3(value) hyp_set_debugreg(3, value) +#else +#define set_dr3(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("movl %0,%%dr3" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_HYP +#define get_dr6() hyp_get_debugreg(6) +#else +#define get_dr6() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("movl %%dr6, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_HYP +#define set_dr6(value) hyp_set_debugreg(6, value) +#else +#define set_dr6(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("movl %0,%%dr6" : : "r" (_temp__)); \ + }) +#endif + +#ifdef MACH_HYP +#define get_dr7() hyp_get_debugreg(7) +#else +#define get_dr7() \ + ({ \ + register unsigned long _temp__; \ + asm volatile("movl %%dr7, %0" : "=r" (_temp__)); \ + _temp__; \ + }) +#endif + +#ifdef MACH_HYP +#define set_dr7(value) hyp_set_debugreg(7, value) +#else +#define set_dr7(value) \ + ({ \ + register unsigned long _temp__ = (value); \ + asm volatile("movl %0,%%dr7" : : "r" (_temp__)); \ + }) +#endif + #endif /* __GNUC__ */ #endif /* __ASSEMBLER__ */ diff --git a/i386/i386/thread.h b/i386/i386/thread.h index f2ae8bf..658793e 100644 --- a/i386/i386/thread.h +++ b/i386/i386/thread.h @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -168,6 +169,7 @@ struct i386_machine_state { struct i386_fpsave_state *ifps; struct v86_assist_state v86s; struct real_descriptor user_gdt[USER_GDT_SLOTS]; + struct i386_debug_state ids; }; typedef struct pcb { diff --git a/i386/include/mach/i386/thread_status.h b/i386/include/mach/i386/thread_status.h index 5f20355..ba1e3de 100644 --- a/i386/include/mach/i386/thread_status.h +++ b/i386/include/mach/i386/thread_status.h @@ -56,6 +56,7 @@ #define i386_ISA_PORT_MAP_STATE 3 #define i386_V86_ASSIST_STATE 4 #define i386_REGS_SEGS_STATE 5 +#define i386_DEBUG_STATE 6 /* * This structure is used for both @@ -144,4 +145,10 @@ struct v86_interrupt_table { #define i386_V86_ASSIST_STATE_COUNT \ (sizeof(struct i386_v86_assist_state)/sizeof(unsigned int)) +struct i386_debug_state { + unsigned int dr[8]; +}; +#define i386_DEBUG_STATE_COUNT \ + (sizeof(struct i386_debug_state)/sizeof(unsigned int)) + #endif /* _MACH_I386_THREAD_STATUS_H_ */