kernel: add support for hardware watchpoints
This commit is contained in:
parent
1cfa405d0a
commit
773be02aa1
|
@ -584,3 +584,10 @@ cmpswap(long *addr, long old, long 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 *ldt; /* local descriptor table */
|
||||
int nldt; /* number of ldt descriptors allocated */
|
||||
|
||||
u32int dr[8]; /* debug registers */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -252,6 +254,7 @@ struct Mach
|
|||
char* cpuidtype;
|
||||
int havetsc;
|
||||
int havepge;
|
||||
int havewatchpt8;
|
||||
uvlong tscticks;
|
||||
int pdballoc;
|
||||
int pdbfree;
|
||||
|
|
|
@ -49,6 +49,9 @@ enum { /* cpuid standard function codes */
|
|||
Procsig,
|
||||
Proctlbcache,
|
||||
Procserial,
|
||||
|
||||
Highextfunc = 0x80000000,
|
||||
Procextfeat,
|
||||
};
|
||||
|
||||
typedef long Rdwrfn(Chan*, void*, long, vlong);
|
||||
|
@ -874,6 +877,23 @@ cpuidentify(void)
|
|||
hwrandbuf = rdrandbuf;
|
||||
else
|
||||
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;
|
||||
return t->family;
|
||||
|
@ -1229,3 +1249,61 @@ dumpmcregs(void)
|
|||
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 getcr3(void);
|
||||
ulong getcr4(void);
|
||||
u32int getdr6(void);
|
||||
char* getconf(char*);
|
||||
void guesscpuhz(int);
|
||||
void halt(void);
|
||||
|
@ -165,6 +166,9 @@ void procfork(Proc*);
|
|||
void putcr0(ulong);
|
||||
void putcr3(ulong);
|
||||
void putcr4(ulong);
|
||||
void putdr(u32int*);
|
||||
void putdr6(u32int);
|
||||
void putdr7(u32int);
|
||||
void* rampage(void);
|
||||
int rdmsr(int, vlong*);
|
||||
void realmode(Ureg*);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#define X86FAMILY(x) ((((x)>>8) & 0x0F) | (((x)>>20) & 0xFF)<<4)
|
||||
|
||||
enum {
|
||||
VectorDE = 1, /* debug exception */
|
||||
VectorNMI = 2, /* non-maskable interrupt */
|
||||
VectorBPT = 3, /* breakpoint */
|
||||
VectorUD = 6, /* invalid opcode exception */
|
||||
|
|
|
@ -845,6 +845,38 @@ _rndbytes:
|
|||
_rnddone:
|
||||
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:
|
||||
* 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));
|
||||
p->ldt = nil;
|
||||
p->nldt = 0;
|
||||
|
||||
memset(p->dr, 0, sizeof(p->dr));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -831,6 +833,9 @@ procfork(Proc *p)
|
|||
p->fpsave = up->fpsave;
|
||||
p->fpstate = FPinactive;
|
||||
}
|
||||
|
||||
/* clear debug registers */
|
||||
memset(p->dr, 0, sizeof(p->dr));
|
||||
splx(s);
|
||||
}
|
||||
|
||||
|
@ -838,6 +843,9 @@ void
|
|||
procrestore(Proc *p)
|
||||
{
|
||||
uvlong t;
|
||||
|
||||
if(p->dr[7] != 0)
|
||||
putdr(p->dr);
|
||||
|
||||
if(p->kp)
|
||||
return;
|
||||
|
@ -854,6 +862,9 @@ void
|
|||
procsave(Proc *p)
|
||||
{
|
||||
uvlong t;
|
||||
|
||||
if(p->dr[7] != 0)
|
||||
putdr7(0);
|
||||
|
||||
cycles(&t);
|
||||
p->kentry -= t;
|
||||
|
|
|
@ -13,6 +13,7 @@ static int trapinited;
|
|||
|
||||
void noted(Ureg*, ulong);
|
||||
|
||||
static void debugexc(Ureg*, void*);
|
||||
static void debugbpt(Ureg*, void*);
|
||||
static void fault386(Ureg*, void*);
|
||||
static void doublefault(Ureg*, void*);
|
||||
|
@ -222,6 +223,7 @@ trapinit(void)
|
|||
* Special traps.
|
||||
* Syscall() is called directly without going through trap().
|
||||
*/
|
||||
trapenable(VectorDE, debugexc, 0, "debugexc");
|
||||
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
||||
trapenable(VectorPF, fault386, 0, "fault386");
|
||||
trapenable(Vector2F, doublefault, 0, "doublefault");
|
||||
|
@ -626,6 +628,35 @@ dumpstack(void)
|
|||
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
|
||||
debugbpt(Ureg* ureg, void*)
|
||||
{
|
||||
|
|
|
@ -144,6 +144,8 @@ struct PMMU
|
|||
ulong kmapcount;
|
||||
ulong kmapindex;
|
||||
ulong mmucount;
|
||||
|
||||
u64int dr[8];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -219,6 +221,7 @@ struct Mach
|
|||
char* cpuidtype;
|
||||
int havetsc;
|
||||
int havepge;
|
||||
int havewatchpt8;
|
||||
uvlong tscticks;
|
||||
|
||||
uintptr stack[1];
|
||||
|
|
|
@ -44,6 +44,7 @@ u64int getcr0(void);
|
|||
u64int getcr2(void);
|
||||
u64int getcr3(void);
|
||||
u64int getcr4(void);
|
||||
u64int getdr6(void);
|
||||
char* getconf(char*);
|
||||
void guesscpuhz(int);
|
||||
void halt(void);
|
||||
|
@ -158,6 +159,9 @@ void procfork(Proc*);
|
|||
void putcr0(u64int);
|
||||
void putcr3(u64int);
|
||||
void putcr4(u64int);
|
||||
void putdr(u64int*);
|
||||
void putdr6(u64int);
|
||||
void putdr7(u64int);
|
||||
void* rampage(void);
|
||||
int rdmsr(int, vlong*);
|
||||
void realmode(Ureg*);
|
||||
|
|
|
@ -692,6 +692,35 @@ ones:
|
|||
f3:
|
||||
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
|
||||
|
|
|
@ -797,6 +797,9 @@ void
|
|||
procrestore(Proc *p)
|
||||
{
|
||||
uvlong t;
|
||||
|
||||
if(p->dr[7] != 0)
|
||||
putdr(p->dr);
|
||||
|
||||
if(p->kp)
|
||||
return;
|
||||
|
@ -810,6 +813,9 @@ void
|
|||
procsave(Proc *p)
|
||||
{
|
||||
uvlong t;
|
||||
|
||||
if(p->dr[7] != 0)
|
||||
putdr7(0);
|
||||
|
||||
cycles(&t);
|
||||
p->kentry -= t;
|
||||
|
|
|
@ -13,6 +13,7 @@ static int trapinited;
|
|||
|
||||
void noted(Ureg*, ulong);
|
||||
|
||||
static void debugexc(Ureg*, void*);
|
||||
static void debugbpt(Ureg*, void*);
|
||||
static void faultamd64(Ureg*, void*);
|
||||
static void doublefault(Ureg*, void*);
|
||||
|
@ -224,6 +225,7 @@ trapinit(void)
|
|||
* Special traps.
|
||||
* Syscall() is called directly without going through trap().
|
||||
*/
|
||||
trapenable(VectorDE, debugexc, 0, "debugexc");
|
||||
trapenable(VectorBPT, debugbpt, 0, "debugpt");
|
||||
trapenable(VectorPF, faultamd64, 0, "faultamd64");
|
||||
trapenable(Vector2F, doublefault, 0, "doublefault");
|
||||
|
@ -587,6 +589,35 @@ dumpstack(void)
|
|||
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
|
||||
debugbpt(Ureg* ureg, void*)
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ enum
|
|||
Qwait,
|
||||
Qprofile,
|
||||
Qsyscall,
|
||||
Qwatchpt,
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -101,6 +102,7 @@ Dirtab procdir[] =
|
|||
"wait", {Qwait}, 0, 0400,
|
||||
"profile", {Qprofile}, 0, 0400,
|
||||
"syscall", {Qsyscall}, 0, 0400,
|
||||
"watchpt", {Qwatchpt}, 0, 0600,
|
||||
};
|
||||
|
||||
static
|
||||
|
@ -181,6 +183,8 @@ profclock(Ureg *ur, Timer *)
|
|||
}
|
||||
}
|
||||
|
||||
static int lenwatchpt(Proc *);
|
||||
|
||||
static int
|
||||
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;
|
||||
}
|
||||
switch(QID(tab->qid)){
|
||||
case Qwatchpt:
|
||||
len = lenwatchpt(p);
|
||||
break;
|
||||
}
|
||||
|
||||
mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
|
||||
devdir(c, qid, tab->name, len, p->user, perm, dp);
|
||||
|
@ -334,19 +343,22 @@ nonone(Proc *p)
|
|||
error(Eperm);
|
||||
}
|
||||
|
||||
static void clearwatchpt(Proc *p);
|
||||
|
||||
static Chan*
|
||||
procopen(Chan *c, int omode)
|
||||
procopen(Chan *c, int omode0)
|
||||
{
|
||||
Proc *p;
|
||||
Pgrp *pg;
|
||||
Chan *tc;
|
||||
int pid;
|
||||
int omode;
|
||||
|
||||
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 (omode != OREAD)
|
||||
if (omode0 != OREAD)
|
||||
error(Eperm);
|
||||
lock(&tlock);
|
||||
if (waserror()){
|
||||
|
@ -366,7 +378,7 @@ procopen(Chan *c, int omode)
|
|||
unlock(&tlock);
|
||||
poperror();
|
||||
|
||||
c->mode = openmode(omode);
|
||||
c->mode = openmode(omode0);
|
||||
c->flag |= COPEN;
|
||||
c->offset = 0;
|
||||
return c;
|
||||
|
@ -382,7 +394,7 @@ procopen(Chan *c, int omode)
|
|||
if(p->pid != pid)
|
||||
error(Eprocdied);
|
||||
|
||||
omode = openmode(omode);
|
||||
omode = openmode(omode0);
|
||||
|
||||
switch(QID(c->qid)){
|
||||
case Qtext:
|
||||
|
@ -425,6 +437,7 @@ procopen(Chan *c, int omode)
|
|||
case Qfpregs:
|
||||
case Qsyscall:
|
||||
case Qppid:
|
||||
case Qwatchpt:
|
||||
nonone(p);
|
||||
break;
|
||||
|
||||
|
@ -454,6 +467,19 @@ procopen(Chan *c, int omode)
|
|||
error(Eprocdied);
|
||||
|
||||
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);
|
||||
poperror();
|
||||
|
||||
|
@ -700,6 +726,119 @@ readfd1(Chan *c, Proc *p, char *buf, int nbuf)
|
|||
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
|
||||
* 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:
|
||||
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);
|
||||
|
@ -1125,6 +1274,9 @@ procwrite(Chan *c, void *va, long n, vlong off)
|
|||
if(p->noteid != id)
|
||||
error(Ebadarg);
|
||||
break;
|
||||
case Qwatchpt:
|
||||
writewatchpt(p, va, n, off);
|
||||
break;
|
||||
default:
|
||||
print("unknown qid in procwrite\n");
|
||||
error(Egreg);
|
||||
|
|
|
@ -49,6 +49,7 @@ typedef struct Timers Timers;
|
|||
typedef struct Uart Uart;
|
||||
typedef struct Waitq Waitq;
|
||||
typedef struct Walkqid Walkqid;
|
||||
typedef struct Watchpt Watchpt;
|
||||
typedef struct Watchdog Watchdog;
|
||||
typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
|
||||
|
||||
|
@ -772,6 +773,9 @@ struct Proc
|
|||
PMMU;
|
||||
|
||||
char *syscalltrace; /* syscall trace */
|
||||
|
||||
Watchpt *watchpt; /* watchpoints */
|
||||
int nwatchpt;
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -962,6 +966,16 @@ struct Watchdog
|
|||
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 */
|
||||
enum
|
||||
|
|
|
@ -319,6 +319,7 @@ void setmalloctag(void*, uintptr);
|
|||
void setrealloctag(void*, uintptr);
|
||||
void setregisters(Ureg*, char*, char*, int);
|
||||
void setswapchan(Chan*);
|
||||
void setupwatchpts(Proc*, Watchpt*, int);
|
||||
char* skipslash(char*);
|
||||
void sleep(Rendez*, int(*)(void*), void*);
|
||||
void* smalloc(ulong);
|
||||
|
|
|
@ -1209,6 +1209,10 @@ pexit(char *exitstr, int freemem)
|
|||
free(up->syscalltrace);
|
||||
up->syscalltrace = nil;
|
||||
}
|
||||
if(up->watchpt != nil){
|
||||
free(up->watchpt);
|
||||
up->watchpt = nil;
|
||||
}
|
||||
qunlock(&up->debug);
|
||||
|
||||
/* Sched must not loop for these locks */
|
||||
|
|
|
@ -497,3 +497,10 @@ cistrncmp(char *a, char *b, int n)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
setupwatchpts(Proc *, Watchpt *, int n)
|
||||
{
|
||||
if(n > 0)
|
||||
error("no watchpoints");
|
||||
}
|
||||
|
|
|
@ -497,3 +497,10 @@ confinit(void)
|
|||
imagmem->maxsize = kpages;
|
||||
// mainmem->flags |= POOL_PARANOIA;
|
||||
}
|
||||
|
||||
void
|
||||
setupwatchpts(Proc *, Watchpt *, int n)
|
||||
{
|
||||
if(n > 0)
|
||||
error("no watchpoints");
|
||||
}
|
||||
|
|
|
@ -907,3 +907,10 @@ wakewfi(void)
|
|||
intrcpu(cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
setupwatchpts(Proc *, Watchpt *, int n)
|
||||
{
|
||||
if(n > 0)
|
||||
error("no watchpoints");
|
||||
}
|
||||
|
|
|
@ -427,3 +427,10 @@ main(void)
|
|||
userinit();
|
||||
schedinit();
|
||||
}
|
||||
|
||||
void
|
||||
setupwatchpts(Proc *, Watchpt *, int n)
|
||||
{
|
||||
if(n > 0)
|
||||
error("no watchpoints");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue