changelog shortlog tags branches files raw gz bz2 help

Mercurial > hg > plan9front / changeset: kernel: add support for hardware watchpoints

changeset 6012: e584910aa1b9
parent 6011: a002d8cbe5f5
child 6013: 35dfbf142a32
author: aiju
date: Mon, 12 Jun 2017 19:03:07 +0000
files: sys/src/9/bcm/main.c sys/src/9/kw/main.c sys/src/9/mtx/main.c sys/src/9/omap/main.c sys/src/9/pc/dat.h sys/src/9/pc/devarch.c sys/src/9/pc/fns.h sys/src/9/pc/io.h sys/src/9/pc/l.s sys/src/9/pc/main.c sys/src/9/pc/trap.c sys/src/9/pc64/dat.h sys/src/9/pc64/fns.h sys/src/9/pc64/l.s sys/src/9/pc64/main.c sys/src/9/pc64/trap.c sys/src/9/port/devproc.c sys/src/9/port/portdat.h sys/src/9/port/portfns.h sys/src/9/port/proc.c sys/src/9/ppc/main.c sys/src/9/sgi/main.c sys/src/9/teg2/main.c sys/src/9/zynq/main.c
description: kernel: add support for hardware watchpoints
     1.1--- a/sys/src/9/bcm/main.c
     1.2+++ b/sys/src/9/bcm/main.c
     1.3@@ -584,3 +584,10 @@ cmpswap(long *addr, long old, long new)
     1.4 {
     1.5 	return cas32(addr, old, new);
     1.6 }
     1.7+
     1.8+void
     1.9+setupwatchpts(Proc *, Watchpt *, int n)
    1.10+{
    1.11+	if(n > 0)
    1.12+		error("no watchpoints");
    1.13+}
     2.1--- a/sys/src/9/kw/main.c
     2.2+++ b/sys/src/9/kw/main.c
     2.3@@ -638,3 +638,10 @@ cmpswap(long *addr, long old, long new)
     2.4 {
     2.5 	return cas32(addr, old, new);
     2.6 }
     2.7+
     2.8+void
     2.9+setupwatchpts(Proc *, Watchpt *, int n)
    2.10+{
    2.11+	if(n > 0)
    2.12+		error("no watchpoints");
    2.13+}
     3.1--- a/sys/src/9/mtx/main.c
     3.2+++ b/sys/src/9/mtx/main.c
     3.3@@ -436,3 +436,10 @@ cistrncmp(char *a, char *b, int n)
     3.4 
     3.5 	return 0;
     3.6 }
     3.7+
     3.8+void
     3.9+setupwatchpts(Proc *, Watchpt *, int n)
    3.10+{
    3.11+	if(n > 0)
    3.12+		error("no watchpoints");
    3.13+}
     4.1--- a/sys/src/9/omap/main.c
     4.2+++ b/sys/src/9/omap/main.c
     4.3@@ -654,3 +654,10 @@ cmpswap(long *addr, long old, long new)
     4.4 {
     4.5 	return cas32(addr, old, new);
     4.6 }
     4.7+
     4.8+void
     4.9+setupwatchpts(Proc *, Watchpt *, int n)
    4.10+{
    4.11+	if(n > 0)
    4.12+		error("no watchpoints");
    4.13+}
     5.1--- a/sys/src/9/pc/dat.h
     5.2+++ b/sys/src/9/pc/dat.h
     5.3@@ -159,6 +159,8 @@ struct PMMU
     5.4 	Segdesc	gdt[NPROCSEG];	/* per process descriptors */
     5.5 	Segdesc	*ldt;	/* local descriptor table */
     5.6 	int	nldt;	/* number of ldt descriptors allocated */
     5.7+	
     5.8+	u32int	dr[8];			/* debug registers */
     5.9 };
    5.10 
    5.11 /*
    5.12@@ -252,6 +254,7 @@ struct Mach
    5.13 	char*	cpuidtype;
    5.14 	int	havetsc;
    5.15 	int	havepge;
    5.16+	int	havewatchpt8;
    5.17 	uvlong	tscticks;
    5.18 	int	pdballoc;
    5.19 	int	pdbfree;
     6.1--- a/sys/src/9/pc/devarch.c
     6.2+++ b/sys/src/9/pc/devarch.c
     6.3@@ -49,6 +49,9 @@ enum {				/* cpuid standard function cod
     6.4 	Procsig,
     6.5 	Proctlbcache,
     6.6 	Procserial,
     6.7+	
     6.8+	Highextfunc = 0x80000000,
     6.9+	Procextfeat,
    6.10 };
    6.11 
    6.12 typedef long Rdwrfn(Chan*, void*, long, vlong);
    6.13@@ -874,6 +877,23 @@ cpuidentify(void)
    6.14 		hwrandbuf = rdrandbuf;
    6.15 	else
    6.16 		hwrandbuf = nil;
    6.17+	
    6.18+	/* 8-byte watchpoints are supported in Long Mode */
    6.19+	if(sizeof(uintptr) == 8)
    6.20+		m->havewatchpt8 = 1;
    6.21+	else if(strcmp(m->cpuidid, "GenuineIntel") == 0){
    6.22+		/* some random CPUs that support 8-byte watchpoints */
    6.23+		if(family == 15 && (model == 3 || model == 4 || model == 6)
    6.24+		|| family == 6 && (model == 15 || model == 23 || model == 28))
    6.25+			m->havewatchpt8 = 1;
    6.26+		/* Intel SDM claims amd64 support implies 8-byte watchpoint support */
    6.27+		cpuid(Highextfunc, regs);
    6.28+		if(regs[0] >= Procextfeat){
    6.29+			cpuid(Procextfeat, regs);
    6.30+			if((regs[3] & 1<<29) != 0)
    6.31+				m->havewatchpt8 = 1;
    6.32+		}
    6.33+	}
    6.34 
    6.35 	cputype = t;
    6.36 	return t->family;
    6.37@@ -1229,3 +1249,61 @@ dumpmcregs(void)
    6.38 		iprint("\n");
    6.39 	}
    6.40 }
    6.41+
    6.42+void
    6.43+setupwatchpts(Proc *pr, Watchpt *wp, int nwp)
    6.44+{
    6.45+	int i;
    6.46+	u8int cfg;
    6.47+	Watchpt *p;
    6.48+
    6.49+	if(nwp > 4)
    6.50+		error("there are four watchpoints.");
    6.51+	if(nwp == 0){
    6.52+		memset(pr->dr, 0, sizeof(pr->dr));
    6.53+		return;
    6.54+	}
    6.55+	for(p = wp; p < wp + nwp; p++){
    6.56+		switch(p->type){
    6.57+		case WATCHRD|WATCHWR: case WATCHWR:
    6.58+			break;
    6.59+		case WATCHEX:
    6.60+			if(p->len != 1)
    6.61+				error("length must be 1 on breakpoints");
    6.62+			break;
    6.63+		default:
    6.64+			error("type must be rw-, -w- or --x");
    6.65+		}
    6.66+		switch(p->len){
    6.67+		case 1: case 2: case 4:
    6.68+			break;
    6.69+		case 8:
    6.70+			if(m->havewatchpt8) break;
    6.71+		default:
    6.72+			error(m->havewatchpt8 ? "length must be 1,2,4,8" : "length must be 1,2,4");
    6.73+		}
    6.74+		if((p->addr & p->len - 1) != 0)
    6.75+			error("address must be aligned according to length");
    6.76+	}
    6.77+	
    6.78+	memset(pr->dr, 0, sizeof(pr->dr));
    6.79+	pr->dr[6] = 0xffff8ff0;
    6.80+	for(i = 0; i < nwp; i++){
    6.81+		pr->dr[i] = wp[i].addr;
    6.82+		switch(wp[i].type){
    6.83+			case WATCHRD|WATCHWR: cfg = 3; break;
    6.84+			case WATCHWR: cfg = 1; break;
    6.85+			case WATCHEX: cfg = 0; break;
    6.86+			default: continue;
    6.87+		}
    6.88+		switch(wp[i].len){
    6.89+			case 1: break;
    6.90+			case 2: cfg |= 4; break;
    6.91+			case 4: cfg |= 12; break;
    6.92+			case 8: cfg |= 8; break;
    6.93+			default: continue;
    6.94+		}
    6.95+		pr->dr[7] |= cfg << 16 + 4 * i;
    6.96+		pr->dr[7] |= 1 << 2 * i + 1;
    6.97+	}
    6.98+}
     7.1--- a/sys/src/9/pc/fns.h
     7.2+++ b/sys/src/9/pc/fns.h
     7.3@@ -51,6 +51,7 @@ ulong	getcr0(void);
     7.4 ulong	getcr2(void);
     7.5 ulong	getcr3(void);
     7.6 ulong	getcr4(void);
     7.7+u32int	getdr6(void);
     7.8 char*	getconf(char*);
     7.9 void	guesscpuhz(int);
    7.10 void	halt(void);
    7.11@@ -165,6 +166,9 @@ void	procfork(Proc*);
    7.12 void	putcr0(ulong);
    7.13 void	putcr3(ulong);
    7.14 void	putcr4(ulong);
    7.15+void	putdr(u32int*);
    7.16+void	putdr6(u32int);
    7.17+void	putdr7(u32int);
    7.18 void*	rampage(void);
    7.19 int	rdmsr(int, vlong*);
    7.20 void	realmode(Ureg*);
     8.1--- a/sys/src/9/pc/io.h
     8.2+++ b/sys/src/9/pc/io.h
     8.3@@ -4,6 +4,7 @@
     8.4 #define X86FAMILY(x)	((((x)>>8) & 0x0F) | (((x)>>20) & 0xFF)<<4)
     8.5 
     8.6 enum {
     8.7+	VectorDE	= 1,		/* debug exception */
     8.8 	VectorNMI	= 2,		/* non-maskable interrupt */
     8.9 	VectorBPT	= 3,		/* breakpoint */
    8.10 	VectorUD	= 6,		/* invalid opcode exception */
     9.1--- a/sys/src/9/pc/l.s
     9.2+++ b/sys/src/9/pc/l.s
     9.3@@ -845,6 +845,38 @@ TEXT rdrandbuf(SB), $0
     9.4 _rnddone:
     9.5 	RET
     9.6 
     9.7+/* debug register access */
     9.8+
     9.9+TEXT putdr(SB), $0
    9.10+	MOVL	p+0(FP), SI
    9.11+	MOVL	28(SI), AX
    9.12+	MOVL	AX, DR7
    9.13+	MOVL	0(SI), AX
    9.14+	MOVL	AX, DR0
    9.15+	MOVL	4(SI), AX
    9.16+	MOVL	AX, DR1
    9.17+	MOVL	8(SI), AX
    9.18+	MOVL	AX, DR2
    9.19+	MOVL	12(SI), AX
    9.20+	MOVL	AX, DR3
    9.21+	MOVL	24(SI), AX
    9.22+	MOVL	AX, DR6
    9.23+	RET
    9.24+
    9.25+TEXT getdr6(SB), $0
    9.26+	MOVL	DR6, AX
    9.27+	RET
    9.28+
    9.29+TEXT putdr6(SB), $0
    9.30+	MOVL	p+0(FP), AX
    9.31+	MOVL	AX, DR6
    9.32+	RET
    9.33+	
    9.34+TEXT putdr7(SB), $0
    9.35+	MOVL	p+0(FP), AX
    9.36+	MOVL	AX, DR7
    9.37+	RET
    9.38+
    9.39 /*
    9.40  *  Used to get to the first process:
    9.41  * 	set up an interrupt return frame and IRET to user level.
    10.1--- a/sys/src/9/pc/main.c
    10.2+++ b/sys/src/9/pc/main.c
    10.3@@ -801,6 +801,8 @@ procsetup(Proc *p)
    10.4 	memset(p->gdt, 0, sizeof(p->gdt));
    10.5 	p->ldt = nil;
    10.6 	p->nldt = 0;
    10.7+	
    10.8+	memset(p->dr, 0, sizeof(p->dr));
    10.9 }
   10.10 
   10.11 void
   10.12@@ -831,6 +833,9 @@ procfork(Proc *p)
   10.13 		p->fpsave = up->fpsave;
   10.14 		p->fpstate = FPinactive;
   10.15 	}
   10.16+	
   10.17+	/* clear debug registers */
   10.18+	memset(p->dr, 0, sizeof(p->dr));
   10.19 	splx(s);
   10.20 }
   10.21 
   10.22@@ -838,6 +843,9 @@ void
   10.23 procrestore(Proc *p)
   10.24 {
   10.25 	uvlong t;
   10.26+	
   10.27+	if(p->dr[7] != 0)
   10.28+		putdr(p->dr);
   10.29 
   10.30 	if(p->kp)
   10.31 		return;
   10.32@@ -854,6 +862,9 @@ void
   10.33 procsave(Proc *p)
   10.34 {
   10.35 	uvlong t;
   10.36+	
   10.37+	if(p->dr[7] != 0)
   10.38+		putdr7(0);
   10.39 
   10.40 	cycles(&t);
   10.41 	p->kentry -= t;
    11.1--- a/sys/src/9/pc/trap.c
    11.2+++ b/sys/src/9/pc/trap.c
    11.3@@ -13,6 +13,7 @@ static int trapinited;
    11.4 
    11.5 void	noted(Ureg*, ulong);
    11.6 
    11.7+static void debugexc(Ureg*, void*);
    11.8 static void debugbpt(Ureg*, void*);
    11.9 static void fault386(Ureg*, void*);
   11.10 static void doublefault(Ureg*, void*);
   11.11@@ -222,6 +223,7 @@ trapinit(void)
   11.12 	 * Special traps.
   11.13 	 * Syscall() is called directly without going through trap().
   11.14 	 */
   11.15+	trapenable(VectorDE, debugexc, 0, "debugexc");
   11.16 	trapenable(VectorBPT, debugbpt, 0, "debugpt");
   11.17 	trapenable(VectorPF, fault386, 0, "fault386");
   11.18 	trapenable(Vector2F, doublefault, 0, "doublefault");
   11.19@@ -627,6 +629,35 @@ dumpstack(void)
   11.20 }
   11.21 
   11.22 static void
   11.23+debugexc(Ureg *, void *)
   11.24+{
   11.25+	u32int dr6, m;
   11.26+	char buf[ERRMAX];
   11.27+	char *p, *e;
   11.28+	int i;
   11.29+
   11.30+	dr6 = getdr6();
   11.31+	if(up == nil)
   11.32+		panic("kernel debug exception dr6=%#.8ux", dr6);
   11.33+	putdr6(up->dr[6]);
   11.34+	m = up->dr[7];
   11.35+	m = (m >> 4 | m >> 3) & 8 | (m >> 3 | m >> 2) & 4 | (m >> 2 | m >> 1) & 2 | (m >> 1 | m) & 1;
   11.36+	m &= dr6;
   11.37+	if(m == 0){
   11.38+		sprint(buf, "sys: debug exception dr6=%#.8ux", dr6);
   11.39+		postnote(up, 1, buf, NDebug);
   11.40+	}else{
   11.41+		p = buf;
   11.42+		e = buf + sizeof(buf);
   11.43+		p = seprint(p, e, "sys: watchpoint ");
   11.44+		for(i = 0; i < 4; i++)
   11.45+			if((m & 1<<i) != 0)
   11.46+				p = seprint(p, e, "%d%s", i, (m >> i + 1 != 0) ? "," : "");
   11.47+		postnote(up, 1, buf, NDebug);
   11.48+	}
   11.49+}
   11.50+
   11.51+static void
   11.52 debugbpt(Ureg* ureg, void*)
   11.53 {
   11.54 	char buf[ERRMAX];
    12.1--- a/sys/src/9/pc64/dat.h
    12.2+++ b/sys/src/9/pc64/dat.h
    12.3@@ -144,6 +144,8 @@ struct PMMU
    12.4 	ulong	kmapcount;
    12.5 	ulong	kmapindex;
    12.6 	ulong	mmucount;
    12.7+	
    12.8+	u64int	dr[8];
    12.9 };
   12.10 
   12.11 /*
   12.12@@ -219,6 +221,7 @@ struct Mach
   12.13 	char*	cpuidtype;
   12.14 	int	havetsc;
   12.15 	int	havepge;
   12.16+	int	havewatchpt8;
   12.17 	uvlong	tscticks;
   12.18 
   12.19 	uintptr	stack[1];
    13.1--- a/sys/src/9/pc64/fns.h
    13.2+++ b/sys/src/9/pc64/fns.h
    13.3@@ -44,6 +44,7 @@ u64int	getcr0(void);
    13.4 u64int	getcr2(void);
    13.5 u64int	getcr3(void);
    13.6 u64int	getcr4(void);
    13.7+u64int	getdr6(void);
    13.8 char*	getconf(char*);
    13.9 void	guesscpuhz(int);
   13.10 void	halt(void);
   13.11@@ -158,6 +159,9 @@ void	procfork(Proc*);
   13.12 void	putcr0(u64int);
   13.13 void	putcr3(u64int);
   13.14 void	putcr4(u64int);
   13.15+void	putdr(u64int*);
   13.16+void	putdr6(u64int);
   13.17+void	putdr7(u64int);
   13.18 void*	rampage(void);
   13.19 int	rdmsr(int, vlong*);
   13.20 void	realmode(Ureg*);
    14.1--- a/sys/src/9/pc64/l.s
    14.2+++ b/sys/src/9/pc64/l.s
    14.3@@ -692,6 +692,35 @@ ones:
    14.4 f3:
    14.5 	RET
    14.6 
    14.7+/* debug register access */
    14.8+
    14.9+TEXT putdr(SB), $0
   14.10+	MOVQ	56(BP), AX
   14.11+	MOVQ	AX, DR7
   14.12+	MOVQ	0(BP), AX
   14.13+	MOVQ	AX, DR0
   14.14+	MOVQ	8(BP), AX
   14.15+	MOVQ	AX, DR1
   14.16+	MOVQ	16(BP), AX
   14.17+	MOVQ	AX, DR2
   14.18+	MOVQ	24(BP), AX
   14.19+	MOVQ	AX, DR3
   14.20+	MOVQ	48(BP), AX
   14.21+	MOVQ	AX, DR6
   14.22+	RET
   14.23+
   14.24+TEXT getdr6(SB), $0
   14.25+	MOVQ	DR6, AX
   14.26+	RET
   14.27+
   14.28+TEXT putdr6(SB), $0
   14.29+	MOVQ	BP, DR6
   14.30+	RET
   14.31+
   14.32+TEXT putdr7(SB), $0
   14.33+	MOVQ	BP, DR7
   14.34+	RET
   14.35+
   14.36 /*
   14.37  */
   14.38 TEXT touser(SB), 1, $-4
    15.1--- a/sys/src/9/pc64/main.c
    15.2+++ b/sys/src/9/pc64/main.c
    15.3@@ -797,6 +797,9 @@ void
    15.4 procrestore(Proc *p)
    15.5 {
    15.6 	uvlong t;
    15.7+	
    15.8+	if(p->dr[7] != 0)
    15.9+		putdr(p->dr);
   15.10 
   15.11 	if(p->kp)
   15.12 		return;
   15.13@@ -810,6 +813,9 @@ void
   15.14 procsave(Proc *p)
   15.15 {
   15.16 	uvlong t;
   15.17+	
   15.18+	if(p->dr[7] != 0)
   15.19+		putdr7(0);
   15.20 
   15.21 	cycles(&t);
   15.22 	p->kentry -= t;
    16.1--- a/sys/src/9/pc64/trap.c
    16.2+++ b/sys/src/9/pc64/trap.c
    16.3@@ -13,6 +13,7 @@ static int trapinited;
    16.4 
    16.5 void	noted(Ureg*, ulong);
    16.6 
    16.7+static void debugexc(Ureg*, void*);
    16.8 static void debugbpt(Ureg*, void*);
    16.9 static void faultamd64(Ureg*, void*);
   16.10 static void doublefault(Ureg*, void*);
   16.11@@ -224,6 +225,7 @@ trapinit(void)
   16.12 	 * Special traps.
   16.13 	 * Syscall() is called directly without going through trap().
   16.14 	 */
   16.15+	trapenable(VectorDE, debugexc, 0, "debugexc");
   16.16 	trapenable(VectorBPT, debugbpt, 0, "debugpt");
   16.17 	trapenable(VectorPF, faultamd64, 0, "faultamd64");
   16.18 	trapenable(Vector2F, doublefault, 0, "doublefault");
   16.19@@ -588,6 +590,35 @@ dumpstack(void)
   16.20 }
   16.21 
   16.22 static void
   16.23+debugexc(Ureg *, void *)
   16.24+{
   16.25+	u64int dr6, m;
   16.26+	char buf[ERRMAX];
   16.27+	char *p, *e;
   16.28+	int i;
   16.29+
   16.30+	dr6 = getdr6();
   16.31+	if(up == nil)
   16.32+		panic("kernel debug exception dr6=%#.8ullx", dr6);
   16.33+	putdr6(up->dr[6]);
   16.34+	m = up->dr[7];
   16.35+	m = (m >> 4 | m >> 3) & 8 | (m >> 3 | m >> 2) & 4 | (m >> 2 | m >> 1) & 2 | (m >> 1 | m) & 1;
   16.36+	m &= dr6;
   16.37+	if(m == 0){
   16.38+		sprint(buf, "sys: debug exception dr6=%#.8ullx", dr6);
   16.39+		postnote(up, 1, buf, NDebug);
   16.40+	}else{
   16.41+		p = buf;
   16.42+		e = buf + sizeof(buf);
   16.43+		p = seprint(p, e, "sys: watchpoint ");
   16.44+		for(i = 0; i < 4; i++)
   16.45+			if((m & 1<<i) != 0)
   16.46+				p = seprint(p, e, "%d%s", i, (m >> i + 1 != 0) ? "," : "");
   16.47+		postnote(up, 1, buf, NDebug);
   16.48+	}
   16.49+}
   16.50+			
   16.51+static void
   16.52 debugbpt(Ureg* ureg, void*)
   16.53 {
   16.54 	char buf[ERRMAX];
    17.1--- a/sys/src/9/port/devproc.c
    17.2+++ b/sys/src/9/port/devproc.c
    17.3@@ -34,6 +34,7 @@ enum
    17.4 	Qwait,
    17.5 	Qprofile,
    17.6 	Qsyscall,
    17.7+	Qwatchpt,
    17.8 };
    17.9 
   17.10 enum
   17.11@@ -101,6 +102,7 @@ Dirtab procdir[] =
   17.12 	"wait",		{Qwait},	0,			0400,
   17.13 	"profile",	{Qprofile},	0,			0400,
   17.14 	"syscall",	{Qsyscall},	0,			0400,	
   17.15+	"watchpt",	{Qwatchpt},	0,			0600,
   17.16 };
   17.17 
   17.18 static
   17.19@@ -181,6 +183,8 @@ profclock(Ureg *ur, Timer *)
   17.20 	}
   17.21 }
   17.22 
   17.23+static int lenwatchpt(Proc *);
   17.24+
   17.25 static int
   17.26 procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
   17.27 {
   17.28@@ -265,6 +269,11 @@ procgen(Chan *c, char *name, Dirtab *tab
   17.29 		}
   17.30 		break;
   17.31 	}
   17.32+	switch(QID(tab->qid)){
   17.33+	case Qwatchpt:
   17.34+		len = lenwatchpt(p);
   17.35+		break;
   17.36+	}
   17.37 
   17.38 	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
   17.39 	devdir(c, qid, tab->name, len, p->user, perm, dp);
   17.40@@ -334,19 +343,22 @@ nonone(Proc *p)
   17.41 	error(Eperm);
   17.42 }
   17.43 
   17.44+static void clearwatchpt(Proc *p);
   17.45+
   17.46 static Chan*
   17.47-procopen(Chan *c, int omode)
   17.48+procopen(Chan *c, int omode0)
   17.49 {
   17.50 	Proc *p;
   17.51 	Pgrp *pg;
   17.52 	Chan *tc;
   17.53 	int pid;
   17.54+	int omode;
   17.55 
   17.56 	if(c->qid.type & QTDIR)
   17.57-		return devopen(c, omode, 0, 0, procgen);
   17.58+		return devopen(c, omode0, 0, 0, procgen);
   17.59 
   17.60 	if(QID(c->qid) == Qtrace){
   17.61-		if (omode != OREAD) 
   17.62+		if (omode0 != OREAD) 
   17.63 			error(Eperm);
   17.64 		lock(&tlock);
   17.65 		if (waserror()){
   17.66@@ -366,7 +378,7 @@ procopen(Chan *c, int omode)
   17.67 		unlock(&tlock);
   17.68 		poperror();
   17.69 
   17.70-		c->mode = openmode(omode);
   17.71+		c->mode = openmode(omode0);
   17.72 		c->flag |= COPEN;
   17.73 		c->offset = 0;
   17.74 		return c;
   17.75@@ -382,7 +394,7 @@ procopen(Chan *c, int omode)
   17.76 	if(p->pid != pid)
   17.77 		error(Eprocdied);
   17.78 
   17.79-	omode = openmode(omode);
   17.80+	omode = openmode(omode0);
   17.81 
   17.82 	switch(QID(c->qid)){
   17.83 	case Qtext:
   17.84@@ -425,6 +437,7 @@ procopen(Chan *c, int omode)
   17.85 	case Qfpregs:
   17.86 	case Qsyscall:	
   17.87 	case Qppid:
   17.88+	case Qwatchpt:
   17.89 		nonone(p);
   17.90 		break;
   17.91 
   17.92@@ -454,6 +467,19 @@ procopen(Chan *c, int omode)
   17.93 		error(Eprocdied);
   17.94 
   17.95 	tc = devopen(c, omode, 0, 0, procgen);
   17.96+	if(waserror()){
   17.97+		cclose(tc);
   17.98+		nexterror();
   17.99+	}
  17.100+	
  17.101+	switch(QID(c->qid)){
  17.102+	case Qwatchpt:
  17.103+		if((omode0 & OTRUNC) != 0)
  17.104+			clearwatchpt(p);
  17.105+		break;
  17.106+	}
  17.107+	
  17.108+	poperror();
  17.109 	qunlock(&p->debug);
  17.110 	poperror();
  17.111 
  17.112@@ -701,6 +727,119 @@ readfd1(Chan *c, Proc *p, char *buf, int
  17.113 }
  17.114 
  17.115 /*
  17.116+ * setupwatchpts(Proc *p, Watchpt *wp, int nwp) is defined for all arches separately.
  17.117+ * It tests whether wp is a valid set of watchpoints and errors out otherwise.
  17.118+ * If and only if they are valid, it sets up all watchpoints (clearing any preexisting ones).
  17.119+ * This is to make sure that failed writes to watchpt don't touch the existing watchpoints.
  17.120+ */
  17.121+
  17.122+static void
  17.123+clearwatchpt(Proc *p)
  17.124+{
  17.125+	setupwatchpts(p, nil, 0);
  17.126+	free(p->watchpt);
  17.127+	p->watchpt = nil;
  17.128+	p->nwatchpt = 0;
  17.129+}
  17.130+
  17.131+static int
  17.132+lenwatchpt(Proc *pr)
  17.133+{
  17.134+	/* careful, not holding debug lock */
  17.135+	return pr->nwatchpt * (10 + 4 * sizeof(uintptr));
  17.136+}
  17.137+
  17.138+static int
  17.139+readwatchpt(Proc *pr, char *buf, int nbuf)
  17.140+{
  17.141+	char *p, *e;
  17.142+	Watchpt *w;
  17.143+	
  17.144+	p = buf;
  17.145+	e = buf + nbuf;
  17.146+	/* careful, length has to match lenwatchpt() */
  17.147+	for(w = pr->watchpt; w < pr->watchpt + pr->nwatchpt; w++)
  17.148+		p = seprint(p, e, sizeof(uintptr) == 8 ? "%c%c%c %#.16p %#.16p\n" : "%c%c%c %#.8p %#.8p\n",
  17.149+			(w->type & WATCHRD) != 0 ? 'r' : '-',
  17.150+			(w->type & WATCHWR) != 0 ? 'w' : '-',
  17.151+			(w->type & WATCHEX) != 0 ? 'x' : '-',
  17.152+			(void *) w->addr, (void *) w->len);
  17.153+	return p - buf;
  17.154+}
  17.155+
  17.156+static int
  17.157+writewatchpt(Proc *pr, char *buf, int nbuf, uvlong offset)
  17.158+{
  17.159+	char *p, *q, *e;
  17.160+	char line[256], *f[4];
  17.161+	Watchpt *wp, *wq;
  17.162+	int rc, nwp, nwp0;
  17.163+	uvlong x;
  17.164+	
  17.165+	p = buf;
  17.166+	e = buf + nbuf;
  17.167+	if(offset != 0)
  17.168+		nwp0 = pr->nwatchpt;
  17.169+	else
  17.170+		nwp0 = 0;
  17.171+	nwp = 0;
  17.172+	for(q = p; q < e; q++)
  17.173+		nwp += *q == '\n';
  17.174+	if(nwp > 65536) error(Egreg);
  17.175+	wp = malloc((nwp0+nwp) * sizeof(Watchpt));
  17.176+	if(wp == nil) error(Enomem);
  17.177+	if(waserror()){
  17.178+		free(wp);
  17.179+		nexterror();
  17.180+	}
  17.181+	if(nwp0 > 0)
  17.182+		memmove(wp, pr->watchpt, sizeof(Watchpt) * nwp0);
  17.183+	for(wq = wp + nwp0;;){
  17.184+		q = memchr(p, '\n', e - p);
  17.185+		if(q == nil)
  17.186+			break;
  17.187+		if(q - p > sizeof(line) - 1)
  17.188+			error("line too long");
  17.189+		memmove(line, p, q - p);
  17.190+		line[q - p] = 0;
  17.191+		p = q + 1;
  17.192+		
  17.193+		rc = tokenize(line, f, nelem(f));
  17.194+		if(rc == 0) continue;
  17.195+		if(rc != 3)
  17.196+			error("wrong number of fields");
  17.197+		for(q = f[0]; *q != 0; q++)
  17.198+			switch(*q){
  17.199+			case 'r': if((wq->type & WATCHRD) != 0) goto tinval; wq->type |= WATCHRD; break;
  17.200+			case 'w': if((wq->type & WATCHWR) != 0) goto tinval; wq->type |= WATCHWR; break;
  17.201+			case 'x': if((wq->type & WATCHEX) != 0) goto tinval; wq->type |= WATCHEX; break;
  17.202+			case '-': break;
  17.203+			default: tinval: error("invalid type");
  17.204+			}
  17.205+		x = strtoull(f[1], &q, 0);
  17.206+		if(f[1] == q || *q != 0 || x != (uintptr) x) error("invalid address");
  17.207+		wq->addr = x;
  17.208+		x = strtoull(f[2], &q, 0);
  17.209+		if(f[2] == q || *q != 0 || x != (uintptr) x) error("invalid length");
  17.210+		wq->len = x;
  17.211+		if(!okaddr(wq->addr, wq->len, 0)) error("bad address");
  17.212+		wq++;
  17.213+	}
  17.214+	nwp = wq - (wp + nwp0);
  17.215+	if(nwp == 0 && nwp0 == pr->nwatchpt){
  17.216+		poperror();
  17.217+		free(wp);
  17.218+		return p - buf;
  17.219+	}
  17.220+	setupwatchpts(pr, wp, nwp0 + nwp);
  17.221+	poperror();
  17.222+	free(pr->watchpt);
  17.223+	pr->watchpt = wp;
  17.224+	pr->nwatchpt = nwp0 + nwp;
  17.225+	return p - buf;
  17.226+}
  17.227+
  17.228+/*
  17.229  * userspace can't pass negative file offset for a
  17.230  * 64 bit kernel address, so we use 63 bit and sign
  17.231  * extend to 64 bit.
  17.232@@ -1006,6 +1145,16 @@ procread(Chan *c, void *va, long n, vlon
  17.233 
  17.234 	case Qppid:
  17.235 		return readnum(offset, va, n, p->parentpid, NUMSIZE);
  17.236+	
  17.237+	case Qwatchpt:
  17.238+		eqlock(&p->debug);
  17.239+		j = readwatchpt(p, statbuf, sizeof(statbuf));
  17.240+		qunlock(&p->debug);
  17.241+		if(offset >= j)
  17.242+			return 0;
  17.243+		if(offset+n > j)
  17.244+			n = j - offset;
  17.245+		goto statbufread;
  17.246 
  17.247 	}
  17.248 	error(Egreg);
  17.249@@ -1125,6 +1274,9 @@ procwrite(Chan *c, void *va, long n, vlo
  17.250 		if(p->noteid != id)
  17.251 			error(Ebadarg);
  17.252 		break;
  17.253+	case Qwatchpt:
  17.254+		writewatchpt(p, va, n, off);
  17.255+		break;
  17.256 	default:
  17.257 		print("unknown qid in procwrite\n");
  17.258 		error(Egreg);
    18.1--- a/sys/src/9/port/portdat.h
    18.2+++ b/sys/src/9/port/portdat.h
    18.3@@ -49,6 +49,7 @@ typedef struct Timers	Timers;
    18.4 typedef struct Uart	Uart;
    18.5 typedef struct Waitq	Waitq;
    18.6 typedef struct Walkqid	Walkqid;
    18.7+typedef struct Watchpt	Watchpt;
    18.8 typedef struct Watchdog	Watchdog;
    18.9 typedef int    Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
   18.10 
   18.11@@ -772,6 +773,9 @@ struct Proc
   18.12 	PMMU;
   18.13 
   18.14 	char	*syscalltrace;	/* syscall trace */
   18.15+	
   18.16+	Watchpt	*watchpt; /* watchpoints */
   18.17+	int nwatchpt;
   18.18 };
   18.19 
   18.20 enum
   18.21@@ -962,6 +966,16 @@ struct Watchdog
   18.22 	void	(*stat)(char*, char*);	/* watchdog statistics */
   18.23 };
   18.24 
   18.25+struct Watchpt
   18.26+{
   18.27+	enum {
   18.28+		WATCHRD = 1,
   18.29+		WATCHWR = 2,
   18.30+		WATCHEX = 4,
   18.31+	} type;
   18.32+	uintptr addr, len;
   18.33+};
   18.34+
   18.35 
   18.36 /* queue state bits,  Qmsg, Qcoalesce, and Qkick can be set in qopen */
   18.37 enum
    19.1--- a/sys/src/9/port/portfns.h
    19.2+++ b/sys/src/9/port/portfns.h
    19.3@@ -319,6 +319,7 @@ void		setmalloctag(void*, uintptr);
    19.4 void		setrealloctag(void*, uintptr);
    19.5 void		setregisters(Ureg*, char*, char*, int);
    19.6 void		setswapchan(Chan*);
    19.7+void		setupwatchpts(Proc*, Watchpt*, int);
    19.8 char*		skipslash(char*);
    19.9 void		sleep(Rendez*, int(*)(void*), void*);
   19.10 void*		smalloc(ulong);
    20.1--- a/sys/src/9/port/proc.c
    20.2+++ b/sys/src/9/port/proc.c
    20.3@@ -1209,6 +1209,10 @@ pexit(char *exitstr, int freemem)
    20.4 		free(up->syscalltrace);
    20.5 		up->syscalltrace = nil;
    20.6 	}
    20.7+	if(up->watchpt != nil){
    20.8+		free(up->watchpt);
    20.9+		up->watchpt = nil;
   20.10+	}
   20.11 	qunlock(&up->debug);
   20.12 
   20.13 	/* Sched must not loop for these locks */
    21.1--- a/sys/src/9/ppc/main.c
    21.2+++ b/sys/src/9/ppc/main.c
    21.3@@ -497,3 +497,10 @@ cistrncmp(char *a, char *b, int n)
    21.4 
    21.5 	return 0;
    21.6 }
    21.7+
    21.8+void
    21.9+setupwatchpts(Proc *, Watchpt *, int n)
   21.10+{
   21.11+	if(n > 0)
   21.12+		error("no watchpoints");
   21.13+}
    22.1--- a/sys/src/9/sgi/main.c
    22.2+++ b/sys/src/9/sgi/main.c
    22.3@@ -497,3 +497,10 @@ confinit(void)
    22.4 	imagmem->maxsize = kpages;
    22.5 //	mainmem->flags |= POOL_PARANOIA;
    22.6 }
    22.7+
    22.8+void
    22.9+setupwatchpts(Proc *, Watchpt *, int n)
   22.10+{
   22.11+	if(n > 0)
   22.12+		error("no watchpoints");
   22.13+}
    23.1--- a/sys/src/9/teg2/main.c
    23.2+++ b/sys/src/9/teg2/main.c
    23.3@@ -907,3 +907,10 @@ wakewfi(void)
    23.4 		intrcpu(cpu);
    23.5 #endif
    23.6 }
    23.7+
    23.8+void
    23.9+setupwatchpts(Proc *, Watchpt *, int n)
   23.10+{
   23.11+	if(n > 0)
   23.12+		error("no watchpoints");
   23.13+}
    24.1--- a/sys/src/9/zynq/main.c
    24.2+++ b/sys/src/9/zynq/main.c
    24.3@@ -427,3 +427,10 @@ main(void)
    24.4 	userinit();
    24.5 	schedinit();
    24.6 }
    24.7+
    24.8+void
    24.9+setupwatchpts(Proc *, Watchpt *, int n)
   24.10+{
   24.11+	if(n > 0)
   24.12+		error("no watchpoints");
   24.13+}