Browse Source

Plan 9 from Bell Labs 2012-08-17

David du Colombier 11 years ago
parent
commit
433fd17b79

+ 63 - 0
sys/man/3/wd

@@ -0,0 +1,63 @@
+.TH WD 3
+.SH NAME
+wd - hardware watchdog timer
+.SH SYNOPSIS
+.nf
+.B bind -a #w /dev
+.sp 0.3v
+.B /dev/wdctl
+.SH DESCRIPTION
+This device presents textual information about hardware watchdog timers
+on PCs and some other machines,
+and allows user-level control of them.
+When enabled, a watchdog timer counts to zero in a few seconds;
+upon reaching zero, it resets the machine with an NMI or equivalent.
+Some process must periodically restart the watchdog to avoid the NMI and
+keep the system alive;
+.IR watchdog (8)
+is one such program.
+.PP
+Reads from
+.I wdctl
+yield data of this form:
+.IP
+.EX
+enabled 3 restarts
+.EE
+.LP
+or
+.IP
+.EX
+disabled 3 restarts
+.EE
+.LP
+Control messages may be written to
+.I wdctl
+and include
+.LR enable ,
+.LR disable ,
+and
+.LR restart .
+.LP
+The watchdog is disabled at system shutdown, and when the last open
+file descriptor for
+.I wdctl
+is closed.
+.SH SOURCE
+.B /sys/src/9/port/devwd.c
+.br
+.B /sys/src/9/*/*watchdog.c
+.SH SEE ALSO
+.IR proc (3),
+.IR watchdog (8)
+.SH BUGS
+On PCs,
+to ensure consistent use of one CPU's timers,
+an
+.L enable
+message wires the issuing process
+(see
+.IR proc (3))
+to a CPU,
+and any subsequent control messages will
+wire the issuing processes to that same CPU.

+ 18 - 0
sys/man/8/watchdog

@@ -0,0 +1,18 @@
+.TH WATCHDOG 8
+.SH NAME
+watchdog \- reset the system if it gets stuck
+.SH SYNOPSIS
+.B aux/watchdog
+.SH DESCRIPTION
+.I Watchdog
+writes
+.L restart
+to
+.L #w/wdctl
+at least once per second.
+If the system gets stuck, the hardware watchdog will reset the system via NMI.
+.SH SOURCE
+.B /sys/src/cmd/aux/watchdog.c
+.SH SEE ALSO
+.IR wd (3),
+.IR reboot (8)

+ 16 - 4
sys/src/9/pc/pc

@@ -1,3 +1,4 @@
+# pc - a normal pc terminal
 dev
 	root
 	cons
@@ -36,11 +37,16 @@ dev
 	uart
 	usb
 
+	wd
 
 link
 	realmode
 	devpccard
 	devi82365
+
+# order of ethernet drivers should match that in ../pcboot/boot so that
+# devices are detected in the same order by bootstraps and kernels
+# and thus given the same controller numbers.
 	ether2000	ether8390
 	ether2114x	pci
 	ether589	etherelnk3
@@ -48,7 +54,8 @@ link
 	ether8003	ether8390
 	ether8139	pci
 	ether8169	pci ethermii
-#	ether82543gc	pci
+# should be obsoleted by igbe
+	ether82543gc	pci
 	ether82563	pci
 	ether82557	pci
 	ether83815	pci
@@ -64,17 +71,21 @@ link
 	ethersmc	devi82365 cis
 	etherwavelan	wavelan devi82365 cis pci
 	ethermedium
-#	etherm10g
+	etherm10g
 	ether82598	pci
+
 	pcmciamodem
 	netdevmedium
 	loopbackmedium
+
 	usbuhci
 	usbohci
 	usbehci		usbehcipc
 
+	x86watchdog
+
 misc
-	archmp		mp apic
+	archmp		mp apic mpacpi
 	mtrr
 
 	sdata		pci sdscsi
@@ -101,6 +112,7 @@ misc
 	vgamga4xx	+cur
 	vganeomagic	+cur
 	vganvidia	+cur
+	vgaradeon	+cur
 	vgargb524	=cur
 	vgas3		+cur vgasavage
 	vgat2r4		+cur
@@ -126,7 +138,7 @@ boot
 	tcp
 
 bootdir
-	bootpc.out boot
+	boot$CONF.out boot
 	/386/bin/ip/ipconfig
 	/386/bin/auth/factotum
 	/386/bin/usb/usbd

+ 17 - 7
sys/src/9/pc/pccpu

@@ -22,49 +22,59 @@ dev
 
 	ether		netif
 	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno
+	kbmap
+	kbin
 
 	sd
 	floppy		dma
 	aoe
 
+	audio		dma
 	uart
 	usb
-	kbin
-	kbmap
-	audio
 
 	wd
 
 link
 	realmode
+
+# order of ethernet drivers should match that in ../pcboot/boot so that
+# devices are detected in the same order by bootstraps and kernels
+# and thus given the same controller numbers.
 	ether2000	ether8390
 	ether2114x	pci
+	ether589	etherelnk3
 	ether79c970	pci
 	ether8003	ether8390
 	ether8139	pci
 	ether8169	pci ethermii
 # should be obsoleted by igbe
-#	ether82543gc	pci
+	ether82543gc	pci
 	ether82563	pci
 	ether82557	pci
 	ether83815	pci
 	etherdp83820	pci
+	etherec2t	ether8390
 	etherelnk3	pci
 	etherga620	pci
 	etherigbe	pci ethermii
 	ethervgbe	pci ethermii
 	ethervt6102	pci ethermii
 	ethervt6105m	pci ethermii
-	etherm10g	pci ethermii
-	ether82598	pci
 	ethersink
+	ethersmc	devi82365 cis
+	etherwavelan	wavelan devi82365 cis pci
 	ethermedium
+	etherm10g
+	ether82598	pci
+
 	loopbackmedium
+
 	usbuhci
 	usbohci
 	usbehci		usbehcipc
 
-#	x86watchdog
+	x86watchdog
 
 misc
 	archmp		mp apic mpacpi

+ 277 - 0
sys/src/9/pc/x86watchdog.c

@@ -0,0 +1,277 @@
+/*
+ * simulate independent hardware watch-dog timer
+ * using local cpu timers and NMIs, one watch-dog per system.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "mp.h"
+
+typedef struct Wd Wd;
+struct Wd {
+	Lock;
+	int	model;
+	int	inuse;
+	uint	ticks;
+};
+
+static Wd x86wd;
+
+enum {
+	P6		= 0,			/* Pentium Pro/II/III */
+	P4		= 1,			/* P4 */
+	K6		= 2,			/* Athlon */
+	K8		= 3,			/* AMD64 */
+
+	Twogigs		= 1ul << 31,
+};
+
+/*
+ * return an interval in cycles of about a second, or as long as
+ * will fit in 31 bits.
+ */
+static long
+interval(void)
+{
+	if (m->cpuhz > Twogigs - 1)
+		return Twogigs - 1;
+	else
+		return m->cpuhz;
+}
+
+static void
+runoncpu(int cpu)
+{
+	while (m->machno != cpu) {
+		procwired(up, cpu);
+		sched();
+	}
+}
+
+static void
+x86wdenable(void)
+{
+	Wd *wd;
+	vlong r, t;
+	int i, model;
+	u32int evntsel;
+
+	wd = &x86wd;
+	lock(wd);
+	if(wd->inuse){
+		unlock(wd);
+		error(Einuse);
+	}
+	unlock(wd);
+
+	/*
+	 * keep this process on cpu 0 so we always see the same timers
+	 * and so that this will work even if all other cpus are shut down.
+	 */
+	runoncpu(0);
+
+	/*
+	 * Check the processor is capable of doing performance
+	 * monitoring and that it has TSC, RDMSR/WRMSR and a local APIC.
+	 */
+	model = -1;
+	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0){
+		if(X86FAMILY(m->cpuidax) == 0x06)
+			model = K6;
+		else if(X86FAMILY(m->cpuidax) == 0x0F)
+			model = K8;
+	}
+	else if(strncmp(m->cpuidid, "GenuineIntel", 12) == 0){
+		if(X86FAMILY(m->cpuidax) == 0x06)
+			model = P6;
+		else if(X86FAMILY(m->cpuidax) == 0x0F)
+			model = P4;
+	}
+	if(model == -1 ||
+	    (m->cpuiddx & (Cpuapic|Cpumsr|Tsc)) != (Cpuapic|Cpumsr|Tsc))
+		error(Enodev);
+
+	lock(wd);
+	if(wd->inuse){
+		unlock(wd);
+		error(Einuse);
+	}
+	wd->model = model;
+	wd->inuse = 1;
+	wd->ticks = 0;
+
+	/*
+	 * See the IA-32 Intel Architecture Software
+	 * Developer's Manual Volume 3: System Programming Guide,
+	 * Chapter 15 and the AMD equivalent for what all this
+	 * bit-whacking means.
+	 */
+	t = interval();
+	switch(model){
+	case P6:
+		wrmsr(0x186, 0);			/* evntsel */
+		wrmsr(0x187, 0);
+		wrmsr(0xC1, 0);				/* perfctr */
+		wrmsr(0xC2, 0);
+
+		lapicnmienable();
+	
+		evntsel = 0x00130000|0x79;
+		wrmsr(0xC1, -t);
+		wrmsr(0x186, 0x00400000|evntsel);
+		break;
+	case P4:
+		rdmsr(0x1A0, &r);
+		if(!(r & 0x0000000000000080LL))
+			return;
+	
+		for(i = 0; i < 18; i++)
+			wrmsr(0x300+i, 0);		/* perfctr */
+		for(i = 0; i < 18; i++)
+			wrmsr(0x360+i, 0);		/* ccr */
+	
+		for(i = 0; i < 31; i++)
+			wrmsr(0x3A0+i, 0);		/* escr */
+		for(i = 0; i < 6; i++)
+			wrmsr(0x3C0+i, 0);		/* escr */
+		for(i = 0; i < 6; i++)
+			wrmsr(0x3C8+i, 0);		/* escr */
+		for(i = 0; i < 2; i++)
+			wrmsr(0x3E0+i, 0);		/* escr */
+	
+		if(!(r & 0x0000000000001000LL)){
+			for(i = 0; i < 2; i++)
+				wrmsr(0x3F1+i, 0);	/* pebs */
+		}
+	
+		lapicnmienable();
+	
+		wrmsr(0x3B8, 0x000000007E00000CLL);	/* escr0 */
+		r = 0x0000000004FF8000ULL;
+		wrmsr(0x36C, r);			/* cccr0 */
+		wrmsr(0x30C, -t);
+		wrmsr(0x36C, 0x0000000000001000LL|r);
+		break;
+	case K6:
+	case K8:
+		/*
+		 * PerfEvtSel 0-3, PerfCtr 0-4.
+		 */
+		for(i = 0; i < 8; i++)
+			wrmsr(0xC0010000+i, 0);
+	
+		lapicnmienable();
+	
+		evntsel = 0x00130000|0x76;
+		wrmsr(0xC0010004, -t);
+		wrmsr(0xC0010000, 0x00400000|evntsel);
+		break;
+	}
+	unlock(wd);
+}
+
+static void
+x86wddisable(void)
+{
+	Wd *wd;
+
+	wd = &x86wd;
+	lock(wd);
+	if(!wd->inuse){
+		/*
+		 * Can't error, called at boot by addwatchdog().
+		 */
+		unlock(wd);
+		return;
+	}
+	unlock(wd);
+
+	runoncpu(0);
+
+	lock(wd);
+	lapicnmidisable();
+	switch(wd->model){
+	case P6:
+		wrmsr(0x186, 0);
+		break;
+	case P4:
+		wrmsr(0x36C, 0);			/* cccr0 */
+		wrmsr(0x3B8, 0);			/* escr0 */
+		break;
+	case K6:
+	case K8:
+		wrmsr(0xC0010000, 0);
+		break;
+	}
+	wd->inuse = 0;
+	unlock(wd);
+}
+
+static void
+x86wdrestart(void)
+{
+	Wd *wd;
+	vlong r, t;
+
+	runoncpu(0);
+	t = interval();
+
+	wd = &x86wd;
+	lock(wd);
+	switch(wd->model){
+	case P6:
+		wrmsr(0xC1, -t);
+		break;
+	case P4:
+		r = 0x0000000004FF8000LL;
+		wrmsr(0x36C, r);
+		lapicnmienable();
+		wrmsr(0x30C, -t);
+		wrmsr(0x36C, 0x0000000000001000LL|r);
+		break;
+	case K6:
+	case K8:
+		wrmsr(0xC0010004, -t);
+		break;
+	}
+	wd->ticks++;
+	unlock(wd);
+}
+
+void
+x86wdstat(char* p, char* ep)
+{
+	Wd *wd;
+	int inuse;
+	uint ticks;
+
+	wd = &x86wd;
+	lock(wd);
+	inuse = wd->inuse;
+	ticks = wd->ticks;
+	unlock(wd);
+
+	if(inuse)
+		seprint(p, ep, "enabled %ud restarts\n", ticks);
+	else
+		seprint(p, ep, "disabled %ud restarts\n", ticks);
+}
+
+Watchdog x86watchdog = {
+	x86wdenable,
+	x86wddisable,
+	x86wdrestart,
+	x86wdstat,
+};
+
+void
+x86watchdoglink(void)
+{
+	addwatchdog(&x86watchdog);
+}

+ 16 - 3
sys/src/9/port/devwd.c

@@ -15,6 +15,7 @@ enum {
 };
 
 static Watchdog *wd;
+static Ref refs;
 static Dirtab wddir[] = {
 	".",		{ Qdir, 0, QTDIR },	0,		0555,
 	"wdctl",	{ Qwdctl, 0 },		0,		0664,
@@ -54,12 +55,24 @@ wdstat(Chan *c, uchar *dp, int n)
 static Chan*
 wdopen(Chan* c, int omode)
 {
-	return devopen(c, omode, wddir, nelem(wddir), devgen);
+	c = devopen(c, omode, wddir, nelem(wddir), devgen);
+	if (c->qid.path == Qwdctl)
+		incref(&refs);
+	return c;
 }
 
 static void
-wdclose(Chan*)
+wdclose(Chan *c)
 {
+	if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0 && wd)
+		wd->disable();
+}
+
+static void
+wdshutdown(void)
+{
+	if (wd)
+		wd->disable();
 }
 
 static long
@@ -141,7 +154,7 @@ Dev wddevtab = {
 
 	devreset,
 	devinit,
-	devshutdown,
+	wdshutdown,
 	wdattach,
 	wdwalk,
 	wdstat,

+ 1 - 1
sys/src/9/ppc/trap.c

@@ -644,7 +644,7 @@ syscall(Ureg* ureg)
 	ret = -1;
 	if(!waserror()){
 		if(scallnr >= nsyscall || systab[scallnr] == nil){
-			pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc);
+			pprint("bad sys call number %ld pc %lux\n", scallnr, ureg->pc);
 			postnote(up, 1, "sys: bad sys call", NDebug);
 			error(Ebadarg);
 		}

+ 1 - 0
sys/src/cmd/aux/mkfile

@@ -34,6 +34,7 @@ TARG=\
 	timesync\
 	trampoline\
 	usage\
+	watchdog\
 	write\
 	zerotrunc\
 

+ 48 - 0
sys/src/cmd/aux/watchdog.c

@@ -0,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+
+static int wdog;
+
+int
+procctl(int pid)
+{
+	int ctlfd;
+	char *ctl;
+
+	ctl = smprint("/proc/%d/ctl", pid);
+	ctlfd = open(ctl, OWRITE);
+	if (ctlfd < 0)
+		sysfatal("open %s: %r", ctl);
+	free(ctl);
+	return ctlfd;
+}
+
+void
+main(int, char **)
+{
+	int ctl;
+
+	wdog = open("#w/wdctl", ORDWR);
+	if (wdog < 0)
+		sysfatal("open #w/wdctl: %r");
+
+	switch(rfork(RFPROC|RFNOWAIT|RFFDG)){
+	case 0:
+		break;
+	default:
+		exits(0);
+	}
+
+	ctl = procctl(getpid());
+	fprint(ctl, "pri 18");
+	close(ctl);
+
+	if (fprint(wdog, "enable") < 0)
+		sysfatal("write #w/wdctl: %r");
+	for(;;){
+		sleep(300);		/* allows 4.2GHz CPU, with some slop */
+		seek(wdog, 0, 0);
+		if (fprint(wdog, "restart") < 0)
+			sysfatal("write #w/wdctl: %r");
+	}
+}