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
interval(void)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
runoncpu(int cpu)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
x86wdenable(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
x86wddisable(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
x86wdrestart(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
x86wdstat(char * p,char * ep)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
x86watchdoglink(void)279 x86watchdoglink(void)
280 {
281 addwatchdog(&x86watchdog);
282 }
283