kernel: add support for hardware watchpoints
This commit is contained in:
parent
1cfa405d0a
commit
773be02aa1
24 changed files with 465 additions and 5 deletions
|
@ -584,3 +584,10 @@ cmpswap(long *addr, long old, long new)
|
||||||
{
|
{
|
||||||
return cas32(addr, old, new);
|
return cas32(addr, old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -638,3 +638,10 @@ cmpswap(long *addr, long old, long new)
|
||||||
{
|
{
|
||||||
return cas32(addr, old, new);
|
return cas32(addr, old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -436,3 +436,10 @@ cistrncmp(char *a, char *b, int n)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -654,3 +654,10 @@ cmpswap(long *addr, long old, long new)
|
||||||
{
|
{
|
||||||
return cas32(addr, old, new);
|
return cas32(addr, old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -159,6 +159,8 @@ struct PMMU
|
||||||
Segdesc gdt[NPROCSEG]; /* per process descriptors */
|
Segdesc gdt[NPROCSEG]; /* per process descriptors */
|
||||||
Segdesc *ldt; /* local descriptor table */
|
Segdesc *ldt; /* local descriptor table */
|
||||||
int nldt; /* number of ldt descriptors allocated */
|
int nldt; /* number of ldt descriptors allocated */
|
||||||
|
|
||||||
|
u32int dr[8]; /* debug registers */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -252,6 +254,7 @@ struct Mach
|
||||||
char* cpuidtype;
|
char* cpuidtype;
|
||||||
int havetsc;
|
int havetsc;
|
||||||
int havepge;
|
int havepge;
|
||||||
|
int havewatchpt8;
|
||||||
uvlong tscticks;
|
uvlong tscticks;
|
||||||
int pdballoc;
|
int pdballoc;
|
||||||
int pdbfree;
|
int pdbfree;
|
||||||
|
|
|
@ -49,6 +49,9 @@ enum { /* cpuid standard function codes */
|
||||||
Procsig,
|
Procsig,
|
||||||
Proctlbcache,
|
Proctlbcache,
|
||||||
Procserial,
|
Procserial,
|
||||||
|
|
||||||
|
Highextfunc = 0x80000000,
|
||||||
|
Procextfeat,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef long Rdwrfn(Chan*, void*, long, vlong);
|
typedef long Rdwrfn(Chan*, void*, long, vlong);
|
||||||
|
@ -874,6 +877,23 @@ cpuidentify(void)
|
||||||
hwrandbuf = rdrandbuf;
|
hwrandbuf = rdrandbuf;
|
||||||
else
|
else
|
||||||
hwrandbuf = nil;
|
hwrandbuf = nil;
|
||||||
|
|
||||||
|
/* 8-byte watchpoints are supported in Long Mode */
|
||||||
|
if(sizeof(uintptr) == 8)
|
||||||
|
m->havewatchpt8 = 1;
|
||||||
|
else if(strcmp(m->cpuidid, "GenuineIntel") == 0){
|
||||||
|
/* some random CPUs that support 8-byte watchpoints */
|
||||||
|
if(family == 15 && (model == 3 || model == 4 || model == 6)
|
||||||
|
|| family == 6 && (model == 15 || model == 23 || model == 28))
|
||||||
|
m->havewatchpt8 = 1;
|
||||||
|
/* Intel SDM claims amd64 support implies 8-byte watchpoint support */
|
||||||
|
cpuid(Highextfunc, regs);
|
||||||
|
if(regs[0] >= Procextfeat){
|
||||||
|
cpuid(Procextfeat, regs);
|
||||||
|
if((regs[3] & 1<<29) != 0)
|
||||||
|
m->havewatchpt8 = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cputype = t;
|
cputype = t;
|
||||||
return t->family;
|
return t->family;
|
||||||
|
@ -1229,3 +1249,61 @@ dumpmcregs(void)
|
||||||
iprint("\n");
|
iprint("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *pr, Watchpt *wp, int nwp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u8int cfg;
|
||||||
|
Watchpt *p;
|
||||||
|
|
||||||
|
if(nwp > 4)
|
||||||
|
error("there are four watchpoints.");
|
||||||
|
if(nwp == 0){
|
||||||
|
memset(pr->dr, 0, sizeof(pr->dr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(p = wp; p < wp + nwp; p++){
|
||||||
|
switch(p->type){
|
||||||
|
case WATCHRD|WATCHWR: case WATCHWR:
|
||||||
|
break;
|
||||||
|
case WATCHEX:
|
||||||
|
if(p->len != 1)
|
||||||
|
error("length must be 1 on breakpoints");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("type must be rw-, -w- or --x");
|
||||||
|
}
|
||||||
|
switch(p->len){
|
||||||
|
case 1: case 2: case 4:
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if(m->havewatchpt8) break;
|
||||||
|
default:
|
||||||
|
error(m->havewatchpt8 ? "length must be 1,2,4,8" : "length must be 1,2,4");
|
||||||
|
}
|
||||||
|
if((p->addr & p->len - 1) != 0)
|
||||||
|
error("address must be aligned according to length");
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pr->dr, 0, sizeof(pr->dr));
|
||||||
|
pr->dr[6] = 0xffff8ff0;
|
||||||
|
for(i = 0; i < nwp; i++){
|
||||||
|
pr->dr[i] = wp[i].addr;
|
||||||
|
switch(wp[i].type){
|
||||||
|
case WATCHRD|WATCHWR: cfg = 3; break;
|
||||||
|
case WATCHWR: cfg = 1; break;
|
||||||
|
case WATCHEX: cfg = 0; break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
switch(wp[i].len){
|
||||||
|
case 1: break;
|
||||||
|
case 2: cfg |= 4; break;
|
||||||
|
case 4: cfg |= 12; break;
|
||||||
|
case 8: cfg |= 8; break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
pr->dr[7] |= cfg << 16 + 4 * i;
|
||||||
|
pr->dr[7] |= 1 << 2 * i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ ulong getcr0(void);
|
||||||
ulong getcr2(void);
|
ulong getcr2(void);
|
||||||
ulong getcr3(void);
|
ulong getcr3(void);
|
||||||
ulong getcr4(void);
|
ulong getcr4(void);
|
||||||
|
u32int getdr6(void);
|
||||||
char* getconf(char*);
|
char* getconf(char*);
|
||||||
void guesscpuhz(int);
|
void guesscpuhz(int);
|
||||||
void halt(void);
|
void halt(void);
|
||||||
|
@ -165,6 +166,9 @@ void procfork(Proc*);
|
||||||
void putcr0(ulong);
|
void putcr0(ulong);
|
||||||
void putcr3(ulong);
|
void putcr3(ulong);
|
||||||
void putcr4(ulong);
|
void putcr4(ulong);
|
||||||
|
void putdr(u32int*);
|
||||||
|
void putdr6(u32int);
|
||||||
|
void putdr7(u32int);
|
||||||
void* rampage(void);
|
void* rampage(void);
|
||||||
int rdmsr(int, vlong*);
|
int rdmsr(int, vlong*);
|
||||||
void realmode(Ureg*);
|
void realmode(Ureg*);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#define X86FAMILY(x) ((((x)>>8) & 0x0F) | (((x)>>20) & 0xFF)<<4)
|
#define X86FAMILY(x) ((((x)>>8) & 0x0F) | (((x)>>20) & 0xFF)<<4)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
VectorDE = 1, /* debug exception */
|
||||||
VectorNMI = 2, /* non-maskable interrupt */
|
VectorNMI = 2, /* non-maskable interrupt */
|
||||||
VectorBPT = 3, /* breakpoint */
|
VectorBPT = 3, /* breakpoint */
|
||||||
VectorUD = 6, /* invalid opcode exception */
|
VectorUD = 6, /* invalid opcode exception */
|
||||||
|
|
|
@ -845,6 +845,38 @@ _rndbytes:
|
||||||
_rnddone:
|
_rnddone:
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
/* debug register access */
|
||||||
|
|
||||||
|
TEXT putdr(SB), $0
|
||||||
|
MOVL p+0(FP), SI
|
||||||
|
MOVL 28(SI), AX
|
||||||
|
MOVL AX, DR7
|
||||||
|
MOVL 0(SI), AX
|
||||||
|
MOVL AX, DR0
|
||||||
|
MOVL 4(SI), AX
|
||||||
|
MOVL AX, DR1
|
||||||
|
MOVL 8(SI), AX
|
||||||
|
MOVL AX, DR2
|
||||||
|
MOVL 12(SI), AX
|
||||||
|
MOVL AX, DR3
|
||||||
|
MOVL 24(SI), AX
|
||||||
|
MOVL AX, DR6
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT getdr6(SB), $0
|
||||||
|
MOVL DR6, AX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT putdr6(SB), $0
|
||||||
|
MOVL p+0(FP), AX
|
||||||
|
MOVL AX, DR6
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT putdr7(SB), $0
|
||||||
|
MOVL p+0(FP), AX
|
||||||
|
MOVL AX, DR7
|
||||||
|
RET
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to get to the first process:
|
* Used to get to the first process:
|
||||||
* set up an interrupt return frame and IRET to user level.
|
* set up an interrupt return frame and IRET to user level.
|
||||||
|
|
|
@ -801,6 +801,8 @@ procsetup(Proc *p)
|
||||||
memset(p->gdt, 0, sizeof(p->gdt));
|
memset(p->gdt, 0, sizeof(p->gdt));
|
||||||
p->ldt = nil;
|
p->ldt = nil;
|
||||||
p->nldt = 0;
|
p->nldt = 0;
|
||||||
|
|
||||||
|
memset(p->dr, 0, sizeof(p->dr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -831,6 +833,9 @@ procfork(Proc *p)
|
||||||
p->fpsave = up->fpsave;
|
p->fpsave = up->fpsave;
|
||||||
p->fpstate = FPinactive;
|
p->fpstate = FPinactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clear debug registers */
|
||||||
|
memset(p->dr, 0, sizeof(p->dr));
|
||||||
splx(s);
|
splx(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,6 +843,9 @@ void
|
||||||
procrestore(Proc *p)
|
procrestore(Proc *p)
|
||||||
{
|
{
|
||||||
uvlong t;
|
uvlong t;
|
||||||
|
|
||||||
|
if(p->dr[7] != 0)
|
||||||
|
putdr(p->dr);
|
||||||
|
|
||||||
if(p->kp)
|
if(p->kp)
|
||||||
return;
|
return;
|
||||||
|
@ -854,6 +862,9 @@ void
|
||||||
procsave(Proc *p)
|
procsave(Proc *p)
|
||||||
{
|
{
|
||||||
uvlong t;
|
uvlong t;
|
||||||
|
|
||||||
|
if(p->dr[7] != 0)
|
||||||
|
putdr7(0);
|
||||||
|
|
||||||
cycles(&t);
|
cycles(&t);
|
||||||
p->kentry -= t;
|
p->kentry -= t;
|
||||||
|
|
|
@ -13,6 +13,7 @@ static int trapinited;
|
||||||
|
|
||||||
void noted(Ureg*, ulong);
|
void noted(Ureg*, ulong);
|
||||||
|
|
||||||
|
static void debugexc(Ureg*, void*);
|
||||||
static void debugbpt(Ureg*, void*);
|
static void debugbpt(Ureg*, void*);
|
||||||
static void fault386(Ureg*, void*);
|
static void fault386(Ureg*, void*);
|
||||||
static void doublefault(Ureg*, void*);
|
static void doublefault(Ureg*, void*);
|
||||||
|
@ -222,6 +223,7 @@ trapinit(void)
|
||||||
* Special traps.
|
* Special traps.
|
||||||
* Syscall() is called directly without going through trap().
|
* Syscall() is called directly without going through trap().
|
||||||
*/
|
*/
|
||||||
|
trapenable(VectorDE, debugexc, 0, "debugexc");
|
||||||
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
||||||
trapenable(VectorPF, fault386, 0, "fault386");
|
trapenable(VectorPF, fault386, 0, "fault386");
|
||||||
trapenable(Vector2F, doublefault, 0, "doublefault");
|
trapenable(Vector2F, doublefault, 0, "doublefault");
|
||||||
|
@ -626,6 +628,35 @@ dumpstack(void)
|
||||||
callwithureg(_dumpstack);
|
callwithureg(_dumpstack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
debugexc(Ureg *, void *)
|
||||||
|
{
|
||||||
|
u32int dr6, m;
|
||||||
|
char buf[ERRMAX];
|
||||||
|
char *p, *e;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dr6 = getdr6();
|
||||||
|
if(up == nil)
|
||||||
|
panic("kernel debug exception dr6=%#.8ux", dr6);
|
||||||
|
putdr6(up->dr[6]);
|
||||||
|
m = up->dr[7];
|
||||||
|
m = (m >> 4 | m >> 3) & 8 | (m >> 3 | m >> 2) & 4 | (m >> 2 | m >> 1) & 2 | (m >> 1 | m) & 1;
|
||||||
|
m &= dr6;
|
||||||
|
if(m == 0){
|
||||||
|
sprint(buf, "sys: debug exception dr6=%#.8ux", dr6);
|
||||||
|
postnote(up, 1, buf, NDebug);
|
||||||
|
}else{
|
||||||
|
p = buf;
|
||||||
|
e = buf + sizeof(buf);
|
||||||
|
p = seprint(p, e, "sys: watchpoint ");
|
||||||
|
for(i = 0; i < 4; i++)
|
||||||
|
if((m & 1<<i) != 0)
|
||||||
|
p = seprint(p, e, "%d%s", i, (m >> i + 1 != 0) ? "," : "");
|
||||||
|
postnote(up, 1, buf, NDebug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
debugbpt(Ureg* ureg, void*)
|
debugbpt(Ureg* ureg, void*)
|
||||||
{
|
{
|
||||||
|
|
|
@ -144,6 +144,8 @@ struct PMMU
|
||||||
ulong kmapcount;
|
ulong kmapcount;
|
||||||
ulong kmapindex;
|
ulong kmapindex;
|
||||||
ulong mmucount;
|
ulong mmucount;
|
||||||
|
|
||||||
|
u64int dr[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -219,6 +221,7 @@ struct Mach
|
||||||
char* cpuidtype;
|
char* cpuidtype;
|
||||||
int havetsc;
|
int havetsc;
|
||||||
int havepge;
|
int havepge;
|
||||||
|
int havewatchpt8;
|
||||||
uvlong tscticks;
|
uvlong tscticks;
|
||||||
|
|
||||||
uintptr stack[1];
|
uintptr stack[1];
|
||||||
|
|
|
@ -44,6 +44,7 @@ u64int getcr0(void);
|
||||||
u64int getcr2(void);
|
u64int getcr2(void);
|
||||||
u64int getcr3(void);
|
u64int getcr3(void);
|
||||||
u64int getcr4(void);
|
u64int getcr4(void);
|
||||||
|
u64int getdr6(void);
|
||||||
char* getconf(char*);
|
char* getconf(char*);
|
||||||
void guesscpuhz(int);
|
void guesscpuhz(int);
|
||||||
void halt(void);
|
void halt(void);
|
||||||
|
@ -158,6 +159,9 @@ void procfork(Proc*);
|
||||||
void putcr0(u64int);
|
void putcr0(u64int);
|
||||||
void putcr3(u64int);
|
void putcr3(u64int);
|
||||||
void putcr4(u64int);
|
void putcr4(u64int);
|
||||||
|
void putdr(u64int*);
|
||||||
|
void putdr6(u64int);
|
||||||
|
void putdr7(u64int);
|
||||||
void* rampage(void);
|
void* rampage(void);
|
||||||
int rdmsr(int, vlong*);
|
int rdmsr(int, vlong*);
|
||||||
void realmode(Ureg*);
|
void realmode(Ureg*);
|
||||||
|
|
|
@ -692,6 +692,35 @@ ones:
|
||||||
f3:
|
f3:
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
/* debug register access */
|
||||||
|
|
||||||
|
TEXT putdr(SB), $0
|
||||||
|
MOVQ 56(BP), AX
|
||||||
|
MOVQ AX, DR7
|
||||||
|
MOVQ 0(BP), AX
|
||||||
|
MOVQ AX, DR0
|
||||||
|
MOVQ 8(BP), AX
|
||||||
|
MOVQ AX, DR1
|
||||||
|
MOVQ 16(BP), AX
|
||||||
|
MOVQ AX, DR2
|
||||||
|
MOVQ 24(BP), AX
|
||||||
|
MOVQ AX, DR3
|
||||||
|
MOVQ 48(BP), AX
|
||||||
|
MOVQ AX, DR6
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT getdr6(SB), $0
|
||||||
|
MOVQ DR6, AX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT putdr6(SB), $0
|
||||||
|
MOVQ BP, DR6
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT putdr7(SB), $0
|
||||||
|
MOVQ BP, DR7
|
||||||
|
RET
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*/
|
*/
|
||||||
TEXT touser(SB), 1, $-4
|
TEXT touser(SB), 1, $-4
|
||||||
|
|
|
@ -797,6 +797,9 @@ void
|
||||||
procrestore(Proc *p)
|
procrestore(Proc *p)
|
||||||
{
|
{
|
||||||
uvlong t;
|
uvlong t;
|
||||||
|
|
||||||
|
if(p->dr[7] != 0)
|
||||||
|
putdr(p->dr);
|
||||||
|
|
||||||
if(p->kp)
|
if(p->kp)
|
||||||
return;
|
return;
|
||||||
|
@ -810,6 +813,9 @@ void
|
||||||
procsave(Proc *p)
|
procsave(Proc *p)
|
||||||
{
|
{
|
||||||
uvlong t;
|
uvlong t;
|
||||||
|
|
||||||
|
if(p->dr[7] != 0)
|
||||||
|
putdr7(0);
|
||||||
|
|
||||||
cycles(&t);
|
cycles(&t);
|
||||||
p->kentry -= t;
|
p->kentry -= t;
|
||||||
|
|
|
@ -13,6 +13,7 @@ static int trapinited;
|
||||||
|
|
||||||
void noted(Ureg*, ulong);
|
void noted(Ureg*, ulong);
|
||||||
|
|
||||||
|
static void debugexc(Ureg*, void*);
|
||||||
static void debugbpt(Ureg*, void*);
|
static void debugbpt(Ureg*, void*);
|
||||||
static void faultamd64(Ureg*, void*);
|
static void faultamd64(Ureg*, void*);
|
||||||
static void doublefault(Ureg*, void*);
|
static void doublefault(Ureg*, void*);
|
||||||
|
@ -224,6 +225,7 @@ trapinit(void)
|
||||||
* Special traps.
|
* Special traps.
|
||||||
* Syscall() is called directly without going through trap().
|
* Syscall() is called directly without going through trap().
|
||||||
*/
|
*/
|
||||||
|
trapenable(VectorDE, debugexc, 0, "debugexc");
|
||||||
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
||||||
trapenable(VectorPF, faultamd64, 0, "faultamd64");
|
trapenable(VectorPF, faultamd64, 0, "faultamd64");
|
||||||
trapenable(Vector2F, doublefault, 0, "doublefault");
|
trapenable(Vector2F, doublefault, 0, "doublefault");
|
||||||
|
@ -587,6 +589,35 @@ dumpstack(void)
|
||||||
callwithureg(_dumpstack);
|
callwithureg(_dumpstack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
debugexc(Ureg *, void *)
|
||||||
|
{
|
||||||
|
u64int dr6, m;
|
||||||
|
char buf[ERRMAX];
|
||||||
|
char *p, *e;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dr6 = getdr6();
|
||||||
|
if(up == nil)
|
||||||
|
panic("kernel debug exception dr6=%#.8ullx", dr6);
|
||||||
|
putdr6(up->dr[6]);
|
||||||
|
m = up->dr[7];
|
||||||
|
m = (m >> 4 | m >> 3) & 8 | (m >> 3 | m >> 2) & 4 | (m >> 2 | m >> 1) & 2 | (m >> 1 | m) & 1;
|
||||||
|
m &= dr6;
|
||||||
|
if(m == 0){
|
||||||
|
sprint(buf, "sys: debug exception dr6=%#.8ullx", dr6);
|
||||||
|
postnote(up, 1, buf, NDebug);
|
||||||
|
}else{
|
||||||
|
p = buf;
|
||||||
|
e = buf + sizeof(buf);
|
||||||
|
p = seprint(p, e, "sys: watchpoint ");
|
||||||
|
for(i = 0; i < 4; i++)
|
||||||
|
if((m & 1<<i) != 0)
|
||||||
|
p = seprint(p, e, "%d%s", i, (m >> i + 1 != 0) ? "," : "");
|
||||||
|
postnote(up, 1, buf, NDebug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
debugbpt(Ureg* ureg, void*)
|
debugbpt(Ureg* ureg, void*)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,7 @@ enum
|
||||||
Qwait,
|
Qwait,
|
||||||
Qprofile,
|
Qprofile,
|
||||||
Qsyscall,
|
Qsyscall,
|
||||||
|
Qwatchpt,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -101,6 +102,7 @@ Dirtab procdir[] =
|
||||||
"wait", {Qwait}, 0, 0400,
|
"wait", {Qwait}, 0, 0400,
|
||||||
"profile", {Qprofile}, 0, 0400,
|
"profile", {Qprofile}, 0, 0400,
|
||||||
"syscall", {Qsyscall}, 0, 0400,
|
"syscall", {Qsyscall}, 0, 0400,
|
||||||
|
"watchpt", {Qwatchpt}, 0, 0600,
|
||||||
};
|
};
|
||||||
|
|
||||||
static
|
static
|
||||||
|
@ -181,6 +183,8 @@ profclock(Ureg *ur, Timer *)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int lenwatchpt(Proc *);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
|
procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
|
||||||
{
|
{
|
||||||
|
@ -265,6 +269,11 @@ procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
switch(QID(tab->qid)){
|
||||||
|
case Qwatchpt:
|
||||||
|
len = lenwatchpt(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
|
mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
|
||||||
devdir(c, qid, tab->name, len, p->user, perm, dp);
|
devdir(c, qid, tab->name, len, p->user, perm, dp);
|
||||||
|
@ -334,19 +343,22 @@ nonone(Proc *p)
|
||||||
error(Eperm);
|
error(Eperm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clearwatchpt(Proc *p);
|
||||||
|
|
||||||
static Chan*
|
static Chan*
|
||||||
procopen(Chan *c, int omode)
|
procopen(Chan *c, int omode0)
|
||||||
{
|
{
|
||||||
Proc *p;
|
Proc *p;
|
||||||
Pgrp *pg;
|
Pgrp *pg;
|
||||||
Chan *tc;
|
Chan *tc;
|
||||||
int pid;
|
int pid;
|
||||||
|
int omode;
|
||||||
|
|
||||||
if(c->qid.type & QTDIR)
|
if(c->qid.type & QTDIR)
|
||||||
return devopen(c, omode, 0, 0, procgen);
|
return devopen(c, omode0, 0, 0, procgen);
|
||||||
|
|
||||||
if(QID(c->qid) == Qtrace){
|
if(QID(c->qid) == Qtrace){
|
||||||
if (omode != OREAD)
|
if (omode0 != OREAD)
|
||||||
error(Eperm);
|
error(Eperm);
|
||||||
lock(&tlock);
|
lock(&tlock);
|
||||||
if (waserror()){
|
if (waserror()){
|
||||||
|
@ -366,7 +378,7 @@ procopen(Chan *c, int omode)
|
||||||
unlock(&tlock);
|
unlock(&tlock);
|
||||||
poperror();
|
poperror();
|
||||||
|
|
||||||
c->mode = openmode(omode);
|
c->mode = openmode(omode0);
|
||||||
c->flag |= COPEN;
|
c->flag |= COPEN;
|
||||||
c->offset = 0;
|
c->offset = 0;
|
||||||
return c;
|
return c;
|
||||||
|
@ -382,7 +394,7 @@ procopen(Chan *c, int omode)
|
||||||
if(p->pid != pid)
|
if(p->pid != pid)
|
||||||
error(Eprocdied);
|
error(Eprocdied);
|
||||||
|
|
||||||
omode = openmode(omode);
|
omode = openmode(omode0);
|
||||||
|
|
||||||
switch(QID(c->qid)){
|
switch(QID(c->qid)){
|
||||||
case Qtext:
|
case Qtext:
|
||||||
|
@ -425,6 +437,7 @@ procopen(Chan *c, int omode)
|
||||||
case Qfpregs:
|
case Qfpregs:
|
||||||
case Qsyscall:
|
case Qsyscall:
|
||||||
case Qppid:
|
case Qppid:
|
||||||
|
case Qwatchpt:
|
||||||
nonone(p);
|
nonone(p);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -454,6 +467,19 @@ procopen(Chan *c, int omode)
|
||||||
error(Eprocdied);
|
error(Eprocdied);
|
||||||
|
|
||||||
tc = devopen(c, omode, 0, 0, procgen);
|
tc = devopen(c, omode, 0, 0, procgen);
|
||||||
|
if(waserror()){
|
||||||
|
cclose(tc);
|
||||||
|
nexterror();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(QID(c->qid)){
|
||||||
|
case Qwatchpt:
|
||||||
|
if((omode0 & OTRUNC) != 0)
|
||||||
|
clearwatchpt(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
poperror();
|
||||||
qunlock(&p->debug);
|
qunlock(&p->debug);
|
||||||
poperror();
|
poperror();
|
||||||
|
|
||||||
|
@ -700,6 +726,119 @@ readfd1(Chan *c, Proc *p, char *buf, int nbuf)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* setupwatchpts(Proc *p, Watchpt *wp, int nwp) is defined for all arches separately.
|
||||||
|
* It tests whether wp is a valid set of watchpoints and errors out otherwise.
|
||||||
|
* If and only if they are valid, it sets up all watchpoints (clearing any preexisting ones).
|
||||||
|
* This is to make sure that failed writes to watchpt don't touch the existing watchpoints.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
clearwatchpt(Proc *p)
|
||||||
|
{
|
||||||
|
setupwatchpts(p, nil, 0);
|
||||||
|
free(p->watchpt);
|
||||||
|
p->watchpt = nil;
|
||||||
|
p->nwatchpt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lenwatchpt(Proc *pr)
|
||||||
|
{
|
||||||
|
/* careful, not holding debug lock */
|
||||||
|
return pr->nwatchpt * (10 + 4 * sizeof(uintptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
readwatchpt(Proc *pr, char *buf, int nbuf)
|
||||||
|
{
|
||||||
|
char *p, *e;
|
||||||
|
Watchpt *w;
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
e = buf + nbuf;
|
||||||
|
/* careful, length has to match lenwatchpt() */
|
||||||
|
for(w = pr->watchpt; w < pr->watchpt + pr->nwatchpt; w++)
|
||||||
|
p = seprint(p, e, sizeof(uintptr) == 8 ? "%c%c%c %#.16p %#.16p\n" : "%c%c%c %#.8p %#.8p\n",
|
||||||
|
(w->type & WATCHRD) != 0 ? 'r' : '-',
|
||||||
|
(w->type & WATCHWR) != 0 ? 'w' : '-',
|
||||||
|
(w->type & WATCHEX) != 0 ? 'x' : '-',
|
||||||
|
(void *) w->addr, (void *) w->len);
|
||||||
|
return p - buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
writewatchpt(Proc *pr, char *buf, int nbuf, uvlong offset)
|
||||||
|
{
|
||||||
|
char *p, *q, *e;
|
||||||
|
char line[256], *f[4];
|
||||||
|
Watchpt *wp, *wq;
|
||||||
|
int rc, nwp, nwp0;
|
||||||
|
uvlong x;
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
e = buf + nbuf;
|
||||||
|
if(offset != 0)
|
||||||
|
nwp0 = pr->nwatchpt;
|
||||||
|
else
|
||||||
|
nwp0 = 0;
|
||||||
|
nwp = 0;
|
||||||
|
for(q = p; q < e; q++)
|
||||||
|
nwp += *q == '\n';
|
||||||
|
if(nwp > 65536) error(Egreg);
|
||||||
|
wp = malloc((nwp0+nwp) * sizeof(Watchpt));
|
||||||
|
if(wp == nil) error(Enomem);
|
||||||
|
if(waserror()){
|
||||||
|
free(wp);
|
||||||
|
nexterror();
|
||||||
|
}
|
||||||
|
if(nwp0 > 0)
|
||||||
|
memmove(wp, pr->watchpt, sizeof(Watchpt) * nwp0);
|
||||||
|
for(wq = wp + nwp0;;){
|
||||||
|
q = memchr(p, '\n', e - p);
|
||||||
|
if(q == nil)
|
||||||
|
break;
|
||||||
|
if(q - p > sizeof(line) - 1)
|
||||||
|
error("line too long");
|
||||||
|
memmove(line, p, q - p);
|
||||||
|
line[q - p] = 0;
|
||||||
|
p = q + 1;
|
||||||
|
|
||||||
|
rc = tokenize(line, f, nelem(f));
|
||||||
|
if(rc == 0) continue;
|
||||||
|
if(rc != 3)
|
||||||
|
error("wrong number of fields");
|
||||||
|
for(q = f[0]; *q != 0; q++)
|
||||||
|
switch(*q){
|
||||||
|
case 'r': if((wq->type & WATCHRD) != 0) goto tinval; wq->type |= WATCHRD; break;
|
||||||
|
case 'w': if((wq->type & WATCHWR) != 0) goto tinval; wq->type |= WATCHWR; break;
|
||||||
|
case 'x': if((wq->type & WATCHEX) != 0) goto tinval; wq->type |= WATCHEX; break;
|
||||||
|
case '-': break;
|
||||||
|
default: tinval: error("invalid type");
|
||||||
|
}
|
||||||
|
x = strtoull(f[1], &q, 0);
|
||||||
|
if(f[1] == q || *q != 0 || x != (uintptr) x) error("invalid address");
|
||||||
|
wq->addr = x;
|
||||||
|
x = strtoull(f[2], &q, 0);
|
||||||
|
if(f[2] == q || *q != 0 || x != (uintptr) x) error("invalid length");
|
||||||
|
wq->len = x;
|
||||||
|
if(!okaddr(wq->addr, wq->len, 0)) error("bad address");
|
||||||
|
wq++;
|
||||||
|
}
|
||||||
|
nwp = wq - (wp + nwp0);
|
||||||
|
if(nwp == 0 && nwp0 == pr->nwatchpt){
|
||||||
|
poperror();
|
||||||
|
free(wp);
|
||||||
|
return p - buf;
|
||||||
|
}
|
||||||
|
setupwatchpts(pr, wp, nwp0 + nwp);
|
||||||
|
poperror();
|
||||||
|
free(pr->watchpt);
|
||||||
|
pr->watchpt = wp;
|
||||||
|
pr->nwatchpt = nwp0 + nwp;
|
||||||
|
return p - buf;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* userspace can't pass negative file offset for a
|
* userspace can't pass negative file offset for a
|
||||||
* 64 bit kernel address, so we use 63 bit and sign
|
* 64 bit kernel address, so we use 63 bit and sign
|
||||||
|
@ -1006,6 +1145,16 @@ procread(Chan *c, void *va, long n, vlong off)
|
||||||
|
|
||||||
case Qppid:
|
case Qppid:
|
||||||
return readnum(offset, va, n, p->parentpid, NUMSIZE);
|
return readnum(offset, va, n, p->parentpid, NUMSIZE);
|
||||||
|
|
||||||
|
case Qwatchpt:
|
||||||
|
eqlock(&p->debug);
|
||||||
|
j = readwatchpt(p, statbuf, sizeof(statbuf));
|
||||||
|
qunlock(&p->debug);
|
||||||
|
if(offset >= j)
|
||||||
|
return 0;
|
||||||
|
if(offset+n > j)
|
||||||
|
n = j - offset;
|
||||||
|
goto statbufread;
|
||||||
|
|
||||||
}
|
}
|
||||||
error(Egreg);
|
error(Egreg);
|
||||||
|
@ -1125,6 +1274,9 @@ procwrite(Chan *c, void *va, long n, vlong off)
|
||||||
if(p->noteid != id)
|
if(p->noteid != id)
|
||||||
error(Ebadarg);
|
error(Ebadarg);
|
||||||
break;
|
break;
|
||||||
|
case Qwatchpt:
|
||||||
|
writewatchpt(p, va, n, off);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print("unknown qid in procwrite\n");
|
print("unknown qid in procwrite\n");
|
||||||
error(Egreg);
|
error(Egreg);
|
||||||
|
|
|
@ -49,6 +49,7 @@ typedef struct Timers Timers;
|
||||||
typedef struct Uart Uart;
|
typedef struct Uart Uart;
|
||||||
typedef struct Waitq Waitq;
|
typedef struct Waitq Waitq;
|
||||||
typedef struct Walkqid Walkqid;
|
typedef struct Walkqid Walkqid;
|
||||||
|
typedef struct Watchpt Watchpt;
|
||||||
typedef struct Watchdog Watchdog;
|
typedef struct Watchdog Watchdog;
|
||||||
typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
|
typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
|
||||||
|
|
||||||
|
@ -772,6 +773,9 @@ struct Proc
|
||||||
PMMU;
|
PMMU;
|
||||||
|
|
||||||
char *syscalltrace; /* syscall trace */
|
char *syscalltrace; /* syscall trace */
|
||||||
|
|
||||||
|
Watchpt *watchpt; /* watchpoints */
|
||||||
|
int nwatchpt;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -962,6 +966,16 @@ struct Watchdog
|
||||||
void (*stat)(char*, char*); /* watchdog statistics */
|
void (*stat)(char*, char*); /* watchdog statistics */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Watchpt
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
WATCHRD = 1,
|
||||||
|
WATCHWR = 2,
|
||||||
|
WATCHEX = 4,
|
||||||
|
} type;
|
||||||
|
uintptr addr, len;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */
|
/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */
|
||||||
enum
|
enum
|
||||||
|
|
|
@ -319,6 +319,7 @@ void setmalloctag(void*, uintptr);
|
||||||
void setrealloctag(void*, uintptr);
|
void setrealloctag(void*, uintptr);
|
||||||
void setregisters(Ureg*, char*, char*, int);
|
void setregisters(Ureg*, char*, char*, int);
|
||||||
void setswapchan(Chan*);
|
void setswapchan(Chan*);
|
||||||
|
void setupwatchpts(Proc*, Watchpt*, int);
|
||||||
char* skipslash(char*);
|
char* skipslash(char*);
|
||||||
void sleep(Rendez*, int(*)(void*), void*);
|
void sleep(Rendez*, int(*)(void*), void*);
|
||||||
void* smalloc(ulong);
|
void* smalloc(ulong);
|
||||||
|
|
|
@ -1209,6 +1209,10 @@ pexit(char *exitstr, int freemem)
|
||||||
free(up->syscalltrace);
|
free(up->syscalltrace);
|
||||||
up->syscalltrace = nil;
|
up->syscalltrace = nil;
|
||||||
}
|
}
|
||||||
|
if(up->watchpt != nil){
|
||||||
|
free(up->watchpt);
|
||||||
|
up->watchpt = nil;
|
||||||
|
}
|
||||||
qunlock(&up->debug);
|
qunlock(&up->debug);
|
||||||
|
|
||||||
/* Sched must not loop for these locks */
|
/* Sched must not loop for these locks */
|
||||||
|
|
|
@ -497,3 +497,10 @@ cistrncmp(char *a, char *b, int n)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -497,3 +497,10 @@ confinit(void)
|
||||||
imagmem->maxsize = kpages;
|
imagmem->maxsize = kpages;
|
||||||
// mainmem->flags |= POOL_PARANOIA;
|
// mainmem->flags |= POOL_PARANOIA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -907,3 +907,10 @@ wakewfi(void)
|
||||||
intrcpu(cpu);
|
intrcpu(cpu);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
|
@ -427,3 +427,10 @@ main(void)
|
||||||
userinit();
|
userinit();
|
||||||
schedinit();
|
schedinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupwatchpts(Proc *, Watchpt *, int n)
|
||||||
|
{
|
||||||
|
if(n > 0)
|
||||||
|
error("no watchpoints");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue