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 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 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 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 101 void arch_watchdog_stop(void) 102 { 103 } 104 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 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 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 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 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 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