Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * simulate independent hardware watch-dog timer
3
 * using local cpu timers and NMIs, one watch-dog per system.
4
 */
5
#include "u.h"
6
#include "../port/lib.h"
7
#include "mem.h"
8
#include "dat.h"
9
#include "fns.h"
10
#include "io.h"
11
#include "../port/error.h"
12
#include "../port/netif.h"
13
 
14
#include "mp.h"
15
 
16
typedef struct Wd Wd;
17
struct Wd {
18
	Lock;
19
	int	model;
20
	int	inuse;
21
	uint	ticks;
22
};
23
 
24
static Wd x86wd;
25
 
26
enum {
27
	P6		= 0,			/* Pentium Pro/II/III */
28
	P4		= 1,			/* P4 */
29
	K6		= 2,			/* Athlon */
30
	K8		= 3,			/* AMD64 */
31
 
32
	Twogigs		= 1ul << 31,
33
};
34
 
35
/*
36
 * return an interval in cycles of about a second, or as long as
37
 * will fit in 31 bits.
38
 */
39
static long
40
interval(void)
41
{
42
	if (m->cpuhz > Twogigs - 1)
43
		return Twogigs - 1;
44
	else
45
		return m->cpuhz;
46
}
47
 
48
static void
49
runoncpu(int cpu)
50
{
51
	if (m->machno != cpu) {
52
		if (up == nil)
53
			panic("x86watchdog: nil up");
54
		procwired(up, cpu);
55
		sched();
56
		if (m->machno != cpu)
57
			panic("x86watchdog: runoncpu: can't switch to cpu%d",
58
				cpu);
59
	}
60
}
61
 
62
static void
63
x86wdenable(void)
64
{
65
	Wd *wd;
66
	vlong r, t;
67
	int i, model;
68
	u32int evntsel;
69
 
70
	wd = &x86wd;
71
	ilock(wd);
72
	if(wd->inuse){
73
		iunlock(wd);
74
		error(Einuse);
75
	}
76
	iunlock(wd);
77
 
78
	/*
79
	 * keep this process on cpu 0 so we always see the same timers
80
	 * and so that this will work even if all other cpus are shut down.
81
	 */
82
	runoncpu(0);
83
 
84
	/*
85
	 * Check the processor is capable of doing performance
86
	 * monitoring and that it has TSC, RDMSR/WRMSR and a local APIC.
87
	 */
88
	model = -1;
89
	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0){
90
		if(X86FAMILY(m->cpuidax) == 0x06)
91
			model = K6;
92
		else if(X86FAMILY(m->cpuidax) == 0x0F)
93
			model = K8;
94
	}
95
	else if(strncmp(m->cpuidid, "GenuineIntel", 12) == 0){
96
		if(X86FAMILY(m->cpuidax) == 0x06)
97
			model = P6;
98
		else if(X86FAMILY(m->cpuidax) == 0x0F)
99
			model = P4;
100
	}
101
	if(model == -1 ||
102
	    (m->cpuiddx & (Cpuapic|Cpumsr|Tsc)) != (Cpuapic|Cpumsr|Tsc))
103
		error(Enodev);
104
 
105
	ilock(wd);
106
	if(wd->inuse){
107
		iunlock(wd);
108
		error(Einuse);
109
	}
110
	wd->model = model;
111
	wd->inuse = 1;
112
	wd->ticks = 0;
113
 
114
	/*
115
	 * See the IA-32 Intel Architecture Software
116
	 * Developer's Manual Volume 3: System Programming Guide,
117
	 * Chapter 15 and the AMD equivalent for what all this
118
	 * bit-whacking means.
119
	 */
120
	t = interval();
121
	switch(model){
122
	case P6:
123
		wrmsr(0x186, 0);			/* evntsel */
124
		wrmsr(0x187, 0);
125
		wrmsr(0xC1, 0);				/* perfctr */
126
		wrmsr(0xC2, 0);
127
 
128
		lapicnmienable();
129
 
130
		evntsel = 0x00130000|0x79;
131
		wrmsr(0xC1, -t);
132
		wrmsr(0x186, 0x00400000|evntsel);
133
		break;
134
	case P4:
135
		rdmsr(0x1A0, &r);
136
		if(!(r & 0x0000000000000080LL))
137
			return;
138
 
139
		for(i = 0; i < 18; i++)
140
			wrmsr(0x300+i, 0);		/* perfctr */
141
		for(i = 0; i < 18; i++)
142
			wrmsr(0x360+i, 0);		/* ccr */
143
 
144
		for(i = 0; i < 31; i++)
145
			wrmsr(0x3A0+i, 0);		/* escr */
146
		for(i = 0; i < 6; i++)
147
			wrmsr(0x3C0+i, 0);		/* escr */
148
		for(i = 0; i < 6; i++)
149
			wrmsr(0x3C8+i, 0);		/* escr */
150
		for(i = 0; i < 2; i++)
151
			wrmsr(0x3E0+i, 0);		/* escr */
152
 
153
		if(!(r & 0x0000000000001000LL)){
154
			for(i = 0; i < 2; i++)
155
				wrmsr(0x3F1+i, 0);	/* pebs */
156
		}
157
 
158
		lapicnmienable();
159
 
160
		wrmsr(0x3B8, 0x000000007E00000CLL);	/* escr0 */
161
		r = 0x0000000004FF8000ULL;
162
		wrmsr(0x36C, r);			/* cccr0 */
163
		wrmsr(0x30C, -t);
164
		wrmsr(0x36C, 0x0000000000001000LL|r);
165
		break;
166
	case K6:
167
	case K8:
168
		/*
169
		 * PerfEvtSel 0-3, PerfCtr 0-4.
170
		 */
171
		for(i = 0; i < 8; i++)
172
			wrmsr(0xC0010000+i, 0);
173
 
174
		lapicnmienable();
175
 
176
		evntsel = 0x00130000|0x76;
177
		wrmsr(0xC0010004, -t);
178
		wrmsr(0xC0010000, 0x00400000|evntsel);
179
		break;
180
	}
181
	iunlock(wd);
182
}
183
 
184
static void
185
x86wddisable(void)
186
{
187
	Wd *wd;
188
 
189
	wd = &x86wd;
190
	ilock(wd);
191
	if(!wd->inuse){
192
		/*
193
		 * Can't error, called at boot by addwatchdog().
194
		 */
195
		iunlock(wd);
196
		return;
197
	}
198
	iunlock(wd);
199
 
200
	runoncpu(0);
201
 
202
	ilock(wd);
203
	lapicnmidisable();
204
	switch(wd->model){
205
	case P6:
206
		wrmsr(0x186, 0);
207
		break;
208
	case P4:
209
		wrmsr(0x36C, 0);			/* cccr0 */
210
		wrmsr(0x3B8, 0);			/* escr0 */
211
		break;
212
	case K6:
213
	case K8:
214
		wrmsr(0xC0010000, 0);
215
		break;
216
	}
217
	wd->inuse = 0;
218
	iunlock(wd);
219
}
220
 
221
static void
222
x86wdrestart(void)
223
{
224
	Wd *wd;
225
	vlong r, t;
226
 
227
	runoncpu(0);
228
	t = interval();
229
 
230
	wd = &x86wd;
231
	ilock(wd);
232
	switch(wd->model){
233
	case P6:
234
		wrmsr(0xC1, -t);
235
		break;
236
	case P4:
237
		r = 0x0000000004FF8000LL;
238
		wrmsr(0x36C, r);
239
		lapicnmienable();
240
		wrmsr(0x30C, -t);
241
		wrmsr(0x36C, 0x0000000000001000LL|r);
242
		break;
243
	case K6:
244
	case K8:
245
		wrmsr(0xC0010004, -t);
246
		break;
247
	}
248
	wd->ticks++;
249
	iunlock(wd);
250
}
251
 
252
void
253
x86wdstat(char* p, char* ep)
254
{
255
	Wd *wd;
256
	int inuse;
257
	uint ticks;
258
 
259
	wd = &x86wd;
260
	ilock(wd);
261
	inuse = wd->inuse;
262
	ticks = wd->ticks;
263
	iunlock(wd);
264
 
265
	if(inuse)
266
		seprint(p, ep, "enabled %ud restarts\n", ticks);
267
	else
268
		seprint(p, ep, "disabled %ud restarts\n", ticks);
269
}
270
 
271
Watchdog x86watchdog = {
272
	x86wdenable,
273
	x86wddisable,
274
	x86wdrestart,
275
	x86wdstat,
276
};
277
 
278
void
279
x86watchdoglink(void)
280
{
281
	addwatchdog(&x86watchdog);
282
}