xref: /minix3/minix/kernel/arch/i386/arch_watchdog.c (revision 9624407e7addfd8b88486acfe3a0e056e2b92ee3)
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