1*433d6423SLionel Sambuc #include "kernel/watchdog.h"
2*433d6423SLionel Sambuc #include "glo.h"
3*433d6423SLionel Sambuc #include <minix/minlib.h>
4*433d6423SLionel Sambuc #include <minix/u64.h>
5*433d6423SLionel Sambuc
6*433d6423SLionel Sambuc #include "apic.h"
7*433d6423SLionel Sambuc
8*433d6423SLionel Sambuc #define CPUID_UNHALTED_CORE_CYCLES_AVAILABLE 0
9*433d6423SLionel Sambuc
10*433d6423SLionel Sambuc /*
11*433d6423SLionel Sambuc * Intel architecture performance counters watchdog
12*433d6423SLionel Sambuc */
13*433d6423SLionel Sambuc
14*433d6423SLionel Sambuc static struct arch_watchdog intel_arch_watchdog;
15*433d6423SLionel Sambuc static struct arch_watchdog amd_watchdog;
16*433d6423SLionel Sambuc
intel_arch_watchdog_init(const unsigned cpu)17*433d6423SLionel Sambuc static void intel_arch_watchdog_init(const unsigned cpu)
18*433d6423SLionel Sambuc {
19*433d6423SLionel Sambuc u64_t cpuf;
20*433d6423SLionel Sambuc u32_t val;
21*433d6423SLionel Sambuc
22*433d6423SLionel Sambuc ia32_msr_write(INTEL_MSR_PERFMON_CRT0, 0, 0);
23*433d6423SLionel Sambuc
24*433d6423SLionel Sambuc /* Int, OS, USR, Core ccyles */
25*433d6423SLionel Sambuc val = 1 << 20 | 1 << 17 | 1 << 16 | 0x3c;
26*433d6423SLionel Sambuc ia32_msr_write(INTEL_MSR_PERFMON_SEL0, 0, val);
27*433d6423SLionel Sambuc
28*433d6423SLionel Sambuc /*
29*433d6423SLionel Sambuc * should give as a tick approx. every 0.5-1s, the perf counter has only
30*433d6423SLionel Sambuc * lowest 31 bits writable :(
31*433d6423SLionel Sambuc */
32*433d6423SLionel Sambuc cpuf = cpu_get_freq(cpu);
33*433d6423SLionel Sambuc while (ex64hi(cpuf) || ex64lo(cpuf) > 0x7fffffffU)
34*433d6423SLionel Sambuc cpuf /= 2;
35*433d6423SLionel Sambuc cpuf = make64(-ex64lo(cpuf), ex64hi(cpuf));
36*433d6423SLionel Sambuc watchdog->resetval = watchdog->watchdog_resetval = cpuf;
37*433d6423SLionel Sambuc
38*433d6423SLionel Sambuc ia32_msr_write(INTEL_MSR_PERFMON_CRT0, 0, ex64lo(cpuf));
39*433d6423SLionel Sambuc
40*433d6423SLionel Sambuc ia32_msr_write(INTEL_MSR_PERFMON_SEL0, 0,
41*433d6423SLionel Sambuc val | INTEL_MSR_PERFMON_SEL0_ENABLE);
42*433d6423SLionel Sambuc
43*433d6423SLionel Sambuc /* unmask the performance counter interrupt */
44*433d6423SLionel Sambuc lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
45*433d6423SLionel Sambuc }
46*433d6423SLionel Sambuc
intel_arch_watchdog_reinit(const unsigned cpu)47*433d6423SLionel Sambuc static void intel_arch_watchdog_reinit(const unsigned cpu)
48*433d6423SLionel Sambuc {
49*433d6423SLionel Sambuc lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
50*433d6423SLionel Sambuc ia32_msr_write(INTEL_MSR_PERFMON_CRT0, 0, ex64lo(watchdog->resetval));
51*433d6423SLionel Sambuc }
52*433d6423SLionel Sambuc
arch_watchdog_init(void)53*433d6423SLionel Sambuc int arch_watchdog_init(void)
54*433d6423SLionel Sambuc {
55*433d6423SLionel Sambuc u32_t eax, ebx, ecx, edx;
56*433d6423SLionel Sambuc unsigned cpu = cpuid;
57*433d6423SLionel Sambuc
58*433d6423SLionel Sambuc if (!lapic_addr) {
59*433d6423SLionel Sambuc printf("ERROR : Cannot use NMI watchdog if APIC is not enabled\n");
60*433d6423SLionel Sambuc return -1;
61*433d6423SLionel Sambuc }
62*433d6423SLionel Sambuc
63*433d6423SLionel Sambuc if (cpu_info[cpu].vendor == CPU_VENDOR_INTEL) {
64*433d6423SLionel Sambuc eax = 0xA;
65*433d6423SLionel Sambuc
66*433d6423SLionel Sambuc _cpuid(&eax, &ebx, &ecx, &edx);
67*433d6423SLionel Sambuc
68*433d6423SLionel Sambuc /* FIXME currently we support only watchdog based on the intel
69*433d6423SLionel Sambuc * architectural performance counters. Some Intel CPUs don't have this
70*433d6423SLionel Sambuc * feature
71*433d6423SLionel Sambuc */
72*433d6423SLionel Sambuc if (ebx & (1 << CPUID_UNHALTED_CORE_CYCLES_AVAILABLE))
73*433d6423SLionel Sambuc return -1;
74*433d6423SLionel Sambuc if (!((((eax >> 8)) & 0xff) > 0))
75*433d6423SLionel Sambuc return -1;
76*433d6423SLionel Sambuc
77*433d6423SLionel Sambuc watchdog = &intel_arch_watchdog;
78*433d6423SLionel Sambuc } else if (cpu_info[cpu].vendor == CPU_VENDOR_AMD) {
79*433d6423SLionel Sambuc if (cpu_info[cpu].family != 6 &&
80*433d6423SLionel Sambuc cpu_info[cpu].family != 15 &&
81*433d6423SLionel Sambuc cpu_info[cpu].family != 16 &&
82*433d6423SLionel Sambuc cpu_info[cpu].family != 17)
83*433d6423SLionel Sambuc return -1;
84*433d6423SLionel Sambuc else
85*433d6423SLionel Sambuc watchdog = &amd_watchdog;
86*433d6423SLionel Sambuc } else
87*433d6423SLionel Sambuc return -1;
88*433d6423SLionel Sambuc
89*433d6423SLionel Sambuc /* Setup PC overflow as NMI for watchdog, it is masked for now */
90*433d6423SLionel Sambuc lapic_write(LAPIC_LVTPCR, APIC_ICR_INT_MASK | APIC_ICR_DM_NMI);
91*433d6423SLionel Sambuc (void) lapic_read(LAPIC_LVTPCR);
92*433d6423SLionel Sambuc
93*433d6423SLionel Sambuc /* double check if LAPIC is enabled */
94*433d6423SLionel Sambuc if (lapic_addr && watchdog->init) {
95*433d6423SLionel Sambuc watchdog->init(cpuid);
96*433d6423SLionel Sambuc }
97*433d6423SLionel Sambuc
98*433d6423SLionel Sambuc return 0;
99*433d6423SLionel Sambuc }
100*433d6423SLionel Sambuc
arch_watchdog_stop(void)101*433d6423SLionel Sambuc void arch_watchdog_stop(void)
102*433d6423SLionel Sambuc {
103*433d6423SLionel Sambuc }
104*433d6423SLionel Sambuc
arch_watchdog_lockup(const struct nmi_frame * frame)105*433d6423SLionel Sambuc void arch_watchdog_lockup(const struct nmi_frame * frame)
106*433d6423SLionel Sambuc {
107*433d6423SLionel Sambuc printf("KERNEL LOCK UP\n"
108*433d6423SLionel Sambuc "eax 0x%08x\n"
109*433d6423SLionel Sambuc "ecx 0x%08x\n"
110*433d6423SLionel Sambuc "edx 0x%08x\n"
111*433d6423SLionel Sambuc "ebx 0x%08x\n"
112*433d6423SLionel Sambuc "ebp 0x%08x\n"
113*433d6423SLionel Sambuc "esi 0x%08x\n"
114*433d6423SLionel Sambuc "edi 0x%08x\n"
115*433d6423SLionel Sambuc "gs 0x%08x\n"
116*433d6423SLionel Sambuc "fs 0x%08x\n"
117*433d6423SLionel Sambuc "es 0x%08x\n"
118*433d6423SLionel Sambuc "ds 0x%08x\n"
119*433d6423SLionel Sambuc "pc 0x%08x\n"
120*433d6423SLionel Sambuc "cs 0x%08x\n"
121*433d6423SLionel Sambuc "eflags 0x%08x\n",
122*433d6423SLionel Sambuc frame->eax,
123*433d6423SLionel Sambuc frame->ecx,
124*433d6423SLionel Sambuc frame->edx,
125*433d6423SLionel Sambuc frame->ebx,
126*433d6423SLionel Sambuc frame->ebp,
127*433d6423SLionel Sambuc frame->esi,
128*433d6423SLionel Sambuc frame->edi,
129*433d6423SLionel Sambuc frame->gs,
130*433d6423SLionel Sambuc frame->fs,
131*433d6423SLionel Sambuc frame->es,
132*433d6423SLionel Sambuc frame->ds,
133*433d6423SLionel Sambuc frame->pc,
134*433d6423SLionel Sambuc frame->cs,
135*433d6423SLionel Sambuc frame->eflags
136*433d6423SLionel Sambuc );
137*433d6423SLionel Sambuc panic("Kernel lockup");
138*433d6423SLionel Sambuc }
139*433d6423SLionel Sambuc
i386_watchdog_start(void)140*433d6423SLionel Sambuc int i386_watchdog_start(void)
141*433d6423SLionel Sambuc {
142*433d6423SLionel Sambuc if (arch_watchdog_init()) {
143*433d6423SLionel Sambuc printf("WARNING watchdog initialization "
144*433d6423SLionel Sambuc "failed! Disabled\n");
145*433d6423SLionel Sambuc watchdog_enabled = 0;
146*433d6423SLionel Sambuc return -1;
147*433d6423SLionel Sambuc }
148*433d6423SLionel Sambuc else
149*433d6423SLionel Sambuc BOOT_VERBOSE(printf("Watchdog enabled\n"););
150*433d6423SLionel Sambuc
151*433d6423SLionel Sambuc return 0;
152*433d6423SLionel Sambuc }
153*433d6423SLionel Sambuc
intel_arch_watchdog_profile_init(const unsigned freq)154*433d6423SLionel Sambuc static int intel_arch_watchdog_profile_init(const unsigned freq)
155*433d6423SLionel Sambuc {
156*433d6423SLionel Sambuc u64_t cpuf;
157*433d6423SLionel Sambuc
158*433d6423SLionel Sambuc /* FIXME works only if all CPUs have the same freq */
159*433d6423SLionel Sambuc cpuf = cpu_get_freq(cpuid);
160*433d6423SLionel Sambuc cpuf /= freq;
161*433d6423SLionel Sambuc
162*433d6423SLionel Sambuc /*
163*433d6423SLionel Sambuc * if freq is too low and the cpu freq too high we may get in a range of
164*433d6423SLionel Sambuc * insane value which cannot be handled by the 31bit CPU perf counter
165*433d6423SLionel Sambuc */
166*433d6423SLionel Sambuc if (ex64hi(cpuf) != 0 || ex64lo(cpuf) > 0x7fffffffU) {
167*433d6423SLionel Sambuc printf("ERROR : nmi watchdog ticks exceed 31bits, use higher frequency\n");
168*433d6423SLionel Sambuc return EINVAL;
169*433d6423SLionel Sambuc }
170*433d6423SLionel Sambuc
171*433d6423SLionel Sambuc cpuf = make64(-ex64lo(cpuf), ex64hi(cpuf));
172*433d6423SLionel Sambuc watchdog->profile_resetval = cpuf;
173*433d6423SLionel Sambuc
174*433d6423SLionel Sambuc return OK;
175*433d6423SLionel Sambuc }
176*433d6423SLionel Sambuc
177*433d6423SLionel Sambuc static struct arch_watchdog intel_arch_watchdog = {
178*433d6423SLionel Sambuc /*.init = */ intel_arch_watchdog_init,
179*433d6423SLionel Sambuc /*.reinit = */ intel_arch_watchdog_reinit,
180*433d6423SLionel Sambuc /*.profile_init = */ intel_arch_watchdog_profile_init
181*433d6423SLionel Sambuc };
182*433d6423SLionel Sambuc
183*433d6423SLionel Sambuc #define AMD_MSR_EVENT_SEL0 0xc0010000
184*433d6423SLionel Sambuc #define AMD_MSR_EVENT_CTR0 0xc0010004
185*433d6423SLionel Sambuc #define AMD_MSR_EVENT_SEL0_ENABLE (1 << 22)
186*433d6423SLionel Sambuc
amd_watchdog_init(const unsigned cpu)187*433d6423SLionel Sambuc static void amd_watchdog_init(const unsigned cpu)
188*433d6423SLionel Sambuc {
189*433d6423SLionel Sambuc u64_t cpuf;
190*433d6423SLionel Sambuc u32_t val;
191*433d6423SLionel Sambuc
192*433d6423SLionel Sambuc ia32_msr_write(AMD_MSR_EVENT_CTR0, 0, 0);
193*433d6423SLionel Sambuc
194*433d6423SLionel Sambuc /* Int, OS, USR, Cycles cpu is running */
195*433d6423SLionel Sambuc val = 1 << 20 | 1 << 17 | 1 << 16 | 0x76;
196*433d6423SLionel Sambuc ia32_msr_write(AMD_MSR_EVENT_SEL0, 0, val);
197*433d6423SLionel Sambuc
198*433d6423SLionel Sambuc cpuf = -cpu_get_freq(cpu);
199*433d6423SLionel Sambuc watchdog->resetval = watchdog->watchdog_resetval = cpuf;
200*433d6423SLionel Sambuc
201*433d6423SLionel Sambuc ia32_msr_write(AMD_MSR_EVENT_CTR0,
202*433d6423SLionel Sambuc ex64hi(watchdog->resetval), ex64lo(watchdog->resetval));
203*433d6423SLionel Sambuc
204*433d6423SLionel Sambuc ia32_msr_write(AMD_MSR_EVENT_SEL0, 0,
205*433d6423SLionel Sambuc val | AMD_MSR_EVENT_SEL0_ENABLE);
206*433d6423SLionel Sambuc
207*433d6423SLionel Sambuc /* unmask the performance counter interrupt */
208*433d6423SLionel Sambuc lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
209*433d6423SLionel Sambuc }
210*433d6423SLionel Sambuc
amd_watchdog_reinit(const unsigned cpu)211*433d6423SLionel Sambuc static void amd_watchdog_reinit(const unsigned cpu)
212*433d6423SLionel Sambuc {
213*433d6423SLionel Sambuc lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
214*433d6423SLionel Sambuc ia32_msr_write(AMD_MSR_EVENT_CTR0,
215*433d6423SLionel Sambuc ex64hi(watchdog->resetval), ex64lo(watchdog->resetval));
216*433d6423SLionel Sambuc }
217*433d6423SLionel Sambuc
amd_watchdog_profile_init(const unsigned freq)218*433d6423SLionel Sambuc static int amd_watchdog_profile_init(const unsigned freq)
219*433d6423SLionel Sambuc {
220*433d6423SLionel Sambuc u64_t cpuf;
221*433d6423SLionel Sambuc
222*433d6423SLionel Sambuc /* FIXME works only if all CPUs have the same freq */
223*433d6423SLionel Sambuc cpuf = cpu_get_freq(cpuid);
224*433d6423SLionel Sambuc cpuf = -cpuf / freq;
225*433d6423SLionel Sambuc
226*433d6423SLionel Sambuc watchdog->profile_resetval = cpuf;
227*433d6423SLionel Sambuc
228*433d6423SLionel Sambuc return OK;
229*433d6423SLionel Sambuc }
230*433d6423SLionel Sambuc
231*433d6423SLionel Sambuc static struct arch_watchdog amd_watchdog = {
232*433d6423SLionel Sambuc /*.init = */ amd_watchdog_init,
233*433d6423SLionel Sambuc /*.reinit = */ amd_watchdog_reinit,
234*433d6423SLionel Sambuc /*.profile_init = */ amd_watchdog_profile_init
235*433d6423SLionel Sambuc };
236