Date: Wed, 10 Dec 2003 13:17:48 -0800 (PST) From: Peter Wemm <peter@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 43758 for review Message-ID: <200312102117.hBALHm4J038427@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=43758 Change 43758 by peter@peter_daintree on 2003/12/10 13:17:34 Checkpoint WIP addition of hardware debug register support. Notable todo: 1) make it compile 2) deal with the 16 vs 8 debug registers 3) finish the cpu switch code Affected files ... .. //depot/projects/hammer/sys/amd64/amd64/cpu_switch.S#15 edit .. //depot/projects/hammer/sys/amd64/amd64/db_trace.c#13 edit .. //depot/projects/hammer/sys/amd64/amd64/genassym.c#29 edit .. //depot/projects/hammer/sys/amd64/amd64/machdep.c#76 edit .. //depot/projects/hammer/sys/amd64/amd64/trap.c#36 edit .. //depot/projects/hammer/sys/amd64/amd64/vm_machdep.c#24 edit .. //depot/projects/hammer/sys/amd64/include/cpufunc.h#17 edit .. //depot/projects/hammer/sys/amd64/include/pcb.h#14 edit Differences ... ==== //depot/projects/hammer/sys/amd64/amd64/cpu_switch.S#15 (text+ko) ==== @@ -130,6 +130,27 @@ movl %fs,PCB_FS(%r8) movl %gs,PCB_GS(%r8) +#if 0 + /* Test if debug registers should be saved. */ + testl $PCB_DBREGS,PCB_FLAGS(%edx) + jz 1f /* no, skip over */ + movl %dr7,%eax /* yes, do the save */ + movl %eax,PCB_DR7(%edx) + andl $0x0000fc00, %eax /* disable all watchpoints */ + movl %eax,%dr7 + movl %dr6,%eax + movl %eax,PCB_DR6(%edx) + movl %dr3,%eax + movl %eax,PCB_DR3(%edx) + movl %dr2,%eax + movl %eax,PCB_DR2(%edx) + movl %dr1,%eax + movl %eax,PCB_DR1(%edx) + movl %dr0,%eax + movl %eax,PCB_DR0(%edx) +1: +#endif + /* have we used fp, and need a save? */ cmpq %rdi,PCPU(FPCURTHREAD) jne 1f @@ -223,6 +244,30 @@ movq %r8, PCPU(CURPCB) movq %rsi, PCPU(CURTHREAD) /* into next thread */ +#if 0 + /* + * Restore debug registers. The special code for dr7 is to + * preserve the current values of its reserved bits. + */ + movl PCB_DR6(%edx),%eax + movl %eax,%dr6 + movl PCB_DR3(%edx),%eax + movl %eax,%dr3 + movl PCB_DR2(%edx),%eax + movl %eax,%dr2 + movl PCB_DR1(%edx),%eax + movl %eax,%dr1 + movl PCB_DR0(%edx),%eax + movl %eax,%dr0 + movl %dr7,%eax + andl $0x0000fc00,%eax + movl PCB_DR7(%edx),%ecx + andl $~0x0000fc00,%ecx + orl %ecx,%eax + movl %eax,%dr7 +1: +#endif + ret #ifdef INVARIANTS ==== //depot/projects/hammer/sys/amd64/amd64/db_trace.c#13 (text+ko) ==== @@ -46,7 +46,6 @@ #include <ddb/db_sym.h> #include <ddb/db_variables.h> -#if 0 db_varfcn_t db_dr0; db_varfcn_t db_dr1; db_varfcn_t db_dr2; @@ -55,7 +54,6 @@ db_varfcn_t db_dr5; db_varfcn_t db_dr6; db_varfcn_t db_dr7; -#endif /* * Machine register set. @@ -87,7 +85,6 @@ { "r15", &ddb_regs.tf_r15, FCN_NULL }, { "rip", &ddb_regs.tf_rip, FCN_NULL }, { "rflags", &ddb_regs.tf_rflags, FCN_NULL }, -#if 0 { "dr0", NULL, db_dr0 }, { "dr1", NULL, db_dr1 }, { "dr2", NULL, db_dr2 }, @@ -96,7 +93,6 @@ { "dr5", NULL, db_dr5 }, { "dr6", NULL, db_dr6 }, { "dr7", NULL, db_dr7 }, -#endif }; struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); @@ -124,12 +120,10 @@ struct proc *p, struct amd64_frame *frame, db_addr_t callpc); -#if 0 static char * watchtype_str(int type); int amd64_set_watch(int watchnum, unsigned int watchaddr, int size, int access, struct dbreg * d); int amd64_clr_watch(int watchnum, struct dbreg * d); -#endif int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); @@ -517,7 +511,6 @@ db_stack_trace_cmd(ebp, 1, -1, NULL); } -#if 0 #define DB_DRX_FUNC(reg) \ int \ db_ ## reg (vp, valuep, op) \ @@ -724,26 +717,3 @@ } db_printf("\n"); } - -#else -int -db_md_set_watchpoint(addr, size) - db_expr_t addr; - db_expr_t size; -{ - return (-1); -} - -int -db_md_clr_watchpoint(addr, size) - db_expr_t addr; - db_expr_t size; -{ - return (-1); -} - -void -db_md_list_watchpoints() -{ -} -#endif ==== //depot/projects/hammer/sys/amd64/amd64/genassym.c#29 (text+ko) ==== @@ -135,6 +135,13 @@ ASSYM(PCB_ES, offsetof(struct pcb, pcb_es)); ASSYM(PCB_FS, offsetof(struct pcb, pcb_fs)); ASSYM(PCB_GS, offsetof(struct pcb, pcb_gs)); +ASSYM(PCB_DR0, offsetof(struct pcb, pcb_dr0)); +ASSYM(PCB_DR1, offsetof(struct pcb, pcb_dr1)); +ASSYM(PCB_DR2, offsetof(struct pcb, pcb_dr2)); +ASSYM(PCB_DR3, offsetof(struct pcb, pcb_dr3)); +ASSYM(PCB_DR6, offsetof(struct pcb, pcb_dr6)); +ASSYM(PCB_DR7, offsetof(struct pcb, pcb_dr7)); +ASSYM(PCB_DBREGS, PCB_DBREGS); ASSYM(PCB_FLAGS, offsetof(struct pcb, pcb_flags)); ASSYM(PCB_FULLCTX, PCB_FULLCTX); ==== //depot/projects/hammer/sys/amd64/amd64/machdep.c#76 (text+ko) ==== @@ -534,6 +534,28 @@ regs->tf_cs = _ucodesel; /* + * Reset the hardware debug registers if they were in use. + * They won't have any meaning for the newly exec'd process. + */ + if (pcb->pcb_flags & PCB_DBREGS) { + pcb->pcb_dr0 = 0; + pcb->pcb_dr1 = 0; + pcb->pcb_dr2 = 0; + pcb->pcb_dr3 = 0; + pcb->pcb_dr6 = 0; + pcb->pcb_dr7 = 0; + if (pcb == PCPU_GET(curpcb)) { + /* + * Clear the debug registers on the running + * CPU, otherwise they will end up affecting + * the next process we switch to. + */ + reset_dbregs(); + } + pcb->pcb_flags &= ~PCB_DBREGS; + } + + /* * Arrange to trap the next fpu or `fwait' instruction (see fpu.c * for why fwait must be trapped at least if there is an fpu or an * emulator). This is mainly to handle the case where npx0 is not @@ -1559,17 +1581,181 @@ int fill_dbregs(struct thread *td, struct dbreg *dbregs) { + struct pcb *pcb; + if (td == NULL) { + dbregs->dr[0] = rdr0(); + dbregs->dr[1] = rdr1(); + dbregs->dr[2] = rdr2(); + dbregs->dr[3] = rdr3(); + dbregs->dr[4] = rdr4(); + dbregs->dr[5] = rdr5(); + dbregs->dr[6] = rdr6(); + dbregs->dr[7] = rdr7(); + } else { + pcb = td->td_pcb; + dbregs->dr[0] = pcb->pcb_dr0; + dbregs->dr[1] = pcb->pcb_dr1; + dbregs->dr[2] = pcb->pcb_dr2; + dbregs->dr[3] = pcb->pcb_dr3; + dbregs->dr[4] = 0; + dbregs->dr[5] = 0; + dbregs->dr[6] = pcb->pcb_dr6; + dbregs->dr[7] = pcb->pcb_dr7; + } return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { + struct pcb *pcb; + int i; + u_int64_t mask1, mask2; + + if (td == NULL) { + load_dr0(dbregs->dr[0]); + load_dr1(dbregs->dr[1]); + load_dr2(dbregs->dr[2]); + load_dr3(dbregs->dr[3]); + load_dr4(dbregs->dr[4]); + load_dr5(dbregs->dr[5]); + load_dr6(dbregs->dr[6]); + load_dr7(dbregs->dr[7]); + } else { + /* + * Don't let an illegal value for dr7 get set. Specifically, + * check for undefined settings. Setting these bit patterns + * result in undefined behaviour and can lead to an unexpected + * TRCTRAP or a general protection fault right here. + */ + for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; + i++, mask1 <<= 2, mask2 <<= 2) + if ((dbregs->dr[7] & mask1) == mask2) + return (EINVAL); + + pcb = td->td_pcb; + + /* + * Don't let a process set a breakpoint that is not within the + * process's address space. If a process could do this, it + * could halt the system by setting a breakpoint in the kernel + * (if ddb was enabled). Thus, we need to check to make sure + * that no breakpoints are being enabled for addresses outside + * process's address space, unless, perhaps, we were called by + * uid 0. + * + * XXX - what about when the watched area of the user's + * address space is written into from within the kernel + * ... wouldn't that still cause a breakpoint to be generated + * from within kernel mode? + */ + + if (suser(td) != 0) { + if (dbregs->dr[7] & 0x3) { + /* dr0 is enabled */ + if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + if (dbregs->dr[7] & 0x3<<2) { + /* dr1 is enabled */ + if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + if (dbregs->dr[7] & 0x3<<4) { + /* dr2 is enabled */ + if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + if (dbregs->dr[7] & 0x3<<6) { + /* dr3 is enabled */ + if (dbregs->dr[3] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + } + + pcb->pcb_dr0 = dbregs->dr[0]; + pcb->pcb_dr1 = dbregs->dr[1]; + pcb->pcb_dr2 = dbregs->dr[2]; + pcb->pcb_dr3 = dbregs->dr[3]; + pcb->pcb_dr6 = dbregs->dr[6]; + pcb->pcb_dr7 = dbregs->dr[7]; + + pcb->pcb_flags |= PCB_DBREGS; + } return (0); } +/* + * Return > 0 if a hardware breakpoint has been hit, and the + * breakpoint was in user space. Return 0, otherwise. + */ +int +user_dbreg_trap(void) +{ + u_int64_t dr7, dr6; /* debug registers dr6 and dr7 */ + u_int64_t bp; /* breakpoint bits extracted from dr6 */ + int nbp; /* number of breakpoints that triggered */ + caddr_t addr[4]; /* breakpoint addresses */ + int i; + + dr7 = rdr7(); + if ((dr7 & 0x000000ff) == 0) { + /* + * all GE and LE bits in the dr7 register are zero, + * thus the trap couldn't have been caused by the + * hardware debug registers + */ + return 0; + } + + nbp = 0; + dr6 = rdr6(); + bp = dr6 & 0x0000000f; + + if (!bp) { + /* + * None of the breakpoint bits are set meaning this + * trap was not caused by any of the debug registers + */ + return 0; + } + + /* + * at least one of the breakpoints were hit, check to see + * which ones and if any of them are user space addresses + */ + + if (bp & 0x01) { + addr[nbp++] = (caddr_t)rdr0(); + } + if (bp & 0x02) { + addr[nbp++] = (caddr_t)rdr1(); + } + if (bp & 0x04) { + addr[nbp++] = (caddr_t)rdr2(); + } + if (bp & 0x08) { + addr[nbp++] = (caddr_t)rdr3(); + } + + for (i=0; i<nbp; i++) { + if (addr[i] < + (caddr_t)VM_MAXUSER_ADDRESS) { + /* + * addr[i] is in user space + */ + return nbp; + } + } + + /* + * None of the breakpoints are in user space. + */ + return 0; +} + #ifndef DDB void Debugger(const char *msg) ==== //depot/projects/hammer/sys/amd64/amd64/trap.c#36 (text+ko) ==== @@ -397,6 +397,24 @@ case T_TRCTRAP: /* trace trap */ /* + * Ignore debug register trace traps due to + * accesses in the user's address space, which + * can happen under several conditions such as + * if a user sets a watchpoint on a buffer and + * then passes that buffer to a system call. + * We still want to get TRCTRAPS for addresses + * in kernel space because that is useful when + * debugging the kernel. + */ + if (user_dbreg_trap()) { + /* + * Reset breakpoint bits because the + * processor doesn't + */ + load_dr6(rdr6() & 0xfffffff0); + goto out; + } + /* * FALLTHROUGH (TRCTRAP kernel mode, kernel address) */ case T_BPTFLT: ==== //depot/projects/hammer/sys/amd64/amd64/vm_machdep.c#24 (text+ko) ==== @@ -162,6 +162,7 @@ pcb2->pcb_rip = (register_t)fork_trampoline; pcb2->pcb_rflags = td2->td_frame->tf_rflags & ~PSL_I; /* ints disabled */ /*- + * pcb2->pcb_dr*: cloned above. * pcb2->pcb_savefpu: cloned above. * pcb2->pcb_flags: cloned above. * pcb2->pcb_onfault: cloned above (always NULL here?). @@ -202,9 +203,12 @@ void cpu_exit(struct thread *td) { - struct mdproc *mdp; - mdp = &td->td_proc->p_md; + if (pcb->pcb_flags & PCB_DBREGS) { + /* disable all hardware breakpoints */ + reset_dbregs(); + pcb->pcb_flags &= ~PCB_DBREGS; + } } void @@ -213,6 +217,11 @@ if (td == PCPU_GET(fpcurthread)) fpudrop(); + if (pcb->pcb_flags & PCB_DBREGS) { + /* disable all hardware breakpoints */ + reset_dbregs(); + pcb->pcb_flags &= ~PCB_DBREGS; + } } void @@ -296,6 +305,7 @@ pcb2->pcb_rflags = PSL_KERNEL; /* ints disabled */ /* * If we didn't copy the pcb, we'd need to do the following registers: + * pcb2->pcb_dr*: cloned above. * pcb2->pcb_savefpu: cloned above. * pcb2->pcb_rflags: cloned above. * pcb2->pcb_onfault: cloned above (always NULL here?). ==== //depot/projects/hammer/sys/amd64/include/cpufunc.h#17 (text+ko) ==== @@ -584,6 +584,118 @@ __asm __volatile("ltr %0" : : "r" (sel)); } +static __inline u_int64_t +rdr0(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr0,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr0(u_int64_t dr0) +{ + __asm __volatile("movq %0,%%dr0" : : "r" (dr0)); +} + +static __inline u_int64_t +rdr1(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr1,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr1(u_int64_t dr1) +{ + __asm __volatile("movq %0,%%dr1" : : "r" (dr1)); +} + +static __inline u_int64_t +rdr2(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr2,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr2(u_int64_t dr2) +{ + __asm __volatile("movq %0,%%dr2" : : "r" (dr2)); +} + +static __inline u_int64_t +rdr3(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr3,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr3(u_int64_t dr3) +{ + __asm __volatile("movq %0,%%dr3" : : "r" (dr3)); +} + +static __inline u_int64_t +rdr4(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr4,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr4(u_int64_t dr4) +{ + __asm __volatile("movq %0,%%dr4" : : "r" (dr4)); +} + +static __inline u_int64_t +rdr5(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr5,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr5(u_int64_t dr5) +{ + __asm __volatile("movq %0,%%dr5" : : "r" (dr5)); +} + +static __inline u_int64_t +rdr6(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr6,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr6(u_int64_t dr6) +{ + __asm __volatile("movq %0,%%dr6" : : "r" (dr6)); +} + +static __inline u_int64_t +rdr7(void) +{ + u_int64_t data; + __asm __volatile("movq %%dr7,%0" : "=r" (data)); + return (data); +} + +static __inline void +load_dr7(u_int64_t dr7) +{ + __asm __volatile("movq %0,%%dr7" : : "r" (dr7)); +} + static __inline register_t intr_disable(void) { @@ -650,13 +762,28 @@ void wbinvd(void); void write_rflags(u_int rf); void wrmsr(u_int msr, u_int64_t newval); -void load_dr7(u_int dr7); +u_int64_t rdr0(void); +void load_dr0(u_int64_t dr0); +u_int64_t rdr1(void); +void load_dr1(u_int64_t dr1); +u_int64_t rdr2(void); +void load_dr2(u_int64_t dr2); +u_int64_t rdr3(void); +void load_dr3(u_int64_t dr3); +u_int64_t rdr4(void); +void load_dr4(u_int64_t dr4); +u_int64_t rdr5(void); +void load_dr5(u_int64_t dr5); +u_int64_t rdr6(void); +void load_dr6(u_int64_t dr6); +u_int64_t rdr7(void); +void load_dr7(u_int64_t dr7); register_t intr_disable(void); void intr_restore(register_t rf); #endif /* __GNUC__ */ -void reset_dbregs(void); +void reset_dbregs(void); __END_DECLS ==== //depot/projects/hammer/sys/amd64/include/pcb.h#14 (text+ko) ==== @@ -64,11 +64,18 @@ u_int32_t pcb_es; u_int32_t pcb_fs; u_int32_t pcb_gs; + u_int64_t pcb_dr0; + u_int64_t pcb_dr1; + u_int64_t pcb_dr2; + u_int64_t pcb_dr3; + u_int64_t pcb_dr6; + u_int64_t pcb_dr7; struct savefpu pcb_save; u_long pcb_flags; -#define PCB_FPUINITDONE 0x01 /* fpu state is initialized */ -#define PCB_FULLCTX 0x02 /* full context restore on sysret */ +#define PCB_DBREGS 0x02 /* process using debug registers */ +#define PCB_FPUINITDONE 0x08 /* fpu state is initialized */ +#define PCB_FULLCTX 0x80 /* full context restore on sysret */ caddr_t pcb_onfault; /* copyin/out fault recovery */ };
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200312102117.hBALHm4J038427>