1606e0002SDavid du Colombier /*
2606e0002SDavid du Colombier * simulate independent hardware watch-dog timer
3606e0002SDavid du Colombier * using local cpu timers and NMIs, one watch-dog per system.
4606e0002SDavid du Colombier */
5606e0002SDavid du Colombier #include "u.h"
6606e0002SDavid du Colombier #include "../port/lib.h"
7606e0002SDavid du Colombier #include "mem.h"
8606e0002SDavid du Colombier #include "dat.h"
9606e0002SDavid du Colombier #include "fns.h"
10606e0002SDavid du Colombier #include "io.h"
11606e0002SDavid du Colombier #include "../port/error.h"
12606e0002SDavid du Colombier #include "../port/netif.h"
13606e0002SDavid du Colombier
14606e0002SDavid du Colombier #include "mp.h"
15606e0002SDavid du Colombier
16606e0002SDavid du Colombier typedef struct Wd Wd;
17606e0002SDavid du Colombier struct Wd {
18606e0002SDavid du Colombier Lock;
19606e0002SDavid du Colombier int model;
20606e0002SDavid du Colombier int inuse;
21606e0002SDavid du Colombier uint ticks;
22606e0002SDavid du Colombier };
23606e0002SDavid du Colombier
24606e0002SDavid du Colombier static Wd x86wd;
25606e0002SDavid du Colombier
26606e0002SDavid du Colombier enum {
27606e0002SDavid du Colombier P6 = 0, /* Pentium Pro/II/III */
28606e0002SDavid du Colombier P4 = 1, /* P4 */
29606e0002SDavid du Colombier K6 = 2, /* Athlon */
30606e0002SDavid du Colombier K8 = 3, /* AMD64 */
31606e0002SDavid du Colombier
32606e0002SDavid du Colombier Twogigs = 1ul << 31,
33606e0002SDavid du Colombier };
34606e0002SDavid du Colombier
35606e0002SDavid du Colombier /*
36606e0002SDavid du Colombier * return an interval in cycles of about a second, or as long as
37606e0002SDavid du Colombier * will fit in 31 bits.
38606e0002SDavid du Colombier */
39606e0002SDavid du Colombier static long
interval(void)40606e0002SDavid du Colombier interval(void)
41606e0002SDavid du Colombier {
42606e0002SDavid du Colombier if (m->cpuhz > Twogigs - 1)
43606e0002SDavid du Colombier return Twogigs - 1;
44606e0002SDavid du Colombier else
45606e0002SDavid du Colombier return m->cpuhz;
46606e0002SDavid du Colombier }
47606e0002SDavid du Colombier
48606e0002SDavid du Colombier static void
runoncpu(int cpu)49606e0002SDavid du Colombier runoncpu(int cpu)
50606e0002SDavid du Colombier {
51f54a2a50SDavid du Colombier if (m->machno != cpu) {
52f54a2a50SDavid du Colombier if (up == nil)
53f54a2a50SDavid du Colombier panic("x86watchdog: nil up");
54606e0002SDavid du Colombier procwired(up, cpu);
55606e0002SDavid du Colombier sched();
56f54a2a50SDavid du Colombier if (m->machno != cpu)
57f54a2a50SDavid du Colombier panic("x86watchdog: runoncpu: can't switch to cpu%d",
58f54a2a50SDavid du Colombier cpu);
59606e0002SDavid du Colombier }
60606e0002SDavid du Colombier }
61606e0002SDavid du Colombier
62606e0002SDavid du Colombier static void
x86wdenable(void)63606e0002SDavid du Colombier x86wdenable(void)
64606e0002SDavid du Colombier {
65606e0002SDavid du Colombier Wd *wd;
66606e0002SDavid du Colombier vlong r, t;
67606e0002SDavid du Colombier int i, model;
68606e0002SDavid du Colombier u32int evntsel;
69606e0002SDavid du Colombier
70606e0002SDavid du Colombier wd = &x86wd;
71*86abb9fbSDavid du Colombier ilock(wd);
72606e0002SDavid du Colombier if(wd->inuse){
73*86abb9fbSDavid du Colombier iunlock(wd);
74606e0002SDavid du Colombier error(Einuse);
75606e0002SDavid du Colombier }
76*86abb9fbSDavid du Colombier iunlock(wd);
77606e0002SDavid du Colombier
78606e0002SDavid du Colombier /*
79606e0002SDavid du Colombier * keep this process on cpu 0 so we always see the same timers
80606e0002SDavid du Colombier * and so that this will work even if all other cpus are shut down.
81606e0002SDavid du Colombier */
82606e0002SDavid du Colombier runoncpu(0);
83606e0002SDavid du Colombier
84606e0002SDavid du Colombier /*
85606e0002SDavid du Colombier * Check the processor is capable of doing performance
86606e0002SDavid du Colombier * monitoring and that it has TSC, RDMSR/WRMSR and a local APIC.
87606e0002SDavid du Colombier */
88606e0002SDavid du Colombier model = -1;
89606e0002SDavid du Colombier if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0){
90606e0002SDavid du Colombier if(X86FAMILY(m->cpuidax) == 0x06)
91606e0002SDavid du Colombier model = K6;
92606e0002SDavid du Colombier else if(X86FAMILY(m->cpuidax) == 0x0F)
93606e0002SDavid du Colombier model = K8;
94606e0002SDavid du Colombier }
95606e0002SDavid du Colombier else if(strncmp(m->cpuidid, "GenuineIntel", 12) == 0){
96606e0002SDavid du Colombier if(X86FAMILY(m->cpuidax) == 0x06)
97606e0002SDavid du Colombier model = P6;
98606e0002SDavid du Colombier else if(X86FAMILY(m->cpuidax) == 0x0F)
99606e0002SDavid du Colombier model = P4;
100606e0002SDavid du Colombier }
101606e0002SDavid du Colombier if(model == -1 ||
102606e0002SDavid du Colombier (m->cpuiddx & (Cpuapic|Cpumsr|Tsc)) != (Cpuapic|Cpumsr|Tsc))
103606e0002SDavid du Colombier error(Enodev);
104606e0002SDavid du Colombier
105*86abb9fbSDavid du Colombier ilock(wd);
106606e0002SDavid du Colombier if(wd->inuse){
107*86abb9fbSDavid du Colombier iunlock(wd);
108606e0002SDavid du Colombier error(Einuse);
109606e0002SDavid du Colombier }
110606e0002SDavid du Colombier wd->model = model;
111606e0002SDavid du Colombier wd->inuse = 1;
112606e0002SDavid du Colombier wd->ticks = 0;
113606e0002SDavid du Colombier
114606e0002SDavid du Colombier /*
115606e0002SDavid du Colombier * See the IA-32 Intel Architecture Software
116606e0002SDavid du Colombier * Developer's Manual Volume 3: System Programming Guide,
117606e0002SDavid du Colombier * Chapter 15 and the AMD equivalent for what all this
118606e0002SDavid du Colombier * bit-whacking means.
119606e0002SDavid du Colombier */
120606e0002SDavid du Colombier t = interval();
121606e0002SDavid du Colombier switch(model){
122606e0002SDavid du Colombier case P6:
123606e0002SDavid du Colombier wrmsr(0x186, 0); /* evntsel */
124606e0002SDavid du Colombier wrmsr(0x187, 0);
125606e0002SDavid du Colombier wrmsr(0xC1, 0); /* perfctr */
126606e0002SDavid du Colombier wrmsr(0xC2, 0);
127606e0002SDavid du Colombier
128606e0002SDavid du Colombier lapicnmienable();
129606e0002SDavid du Colombier
130606e0002SDavid du Colombier evntsel = 0x00130000|0x79;
131606e0002SDavid du Colombier wrmsr(0xC1, -t);
132606e0002SDavid du Colombier wrmsr(0x186, 0x00400000|evntsel);
133606e0002SDavid du Colombier break;
134606e0002SDavid du Colombier case P4:
135606e0002SDavid du Colombier rdmsr(0x1A0, &r);
136606e0002SDavid du Colombier if(!(r & 0x0000000000000080LL))
137606e0002SDavid du Colombier return;
138606e0002SDavid du Colombier
139606e0002SDavid du Colombier for(i = 0; i < 18; i++)
140606e0002SDavid du Colombier wrmsr(0x300+i, 0); /* perfctr */
141606e0002SDavid du Colombier for(i = 0; i < 18; i++)
142606e0002SDavid du Colombier wrmsr(0x360+i, 0); /* ccr */
143606e0002SDavid du Colombier
144606e0002SDavid du Colombier for(i = 0; i < 31; i++)
145606e0002SDavid du Colombier wrmsr(0x3A0+i, 0); /* escr */
146606e0002SDavid du Colombier for(i = 0; i < 6; i++)
147606e0002SDavid du Colombier wrmsr(0x3C0+i, 0); /* escr */
148606e0002SDavid du Colombier for(i = 0; i < 6; i++)
149606e0002SDavid du Colombier wrmsr(0x3C8+i, 0); /* escr */
150606e0002SDavid du Colombier for(i = 0; i < 2; i++)
151606e0002SDavid du Colombier wrmsr(0x3E0+i, 0); /* escr */
152606e0002SDavid du Colombier
153606e0002SDavid du Colombier if(!(r & 0x0000000000001000LL)){
154606e0002SDavid du Colombier for(i = 0; i < 2; i++)
155606e0002SDavid du Colombier wrmsr(0x3F1+i, 0); /* pebs */
156606e0002SDavid du Colombier }
157606e0002SDavid du Colombier
158606e0002SDavid du Colombier lapicnmienable();
159606e0002SDavid du Colombier
160606e0002SDavid du Colombier wrmsr(0x3B8, 0x000000007E00000CLL); /* escr0 */
161606e0002SDavid du Colombier r = 0x0000000004FF8000ULL;
162606e0002SDavid du Colombier wrmsr(0x36C, r); /* cccr0 */
163606e0002SDavid du Colombier wrmsr(0x30C, -t);
164606e0002SDavid du Colombier wrmsr(0x36C, 0x0000000000001000LL|r);
165606e0002SDavid du Colombier break;
166606e0002SDavid du Colombier case K6:
167606e0002SDavid du Colombier case K8:
168606e0002SDavid du Colombier /*
169606e0002SDavid du Colombier * PerfEvtSel 0-3, PerfCtr 0-4.
170606e0002SDavid du Colombier */
171606e0002SDavid du Colombier for(i = 0; i < 8; i++)
172606e0002SDavid du Colombier wrmsr(0xC0010000+i, 0);
173606e0002SDavid du Colombier
174606e0002SDavid du Colombier lapicnmienable();
175606e0002SDavid du Colombier
176606e0002SDavid du Colombier evntsel = 0x00130000|0x76;
177606e0002SDavid du Colombier wrmsr(0xC0010004, -t);
178606e0002SDavid du Colombier wrmsr(0xC0010000, 0x00400000|evntsel);
179606e0002SDavid du Colombier break;
180606e0002SDavid du Colombier }
181*86abb9fbSDavid du Colombier iunlock(wd);
182606e0002SDavid du Colombier }
183606e0002SDavid du Colombier
184606e0002SDavid du Colombier static void
x86wddisable(void)185606e0002SDavid du Colombier x86wddisable(void)
186606e0002SDavid du Colombier {
187606e0002SDavid du Colombier Wd *wd;
188606e0002SDavid du Colombier
189606e0002SDavid du Colombier wd = &x86wd;
190*86abb9fbSDavid du Colombier ilock(wd);
191606e0002SDavid du Colombier if(!wd->inuse){
192606e0002SDavid du Colombier /*
193606e0002SDavid du Colombier * Can't error, called at boot by addwatchdog().
194606e0002SDavid du Colombier */
195*86abb9fbSDavid du Colombier iunlock(wd);
196606e0002SDavid du Colombier return;
197606e0002SDavid du Colombier }
198*86abb9fbSDavid du Colombier iunlock(wd);
199606e0002SDavid du Colombier
200606e0002SDavid du Colombier runoncpu(0);
201606e0002SDavid du Colombier
202*86abb9fbSDavid du Colombier ilock(wd);
203606e0002SDavid du Colombier lapicnmidisable();
204606e0002SDavid du Colombier switch(wd->model){
205606e0002SDavid du Colombier case P6:
206606e0002SDavid du Colombier wrmsr(0x186, 0);
207606e0002SDavid du Colombier break;
208606e0002SDavid du Colombier case P4:
209606e0002SDavid du Colombier wrmsr(0x36C, 0); /* cccr0 */
210606e0002SDavid du Colombier wrmsr(0x3B8, 0); /* escr0 */
211606e0002SDavid du Colombier break;
212606e0002SDavid du Colombier case K6:
213606e0002SDavid du Colombier case K8:
214606e0002SDavid du Colombier wrmsr(0xC0010000, 0);
215606e0002SDavid du Colombier break;
216606e0002SDavid du Colombier }
217606e0002SDavid du Colombier wd->inuse = 0;
218*86abb9fbSDavid du Colombier iunlock(wd);
219606e0002SDavid du Colombier }
220606e0002SDavid du Colombier
221606e0002SDavid du Colombier static void
x86wdrestart(void)222606e0002SDavid du Colombier x86wdrestart(void)
223606e0002SDavid du Colombier {
224606e0002SDavid du Colombier Wd *wd;
225606e0002SDavid du Colombier vlong r, t;
226606e0002SDavid du Colombier
227606e0002SDavid du Colombier runoncpu(0);
228606e0002SDavid du Colombier t = interval();
229606e0002SDavid du Colombier
230606e0002SDavid du Colombier wd = &x86wd;
231*86abb9fbSDavid du Colombier ilock(wd);
232606e0002SDavid du Colombier switch(wd->model){
233606e0002SDavid du Colombier case P6:
234606e0002SDavid du Colombier wrmsr(0xC1, -t);
235606e0002SDavid du Colombier break;
236606e0002SDavid du Colombier case P4:
237606e0002SDavid du Colombier r = 0x0000000004FF8000LL;
238606e0002SDavid du Colombier wrmsr(0x36C, r);
239606e0002SDavid du Colombier lapicnmienable();
240606e0002SDavid du Colombier wrmsr(0x30C, -t);
241606e0002SDavid du Colombier wrmsr(0x36C, 0x0000000000001000LL|r);
242606e0002SDavid du Colombier break;
243606e0002SDavid du Colombier case K6:
244606e0002SDavid du Colombier case K8:
245606e0002SDavid du Colombier wrmsr(0xC0010004, -t);
246606e0002SDavid du Colombier break;
247606e0002SDavid du Colombier }
248606e0002SDavid du Colombier wd->ticks++;
249*86abb9fbSDavid du Colombier iunlock(wd);
250606e0002SDavid du Colombier }
251606e0002SDavid du Colombier
252606e0002SDavid du Colombier void
x86wdstat(char * p,char * ep)253606e0002SDavid du Colombier x86wdstat(char* p, char* ep)
254606e0002SDavid du Colombier {
255606e0002SDavid du Colombier Wd *wd;
256606e0002SDavid du Colombier int inuse;
257606e0002SDavid du Colombier uint ticks;
258606e0002SDavid du Colombier
259606e0002SDavid du Colombier wd = &x86wd;
260*86abb9fbSDavid du Colombier ilock(wd);
261606e0002SDavid du Colombier inuse = wd->inuse;
262606e0002SDavid du Colombier ticks = wd->ticks;
263*86abb9fbSDavid du Colombier iunlock(wd);
264606e0002SDavid du Colombier
265606e0002SDavid du Colombier if(inuse)
266606e0002SDavid du Colombier seprint(p, ep, "enabled %ud restarts\n", ticks);
267606e0002SDavid du Colombier else
268606e0002SDavid du Colombier seprint(p, ep, "disabled %ud restarts\n", ticks);
269606e0002SDavid du Colombier }
270606e0002SDavid du Colombier
271606e0002SDavid du Colombier Watchdog x86watchdog = {
272606e0002SDavid du Colombier x86wdenable,
273606e0002SDavid du Colombier x86wddisable,
274606e0002SDavid du Colombier x86wdrestart,
275606e0002SDavid du Colombier x86wdstat,
276606e0002SDavid du Colombier };
277606e0002SDavid du Colombier
278606e0002SDavid du Colombier void
x86watchdoglink(void)279606e0002SDavid du Colombier x86watchdoglink(void)
280606e0002SDavid du Colombier {
281606e0002SDavid du Colombier addwatchdog(&x86watchdog);
282606e0002SDavid du Colombier }
283