PERFORCE change 43758 for review
Peter Wemm
peter at FreeBSD.org
Wed Dec 10 13:17:57 PST 2003
http://perforce.freebsd.org/chv.cgi?CH=43758
Change 43758 by peter at 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 */
};
More information about the p4-projects
mailing list