aboutsummaryrefslogtreecommitdiff
path: root/sys/amd64/amd64/db_trace.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2013-05-21 11:24:32 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2013-05-21 11:24:32 +0000
commit4f493a25f48e7e045b6d1a852f089f2a7268738e (patch)
treed5ea9e63d608b4413df87835f72df81b024daad1 /sys/amd64/amd64/db_trace.c
parentea38ca6ea51f608b9f55d8be8a858dbc8b9bfa35 (diff)
downloadsrc-4f493a25f48e7e045b6d1a852f089f2a7268738e.tar.gz
src-4f493a25f48e7e045b6d1a852f089f2a7268738e.zip
Fix the hardware watchpoints on SMP amd64. Load the updated %dr
registers also on other CPUs, besides the CPU which happens to execute the ddb. The debugging registers are stored in the pcpu area, together with the command which is executed by the IPI stop handler upon resume. Reviewed by: jhb Sponsored by: The FreeBSD Foundation MFC after: 1 week
Notes
Notes: svn path=/head/; revision=250851
Diffstat (limited to 'sys/amd64/amd64/db_trace.c')
-rw-r--r--sys/amd64/amd64/db_trace.c79
1 files changed, 57 insertions, 22 deletions
diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c
index 2c81f8765290..39297d9b2ed0 100644
--- a/sys/amd64/amd64/db_trace.c
+++ b/sys/amd64/amd64/db_trace.c
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/kdb.h>
#include <sys/proc.h>
+#include <sys/smp.h>
#include <sys/stack.h>
#include <sys/sysent.h>
@@ -63,6 +64,8 @@ static db_varfcn_t db_frame;
static db_varfcn_t db_rsp;
static db_varfcn_t db_ss;
+CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg));
+
/*
* Machine register set.
*/
@@ -591,64 +594,82 @@ db_md_set_watchpoint(addr, size)
db_expr_t addr;
db_expr_t size;
{
- struct dbreg d;
- int avail, i, wsize;
+ struct dbreg *d;
+ struct pcpu *pc;
+ int avail, c, cpu, i, wsize;
- fill_dbregs(NULL, &d);
+ d = (struct dbreg *)PCPU_PTR(dbreg);
+ cpu = PCPU_GET(cpuid);
+ fill_dbregs(NULL, d);
avail = 0;
- for(i = 0; i < 4; i++) {
- if (!DBREG_DR7_ENABLED(d.dr[7], i))
+ for (i = 0; i < 4; i++) {
+ if (!DBREG_DR7_ENABLED(d->dr[7], i))
avail++;
}
if (avail * 8 < size)
return (-1);
- for (i = 0; i < 4 && (size > 0); i++) {
- if (!DBREG_DR7_ENABLED(d.dr[7], i)) {
+ for (i = 0; i < 4 && size > 0; i++) {
+ if (!DBREG_DR7_ENABLED(d->dr[7], i)) {
if (size >= 8 || (avail == 1 && size > 4))
wsize = 8;
else if (size > 2)
wsize = 4;
else
wsize = size;
- amd64_set_watch(i, addr, wsize,
- DBREG_DR7_WRONLY, &d);
+ amd64_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, d);
addr += wsize;
size -= wsize;
avail--;
}
}
- set_dbregs(NULL, &d);
+ set_dbregs(NULL, d);
+ CPU_FOREACH(c) {
+ if (c == cpu)
+ continue;
+ pc = pcpu_find(c);
+ memcpy(pc->pc_dbreg, d, sizeof(*d));
+ pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
+ }
- return(0);
+ return (0);
}
-
int
db_md_clr_watchpoint(addr, size)
db_expr_t addr;
db_expr_t size;
{
- struct dbreg d;
- int i;
+ struct dbreg *d;
+ struct pcpu *pc;
+ int i, c, cpu;
- fill_dbregs(NULL, &d);
+ d = (struct dbreg *)PCPU_PTR(dbreg);
+ cpu = PCPU_GET(cpuid);
+ fill_dbregs(NULL, d);
- for(i = 0; i < 4; i++) {
- if (DBREG_DR7_ENABLED(d.dr[7], i)) {
- if ((DBREG_DRX((&d), i) >= addr) &&
- (DBREG_DRX((&d), i) < addr+size))
- amd64_clr_watch(i, &d);
+ for (i = 0; i < 4; i++) {
+ if (DBREG_DR7_ENABLED(d->dr[7], i)) {
+ if (DBREG_DRX((d), i) >= addr &&
+ DBREG_DRX((d), i) < addr + size)
+ amd64_clr_watch(i, d);
}
}
- set_dbregs(NULL, &d);
+ set_dbregs(NULL, d);
+ CPU_FOREACH(c) {
+ if (c == cpu)
+ continue;
+ pc = pcpu_find(c);
+ memcpy(pc->pc_dbreg, d, sizeof(*d));
+ pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
+ }
- return(0);
+ return (0);
}
@@ -699,3 +720,17 @@ db_md_list_watchpoints()
}
db_printf("\n");
}
+
+void
+amd64_db_resume_dbreg(void)
+{
+ struct dbreg *d;
+
+ switch (PCPU_GET(dbreg_cmd)) {
+ case PC_DBREG_CMD_LOAD:
+ d = (struct dbreg *)PCPU_PTR(dbreg);
+ set_dbregs(NULL, d);
+ PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE);
+ break;
+ }
+}