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
This commit is contained in:
parent
5ee265a48b
commit
b2b2f0b782
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_cpu.h"
|
||||
#include "opt_ddb.h"
|
||||
#include "opt_kstack_pages.h"
|
||||
#include "opt_sched.h"
|
||||
#include "opt_smp.h"
|
||||
@ -1396,6 +1397,10 @@ cpustop_handler(void)
|
||||
CPU_CLR_ATOMIC(cpu, &started_cpus);
|
||||
CPU_CLR_ATOMIC(cpu, &stopped_cpus);
|
||||
|
||||
#ifdef DDB
|
||||
amd64_db_resume_dbreg();
|
||||
#endif
|
||||
|
||||
if (cpu == 0 && cpustop_restartfunc != NULL) {
|
||||
cpustop_restartfunc();
|
||||
cpustop_restartfunc = NULL;
|
||||
|
@ -117,5 +117,6 @@ void minidumpsys(struct dumperinfo *);
|
||||
struct savefpu *get_pcb_user_save_td(struct thread *td);
|
||||
struct savefpu *get_pcb_user_save_pcb(struct pcb *pcb);
|
||||
struct pcb *get_pcb_td(struct thread *td);
|
||||
void amd64_db_resume_dbreg(void);
|
||||
|
||||
#endif /* !_MACHINE_MD_VAR_H_ */
|
||||
|
@ -78,9 +78,14 @@
|
||||
struct system_segment_descriptor *pc_tss; \
|
||||
u_int pc_cmci_mask /* MCx banks for CMCI */ \
|
||||
PCPU_XEN_FIELDS; \
|
||||
char __pad[293] /* be divisor of PAGE_SIZE \
|
||||
uint64_t pc_dbreg[16]; /* ddb debugging regs */ \
|
||||
int pc_dbreg_cmd; /* ddb debugging reg cmd */ \
|
||||
char __pad[161] /* be divisor of PAGE_SIZE \
|
||||
after cache alignment */
|
||||
|
||||
#define PC_DBREG_CMD_NONE 0
|
||||
#define PC_DBREG_CMD_LOAD 1
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#ifdef lint
|
||||
|
Loading…
Reference in New Issue
Block a user