1 #include "kernel/kernel.h"
2 #include "kernel/watchdog.h"
3 #include "arch_proto.h"
5 #include <minix/minlib.h>
10 #define CPUID_UNHALTED_CORE_CYCLES_AVAILABLE 0
12 #define INTEL_MSR_PERFMON_CRT0 0xc1
13 #define INTEL_MSR_PERFMON_SEL0 0x186
15 #define INTEL_MSR_PERFMON_SEL0_ENABLE (1 << 22)
18 * Intel architecture performance counters watchdog
21 static struct arch_watchdog intel_arch_watchdog
;
22 static struct arch_watchdog amd_watchdog
;
24 static void intel_arch_watchdog_init(const unsigned cpu
)
29 ia32_msr_write(INTEL_MSR_PERFMON_CRT0
, 0, 0);
31 /* Int, OS, USR, Core ccyles */
32 val
= 1 << 20 | 1 << 17 | 1 << 16 | 0x3c;
33 ia32_msr_write(INTEL_MSR_PERFMON_SEL0
, 0, val
);
36 * should give as a tick approx. every 0.5-1s, the perf counter has only
37 * lowest 31 bits writable :(
39 cpuf
= cpu_get_freq(cpu
);
40 while (ex64hi(cpuf
) || ex64lo(cpuf
) > 0x7fffffffU
)
41 cpuf
= div64u64(cpuf
, 2);
42 cpuf
= make64(-ex64lo(cpuf
), ex64hi(cpuf
));
43 watchdog
->resetval
= watchdog
->watchdog_resetval
= cpuf
;
45 ia32_msr_write(INTEL_MSR_PERFMON_CRT0
, 0, ex64lo(cpuf
));
47 ia32_msr_write(INTEL_MSR_PERFMON_SEL0
, 0,
48 val
| INTEL_MSR_PERFMON_SEL0_ENABLE
);
50 /* unmask the performance counter interrupt */
51 lapic_write(LAPIC_LVTPCR
, APIC_ICR_DM_NMI
);
54 static void intel_arch_watchdog_reinit(const unsigned cpu
)
56 lapic_write(LAPIC_LVTPCR
, APIC_ICR_DM_NMI
);
57 ia32_msr_write(INTEL_MSR_PERFMON_CRT0
, 0, ex64lo(watchdog
->resetval
));
60 int arch_watchdog_init(void)
62 u32_t eax
, ebx
, ecx
, edx
;
66 printf("ERROR : Cannot use NMI watchdog if APIC is not enabled\n");
70 if (cpu_info
[cpu
].vendor
== CPU_VENDOR_INTEL
) {
73 _cpuid(&eax
, &ebx
, &ecx
, &edx
);
75 /* FIXME currently we support only watchdog based on the intel
76 * architectural performance counters. Some Intel CPUs don't have this
79 if (ebx
& (1 << CPUID_UNHALTED_CORE_CYCLES_AVAILABLE
))
81 if (!((((eax
>> 8)) & 0xff) > 0))
84 watchdog
= &intel_arch_watchdog
;
85 } else if (cpu_info
[cpu
].vendor
== CPU_VENDOR_AMD
) {
86 if (cpu_info
[cpu
].family
!= 6 &&
87 cpu_info
[cpu
].family
!= 15 &&
88 cpu_info
[cpu
].family
!= 16 &&
89 cpu_info
[cpu
].family
!= 17)
92 watchdog
= &amd_watchdog
;
96 /* Setup PC overflow as NMI for watchdog, it is masked for now */
97 lapic_write(LAPIC_LVTPCR
, APIC_ICR_INT_MASK
| APIC_ICR_DM_NMI
);
98 (void) lapic_read(LAPIC_LVTPCR
);
100 /* double check if LAPIC is enabled */
101 if (lapic_addr
&& watchdog
->init
) {
102 watchdog
->init(cpuid
);
108 void arch_watchdog_stop(void)
112 void arch_watchdog_lockup(const struct nmi_frame
* frame
)
114 printf("KERNEL LOCK UP\n"
144 panic("Kernel lockup");
147 int i386_watchdog_start(void)
149 if (arch_watchdog_init()) {
150 printf("WARNING watchdog initialization "
151 "failed! Disabled\n");
152 watchdog_enabled
= 0;
156 BOOT_VERBOSE(printf("Watchdog enabled\n"););
161 static int intel_arch_watchdog_profile_init(const unsigned freq
)
165 /* FIXME works only if all CPUs have the same freq */
166 cpuf
= cpu_get_freq(cpuid
);
167 cpuf
= div64u64(cpuf
, freq
);
170 * if freq is too low and the cpu freq too high we may get in a range of
171 * insane value which cannot be handled by the 31bit CPU perf counter
173 if (ex64hi(cpuf
) != 0 || ex64lo(cpuf
) > 0x7fffffffU
) {
174 printf("ERROR : nmi watchdog ticks exceed 31bits, use higher frequency\n");
178 cpuf
= make64(-ex64lo(cpuf
), ex64hi(cpuf
));
179 watchdog
->profile_resetval
= cpuf
;
184 static struct arch_watchdog intel_arch_watchdog
= {
185 /*.init = */ intel_arch_watchdog_init
,
186 /*.reinit = */ intel_arch_watchdog_reinit
,
187 /*.profile_init = */ intel_arch_watchdog_profile_init
190 #define AMD_MSR_EVENT_SEL0 0xc0010000
191 #define AMD_MSR_EVENT_CTR0 0xc0010004
192 #define AMD_MSR_EVENT_SEL0_ENABLE (1 << 22)
194 static void amd_watchdog_init(const unsigned cpu
)
199 ia32_msr_write(AMD_MSR_EVENT_CTR0
, 0, 0);
201 /* Int, OS, USR, Cycles cpu is running */
202 val
= 1 << 20 | 1 << 17 | 1 << 16 | 0x76;
203 ia32_msr_write(AMD_MSR_EVENT_SEL0
, 0, val
);
205 cpuf
= cpu_get_freq(cpu
);
207 watchdog
->resetval
= watchdog
->watchdog_resetval
= cpuf
;
209 ia32_msr_write(AMD_MSR_EVENT_CTR0
,
210 ex64hi(watchdog
->resetval
), ex64lo(watchdog
->resetval
));
212 ia32_msr_write(AMD_MSR_EVENT_SEL0
, 0,
213 val
| AMD_MSR_EVENT_SEL0_ENABLE
);
215 /* unmask the performance counter interrupt */
216 lapic_write(LAPIC_LVTPCR
, APIC_ICR_DM_NMI
);
219 static void amd_watchdog_reinit(const unsigned cpu
)
221 lapic_write(LAPIC_LVTPCR
, APIC_ICR_DM_NMI
);
222 ia32_msr_write(AMD_MSR_EVENT_CTR0
,
223 ex64hi(watchdog
->resetval
), ex64lo(watchdog
->resetval
));
226 static int amd_watchdog_profile_init(const unsigned freq
)
230 /* FIXME works only if all CPUs have the same freq */
231 cpuf
= cpu_get_freq(cpuid
);
232 cpuf
= div64u64(cpuf
, freq
);
235 watchdog
->profile_resetval
= cpuf
;
240 static struct arch_watchdog amd_watchdog
= {
241 /*.init = */ amd_watchdog_init
,
242 /*.reinit = */ amd_watchdog_reinit
,
243 /*.profile_init = */ amd_watchdog_profile_init