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