]>
Commit | Line | Data |
---|---|---|
0c530ab8 A |
1 | /* |
2 | * Copyright (c) 2007 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
2d21ac55 | 29 | #include <kern/kalloc.h> |
0c530ab8 A |
30 | #include <i386/cpu_data.h> |
31 | #include <i386/cpuid.h> | |
32 | #include <i386/machine_check.h> | |
33 | #include <i386/proc_reg.h> | |
34 | ||
35 | #define IF(bool,str) ((bool) ? (str) : "") | |
36 | ||
37 | static boolean_t mca_initialized = FALSE; | |
38 | static boolean_t mca_MCE_present = FALSE; | |
39 | static boolean_t mca_MCA_present = FALSE; | |
2d21ac55 | 40 | static uint32_t mca_family = 0; |
0c530ab8 A |
41 | static unsigned int mca_error_bank_count = 0; |
42 | static boolean_t mca_control_MSR_present = FALSE; | |
43 | static boolean_t mca_threshold_status_present = FALSE; | |
44 | static boolean_t mca_extended_MSRs_present = FALSE; | |
45 | static unsigned int mca_extended_MSRs_count = 0; | |
46 | static ia32_mcg_cap_t ia32_mcg_cap; | |
2d21ac55 A |
47 | static boolean_t mca_exception_taken = FALSE; |
48 | ||
49 | decl_simple_lock_data(static, mca_lock); | |
50 | ||
51 | typedef struct { | |
52 | ia32_mci_ctl_t mca_mci_ctl; | |
53 | ia32_mci_status_t mca_mci_status; | |
54 | ia32_mci_misc_t mca_mci_misc; | |
55 | ia32_mci_addr_t mca_mci_addr; | |
56 | } mca_mci_bank_t; | |
57 | ||
58 | typedef struct mca_state { | |
59 | ia32_mcg_ctl_t mca_mcg_ctl; | |
60 | ia32_mcg_status_t mca_mcg_status; | |
61 | mca_mci_bank_t mca_error_bank[0]; | |
62 | } mca_state_t; | |
0c530ab8 A |
63 | |
64 | static void | |
65 | mca_get_availability(void) | |
66 | { | |
67 | uint64_t features = cpuid_info()->cpuid_features; | |
68 | uint32_t family = cpuid_info()->cpuid_family; | |
69 | ||
70 | mca_MCE_present = (features & CPUID_FEATURE_MCE) != 0; | |
71 | mca_MCA_present = (features & CPUID_FEATURE_MCA) != 0; | |
2d21ac55 | 72 | mca_family = family; |
0c530ab8 A |
73 | |
74 | /* | |
75 | * If MCA, the number of banks etc is reported by the IA32_MCG_CAP MSR. | |
76 | */ | |
77 | if (mca_MCA_present) { | |
78 | ia32_mcg_cap.u64 = rdmsr64(IA32_MCG_CAP); | |
79 | mca_error_bank_count = ia32_mcg_cap.bits.count; | |
80 | mca_control_MSR_present = ia32_mcg_cap.bits.mcg_ctl_p; | |
81 | mca_threshold_status_present = ia32_mcg_cap.bits.mcg_tes_p; | |
82 | if (family == 0x0F) { | |
83 | mca_extended_MSRs_present = ia32_mcg_cap.bits.mcg_ext_p; | |
84 | mca_extended_MSRs_count = ia32_mcg_cap.bits.mcg_ext_cnt; | |
85 | } | |
86 | } | |
87 | } | |
88 | ||
89 | void | |
90 | mca_cpu_init(void) | |
91 | { | |
92 | unsigned int i; | |
93 | ||
94 | /* | |
95 | * The first (boot) processor is responsible for discovering the | |
96 | * machine check architecture present on this machine. | |
97 | */ | |
98 | if (!mca_initialized) { | |
99 | mca_get_availability(); | |
100 | mca_initialized = TRUE; | |
2d21ac55 | 101 | simple_lock_init(&mca_lock, 0); |
0c530ab8 A |
102 | } |
103 | ||
104 | if (mca_MCA_present) { | |
105 | ||
106 | /* Enable all MCA features */ | |
107 | if (mca_control_MSR_present) | |
108 | wrmsr64(IA32_MCG_CTL, IA32_MCG_CTL_ENABLE); | |
109 | ||
2d21ac55 | 110 | switch (mca_family) { |
0c530ab8 A |
111 | case 0x06: |
112 | /* Enable all but mc0 */ | |
113 | for (i = 1; i < mca_error_bank_count; i++) | |
114 | wrmsr64(IA32_MCi_CTL(i),0xFFFFFFFFFFFFFFFFULL); | |
115 | ||
116 | /* Clear all errors */ | |
117 | for (i = 0; i < mca_error_bank_count; i++) | |
118 | wrmsr64(IA32_MCi_STATUS(i), 0ULL); | |
119 | break; | |
120 | case 0x0F: | |
121 | /* Enable all banks */ | |
122 | for (i = 0; i < mca_error_bank_count; i++) | |
123 | wrmsr64(IA32_MCi_CTL(i),0xFFFFFFFFFFFFFFFFULL); | |
124 | ||
125 | /* Clear all errors */ | |
126 | for (i = 0; i < mca_error_bank_count; i++) | |
127 | wrmsr64(IA32_MCi_STATUS(i), 0ULL); | |
128 | break; | |
129 | } | |
130 | } | |
131 | ||
132 | /* Enable machine check exception handling if available */ | |
133 | if (mca_MCE_present) { | |
134 | set_cr4(get_cr4()|CR4_MCE); | |
135 | } | |
136 | } | |
137 | ||
2d21ac55 A |
138 | void |
139 | mca_cpu_alloc(cpu_data_t *cdp) | |
140 | { | |
141 | vm_size_t mca_state_size; | |
142 | ||
143 | /* | |
144 | * Allocate space for an array of error banks. | |
145 | */ | |
146 | mca_state_size = sizeof(mca_state_t) + | |
147 | sizeof(mca_mci_bank_t) * mca_error_bank_count; | |
148 | cdp->cpu_mca_state = kalloc(mca_state_size); | |
149 | if (cdp->cpu_mca_state == NULL) { | |
150 | printf("mca_cpu_alloc() failed for cpu %d\n", cdp->cpu_number); | |
151 | return; | |
152 | } | |
153 | bzero((void *) cdp->cpu_mca_state, mca_state_size); | |
154 | ||
155 | /* | |
156 | * If the boot processor is yet have its allocation made, | |
157 | * do this now. | |
158 | */ | |
159 | if (cpu_datap(master_cpu)->cpu_mca_state == NULL) | |
160 | mca_cpu_alloc(cpu_datap(master_cpu)); | |
161 | } | |
162 | ||
163 | static void | |
164 | mca_save_state(void) | |
165 | { | |
166 | mca_state_t *mca_state; | |
167 | mca_mci_bank_t *bank; | |
168 | unsigned int i; | |
169 | ||
170 | assert(!ml_get_interrupts_enabled() || get_preemption_level() > 0); | |
171 | ||
172 | mca_state = (mca_state_t *) current_cpu_datap()->cpu_mca_state; | |
173 | if (mca_state == NULL) | |
174 | return; | |
175 | ||
176 | mca_state->mca_mcg_ctl = mca_control_MSR_present ? | |
177 | rdmsr64(IA32_MCG_CTL) : 0ULL; | |
178 | mca_state->mca_mcg_status.u64 = rdmsr64(IA32_MCG_STATUS); | |
179 | ||
180 | bank = (mca_mci_bank_t *) &mca_state->mca_error_bank[0]; | |
181 | for (i = 0; i < mca_error_bank_count; i++, bank++) { | |
182 | bank->mca_mci_ctl = rdmsr64(IA32_MCi_CTL(i)); | |
183 | bank->mca_mci_status.u64 = rdmsr64(IA32_MCi_STATUS(i)); | |
184 | if (!bank->mca_mci_status.bits.val) | |
185 | continue; | |
186 | bank->mca_mci_misc = (bank->mca_mci_status.bits.miscv)? | |
187 | rdmsr64(IA32_MCi_MISC(i)) : 0ULL; | |
188 | bank->mca_mci_addr = (bank->mca_mci_status.bits.addrv)? | |
189 | rdmsr64(IA32_MCi_ADDR(i)) : 0ULL; | |
190 | } | |
191 | } | |
192 | ||
193 | void | |
194 | mca_check_save(void) | |
195 | { | |
196 | if (mca_exception_taken) | |
197 | mca_save_state(); | |
198 | } | |
199 | ||
0c530ab8 A |
200 | static void mca_dump_64bit_state(void) |
201 | { | |
202 | kdb_printf("Extended Machine Check State:\n"); | |
203 | kdb_printf(" IA32_MCG_RAX: 0x%016qx\n", rdmsr64(IA32_MCG_RAX)); | |
204 | kdb_printf(" IA32_MCG_RBX: 0x%016qx\n", rdmsr64(IA32_MCG_RBX)); | |
205 | kdb_printf(" IA32_MCG_RCX: 0x%016qx\n", rdmsr64(IA32_MCG_RCX)); | |
206 | kdb_printf(" IA32_MCG_RDX: 0x%016qx\n", rdmsr64(IA32_MCG_RDX)); | |
207 | kdb_printf(" IA32_MCG_RSI: 0x%016qx\n", rdmsr64(IA32_MCG_RSI)); | |
208 | kdb_printf(" IA32_MCG_RDI: 0x%016qx\n", rdmsr64(IA32_MCG_RDI)); | |
209 | kdb_printf(" IA32_MCG_RBP: 0x%016qx\n", rdmsr64(IA32_MCG_RBP)); | |
210 | kdb_printf(" IA32_MCG_RSP: 0x%016qx\n", rdmsr64(IA32_MCG_RSP)); | |
211 | kdb_printf(" IA32_MCG_RFLAGS: 0x%016qx\n", rdmsr64(IA32_MCG_RFLAGS)); | |
212 | kdb_printf(" IA32_MCG_RIP: 0x%016qx\n", rdmsr64(IA32_MCG_RIP)); | |
213 | kdb_printf(" IA32_MCG_MISC: 0x%016qx\n", rdmsr64(IA32_MCG_MISC)); | |
214 | kdb_printf(" IA32_MCG_R8: 0x%016qx\n", rdmsr64(IA32_MCG_R8)); | |
215 | kdb_printf(" IA32_MCG_R9: 0x%016qx\n", rdmsr64(IA32_MCG_R9)); | |
216 | kdb_printf(" IA32_MCG_R10: 0x%016qx\n", rdmsr64(IA32_MCG_R10)); | |
217 | kdb_printf(" IA32_MCG_R11: 0x%016qx\n", rdmsr64(IA32_MCG_R11)); | |
218 | kdb_printf(" IA32_MCG_R12: 0x%016qx\n", rdmsr64(IA32_MCG_R12)); | |
219 | kdb_printf(" IA32_MCG_R13: 0x%016qx\n", rdmsr64(IA32_MCG_R13)); | |
220 | kdb_printf(" IA32_MCG_R14: 0x%016qx\n", rdmsr64(IA32_MCG_R14)); | |
221 | kdb_printf(" IA32_MCG_R15: 0x%016qx\n", rdmsr64(IA32_MCG_R15)); | |
222 | } | |
223 | ||
224 | static uint32_t rdmsr32(uint32_t msr) | |
225 | { | |
226 | return (uint32_t) rdmsr64(msr); | |
227 | } | |
228 | ||
229 | static void mca_dump_32bit_state(void) | |
230 | { | |
231 | kdb_printf("Extended Machine Check State:\n"); | |
232 | kdb_printf(" IA32_MCG_EAX: 0x%08x\n", rdmsr32(IA32_MCG_EAX)); | |
233 | kdb_printf(" IA32_MCG_EBX: 0x%08x\n", rdmsr32(IA32_MCG_EBX)); | |
234 | kdb_printf(" IA32_MCG_ECX: 0x%08x\n", rdmsr32(IA32_MCG_ECX)); | |
235 | kdb_printf(" IA32_MCG_EDX: 0x%08x\n", rdmsr32(IA32_MCG_EDX)); | |
236 | kdb_printf(" IA32_MCG_ESI: 0x%08x\n", rdmsr32(IA32_MCG_ESI)); | |
237 | kdb_printf(" IA32_MCG_EDI: 0x%08x\n", rdmsr32(IA32_MCG_EDI)); | |
238 | kdb_printf(" IA32_MCG_EBP: 0x%08x\n", rdmsr32(IA32_MCG_EBP)); | |
239 | kdb_printf(" IA32_MCG_ESP: 0x%08x\n", rdmsr32(IA32_MCG_ESP)); | |
240 | kdb_printf(" IA32_MCG_EFLAGS: 0x%08x\n", rdmsr32(IA32_MCG_EFLAGS)); | |
241 | kdb_printf(" IA32_MCG_EIP: 0x%08x\n", rdmsr32(IA32_MCG_EIP)); | |
242 | kdb_printf(" IA32_MCG_MISC: 0x%08x\n", rdmsr32(IA32_MCG_MISC)); | |
243 | } | |
244 | ||
cf7d32b8 A |
245 | static void |
246 | mca_report_cpu_info(void) | |
247 | { | |
248 | uint64_t microcode; | |
249 | i386_cpu_info_t *infop = cpuid_info(); | |
250 | ||
251 | // microcode revision is top 32 bits of MSR_IA32_UCODE_REV | |
252 | microcode = rdmsr64(MSR_IA32_UCODE_REV) >> 32; | |
253 | kdb_printf("family: %d model: %d stepping: %d microcode revision %d\n", | |
254 | infop->cpuid_family, | |
255 | infop->cpuid_model, | |
256 | infop->cpuid_stepping, | |
257 | (uint32_t) microcode); | |
258 | kdb_printf("%s\n", infop->cpuid_brand_string); | |
259 | } | |
260 | ||
261 | ||
0c530ab8 A |
262 | static const char *mca_threshold_status[] = { |
263 | [THRESHOLD_STATUS_NO_TRACKING] "No tracking", | |
264 | [THRESHOLD_STATUS_GREEN] "Green", | |
265 | [THRESHOLD_STATUS_YELLOW] "Yellow", | |
266 | [THRESHOLD_STATUS_RESERVED] "Reserved" | |
267 | }; | |
268 | ||
269 | static void | |
270 | mca_dump_error_banks(void) | |
271 | { | |
272 | unsigned int i; | |
273 | ia32_mci_status_t status; | |
274 | ||
275 | kdb_printf("MCA error-reporting registers:\n"); | |
276 | for (i = 0; i < mca_error_bank_count; i++ ) { | |
277 | status.u64 = rdmsr64(IA32_MCi_STATUS(i)); | |
278 | kdb_printf( | |
279 | " IA32_MC%d_STATUS(0x%x): 0x%016qx %svalid\n", | |
280 | i, IA32_MCi_STATUS(i), status.u64, | |
281 | IF(!status.bits.val, "in")); | |
282 | if (!status.bits.val) | |
283 | continue; | |
284 | kdb_printf( | |
285 | " MCA error code : 0x%04x\n", | |
286 | status.bits.mca_error); | |
287 | kdb_printf( | |
288 | " Model specific error code: 0x%04x\n", | |
289 | status.bits.model_specific_error); | |
290 | if (!mca_threshold_status_present) { | |
291 | kdb_printf( | |
292 | " Other information : 0x%08x\n", | |
293 | status.bits.other_information); | |
294 | } else { | |
295 | int threshold = status.bits_tes_p.threshold; | |
296 | kdb_printf( | |
297 | " Other information : 0x%08x\n" | |
298 | " Threshold-based status : %s\n", | |
299 | status.bits_tes_p.other_information, | |
300 | (status.bits_tes_p.uc == 0) ? | |
301 | mca_threshold_status[threshold] : | |
302 | "Undefined"); | |
303 | } | |
304 | kdb_printf( | |
305 | " Status bits:\n%s%s%s%s%s%s", | |
306 | IF(status.bits.pcc, " Processor context corrupt\n"), | |
307 | IF(status.bits.addrv, " ADDR register valid\n"), | |
308 | IF(status.bits.miscv, " MISC register valid\n"), | |
309 | IF(status.bits.en, " Error enabled\n"), | |
310 | IF(status.bits.uc, " Uncorrected error\n"), | |
311 | IF(status.bits.over, " Error overflow\n")); | |
312 | if (status.bits.addrv) | |
313 | kdb_printf( | |
314 | " IA32_MC%d_ADDR(0x%x): 0x%016qx\n", | |
315 | i, IA32_MCi_ADDR(i), rdmsr64(IA32_MCi_ADDR(i))); | |
316 | if (status.bits.miscv) | |
317 | kdb_printf( | |
318 | " IA32_MC%d_MISC(0x%x): 0x%016qx\n", | |
319 | i, IA32_MCi_MISC(i), rdmsr64(IA32_MCi_MISC(i))); | |
320 | } | |
321 | } | |
322 | ||
323 | void | |
324 | mca_dump(void) | |
325 | { | |
326 | ia32_mcg_status_t status; | |
327 | ||
2d21ac55 A |
328 | mca_save_state(); |
329 | ||
4a3eedf9 A |
330 | /* |
331 | * Serialize in case of multiple simultaneous machine-checks. | |
332 | * Only the first caller is allowed to print MCA registers. | |
333 | */ | |
2d21ac55 | 334 | simple_lock(&mca_lock); |
4a3eedf9 A |
335 | if (mca_exception_taken) { |
336 | simple_unlock(&mca_lock); | |
337 | return; | |
338 | } | |
339 | mca_exception_taken = TRUE; | |
2d21ac55 | 340 | |
0c530ab8 A |
341 | /* |
342 | * Report machine-check capabilities: | |
343 | */ | |
344 | kdb_printf( | |
2d21ac55 A |
345 | "Machine-check capabilities (cpu %d) 0x%016qx:\n", |
346 | cpu_number(), ia32_mcg_cap.u64); | |
cf7d32b8 A |
347 | |
348 | mca_report_cpu_info(); | |
349 | ||
0c530ab8 A |
350 | kdb_printf( |
351 | " %d error-reporting banks\n%s%s", mca_error_bank_count, | |
352 | IF(mca_control_MSR_present, | |
353 | " control MSR present\n"), | |
354 | IF(mca_threshold_status_present, | |
355 | " threshold-based error status present\n")); | |
356 | if (mca_extended_MSRs_present) | |
357 | kdb_printf( | |
358 | " %d extended MSRs present\n", mca_extended_MSRs_count); | |
359 | ||
360 | /* | |
361 | * Report machine-check status: | |
362 | */ | |
363 | status.u64 = rdmsr64(IA32_MCG_STATUS); | |
364 | kdb_printf( | |
365 | "Machine-check status 0x%016qx\n%s%s%s", status.u64, | |
366 | IF(status.bits.ripv, " restart IP valid\n"), | |
367 | IF(status.bits.eipv, " error IP valid\n"), | |
368 | IF(status.bits.mcip, " machine-check in progress\n")); | |
369 | ||
370 | /* | |
371 | * Dump error-reporting registers: | |
372 | */ | |
373 | mca_dump_error_banks(); | |
374 | ||
375 | /* | |
376 | * Dump any extended machine state: | |
377 | */ | |
378 | if (mca_extended_MSRs_present) { | |
379 | if (cpu_mode_is64bit()) | |
380 | mca_dump_64bit_state(); | |
381 | else | |
382 | mca_dump_32bit_state(); | |
383 | } | |
2d21ac55 A |
384 | |
385 | simple_unlock(&mca_lock); | |
0c530ab8 | 386 | } |