1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2018, Advanced Micro Devices, Inc. |
3 | |
4 | #include <linux/cper.h> |
5 | #include <linux/acpi.h> |
6 | |
7 | /* |
8 | * We don't need a "CPER_IA" prefix since these are all locally defined. |
9 | * This will save us a lot of line space. |
10 | */ |
11 | #define VALID_LAPIC_ID BIT_ULL(0) |
12 | #define VALID_CPUID_INFO BIT_ULL(1) |
13 | #define VALID_PROC_ERR_INFO_NUM(bits) (((bits) & GENMASK_ULL(7, 2)) >> 2) |
14 | #define VALID_PROC_CXT_INFO_NUM(bits) (((bits) & GENMASK_ULL(13, 8)) >> 8) |
15 | |
16 | #define INFO_ERR_STRUCT_TYPE_CACHE \ |
17 | GUID_INIT(0xA55701F5, 0xE3EF, 0x43DE, 0xAC, 0x72, 0x24, 0x9B, \ |
18 | 0x57, 0x3F, 0xAD, 0x2C) |
19 | #define INFO_ERR_STRUCT_TYPE_TLB \ |
20 | GUID_INIT(0xFC06B535, 0x5E1F, 0x4562, 0x9F, 0x25, 0x0A, 0x3B, \ |
21 | 0x9A, 0xDB, 0x63, 0xC3) |
22 | #define INFO_ERR_STRUCT_TYPE_BUS \ |
23 | GUID_INIT(0x1CF3F8B3, 0xC5B1, 0x49a2, 0xAA, 0x59, 0x5E, 0xEF, \ |
24 | 0x92, 0xFF, 0xA6, 0x3C) |
25 | #define INFO_ERR_STRUCT_TYPE_MS \ |
26 | GUID_INIT(0x48AB7F57, 0xDC34, 0x4f6c, 0xA7, 0xD3, 0xB0, 0xB5, \ |
27 | 0xB0, 0xA7, 0x43, 0x14) |
28 | |
29 | #define INFO_VALID_CHECK_INFO BIT_ULL(0) |
30 | #define INFO_VALID_TARGET_ID BIT_ULL(1) |
31 | #define INFO_VALID_REQUESTOR_ID BIT_ULL(2) |
32 | #define INFO_VALID_RESPONDER_ID BIT_ULL(3) |
33 | #define INFO_VALID_IP BIT_ULL(4) |
34 | |
35 | #define CHECK_VALID_TRANS_TYPE BIT_ULL(0) |
36 | #define CHECK_VALID_OPERATION BIT_ULL(1) |
37 | #define CHECK_VALID_LEVEL BIT_ULL(2) |
38 | #define CHECK_VALID_PCC BIT_ULL(3) |
39 | #define CHECK_VALID_UNCORRECTED BIT_ULL(4) |
40 | #define CHECK_VALID_PRECISE_IP BIT_ULL(5) |
41 | #define CHECK_VALID_RESTARTABLE_IP BIT_ULL(6) |
42 | #define CHECK_VALID_OVERFLOW BIT_ULL(7) |
43 | |
44 | #define CHECK_VALID_BUS_PART_TYPE BIT_ULL(8) |
45 | #define CHECK_VALID_BUS_TIME_OUT BIT_ULL(9) |
46 | #define CHECK_VALID_BUS_ADDR_SPACE BIT_ULL(10) |
47 | |
48 | #define CHECK_VALID_BITS(check) (((check) & GENMASK_ULL(15, 0))) |
49 | #define CHECK_TRANS_TYPE(check) (((check) & GENMASK_ULL(17, 16)) >> 16) |
50 | #define CHECK_OPERATION(check) (((check) & GENMASK_ULL(21, 18)) >> 18) |
51 | #define CHECK_LEVEL(check) (((check) & GENMASK_ULL(24, 22)) >> 22) |
52 | #define CHECK_PCC BIT_ULL(25) |
53 | #define CHECK_UNCORRECTED BIT_ULL(26) |
54 | #define CHECK_PRECISE_IP BIT_ULL(27) |
55 | #define CHECK_RESTARTABLE_IP BIT_ULL(28) |
56 | #define CHECK_OVERFLOW BIT_ULL(29) |
57 | |
58 | #define CHECK_BUS_PART_TYPE(check) (((check) & GENMASK_ULL(31, 30)) >> 30) |
59 | #define CHECK_BUS_TIME_OUT BIT_ULL(32) |
60 | #define CHECK_BUS_ADDR_SPACE(check) (((check) & GENMASK_ULL(34, 33)) >> 33) |
61 | |
62 | #define CHECK_VALID_MS_ERR_TYPE BIT_ULL(0) |
63 | #define CHECK_VALID_MS_PCC BIT_ULL(1) |
64 | #define CHECK_VALID_MS_UNCORRECTED BIT_ULL(2) |
65 | #define CHECK_VALID_MS_PRECISE_IP BIT_ULL(3) |
66 | #define CHECK_VALID_MS_RESTARTABLE_IP BIT_ULL(4) |
67 | #define CHECK_VALID_MS_OVERFLOW BIT_ULL(5) |
68 | |
69 | #define CHECK_MS_ERR_TYPE(check) (((check) & GENMASK_ULL(18, 16)) >> 16) |
70 | #define CHECK_MS_PCC BIT_ULL(19) |
71 | #define CHECK_MS_UNCORRECTED BIT_ULL(20) |
72 | #define CHECK_MS_PRECISE_IP BIT_ULL(21) |
73 | #define CHECK_MS_RESTARTABLE_IP BIT_ULL(22) |
74 | #define CHECK_MS_OVERFLOW BIT_ULL(23) |
75 | |
76 | #define CTX_TYPE_MSR 1 |
77 | #define CTX_TYPE_MMREG 7 |
78 | |
79 | enum err_types { |
80 | ERR_TYPE_CACHE = 0, |
81 | ERR_TYPE_TLB, |
82 | ERR_TYPE_BUS, |
83 | ERR_TYPE_MS, |
84 | N_ERR_TYPES |
85 | }; |
86 | |
87 | static enum err_types cper_get_err_type(const guid_t *err_type) |
88 | { |
89 | if (guid_equal(u1: err_type, u2: &INFO_ERR_STRUCT_TYPE_CACHE)) |
90 | return ERR_TYPE_CACHE; |
91 | else if (guid_equal(u1: err_type, u2: &INFO_ERR_STRUCT_TYPE_TLB)) |
92 | return ERR_TYPE_TLB; |
93 | else if (guid_equal(u1: err_type, u2: &INFO_ERR_STRUCT_TYPE_BUS)) |
94 | return ERR_TYPE_BUS; |
95 | else if (guid_equal(u1: err_type, u2: &INFO_ERR_STRUCT_TYPE_MS)) |
96 | return ERR_TYPE_MS; |
97 | else |
98 | return N_ERR_TYPES; |
99 | } |
100 | |
101 | static const char * const ia_check_trans_type_strs[] = { |
102 | "Instruction" , |
103 | "Data Access" , |
104 | "Generic" , |
105 | }; |
106 | |
107 | static const char * const ia_check_op_strs[] = { |
108 | "generic error" , |
109 | "generic read" , |
110 | "generic write" , |
111 | "data read" , |
112 | "data write" , |
113 | "instruction fetch" , |
114 | "prefetch" , |
115 | "eviction" , |
116 | "snoop" , |
117 | }; |
118 | |
119 | static const char * const ia_check_bus_part_type_strs[] = { |
120 | "Local Processor originated request" , |
121 | "Local Processor responded to request" , |
122 | "Local Processor observed" , |
123 | "Generic" , |
124 | }; |
125 | |
126 | static const char * const ia_check_bus_addr_space_strs[] = { |
127 | "Memory Access" , |
128 | "Reserved" , |
129 | "I/O" , |
130 | "Other Transaction" , |
131 | }; |
132 | |
133 | static const char * const ia_check_ms_error_type_strs[] = { |
134 | "No Error" , |
135 | "Unclassified" , |
136 | "Microcode ROM Parity Error" , |
137 | "External Error" , |
138 | "FRC Error" , |
139 | "Internal Unclassified" , |
140 | }; |
141 | |
142 | static const char * const ia_reg_ctx_strs[] = { |
143 | "Unclassified Data" , |
144 | "MSR Registers (Machine Check and other MSRs)" , |
145 | "32-bit Mode Execution Context" , |
146 | "64-bit Mode Execution Context" , |
147 | "FXSAVE Context" , |
148 | "32-bit Mode Debug Registers (DR0-DR7)" , |
149 | "64-bit Mode Debug Registers (DR0-DR7)" , |
150 | "Memory Mapped Registers" , |
151 | }; |
152 | |
153 | static inline void print_bool(char *str, const char *pfx, u64 check, u64 bit) |
154 | { |
155 | printk("%s%s: %s\n" , pfx, str, (check & bit) ? "true" : "false" ); |
156 | } |
157 | |
158 | static void print_err_info_ms(const char *pfx, u16 validation_bits, u64 check) |
159 | { |
160 | if (validation_bits & CHECK_VALID_MS_ERR_TYPE) { |
161 | u8 err_type = CHECK_MS_ERR_TYPE(check); |
162 | |
163 | printk("%sError Type: %u, %s\n" , pfx, err_type, |
164 | err_type < ARRAY_SIZE(ia_check_ms_error_type_strs) ? |
165 | ia_check_ms_error_type_strs[err_type] : "unknown" ); |
166 | } |
167 | |
168 | if (validation_bits & CHECK_VALID_MS_PCC) |
169 | print_bool(str: "Processor Context Corrupt" , pfx, check, CHECK_MS_PCC); |
170 | |
171 | if (validation_bits & CHECK_VALID_MS_UNCORRECTED) |
172 | print_bool(str: "Uncorrected" , pfx, check, CHECK_MS_UNCORRECTED); |
173 | |
174 | if (validation_bits & CHECK_VALID_MS_PRECISE_IP) |
175 | print_bool(str: "Precise IP" , pfx, check, CHECK_MS_PRECISE_IP); |
176 | |
177 | if (validation_bits & CHECK_VALID_MS_RESTARTABLE_IP) |
178 | print_bool(str: "Restartable IP" , pfx, check, CHECK_MS_RESTARTABLE_IP); |
179 | |
180 | if (validation_bits & CHECK_VALID_MS_OVERFLOW) |
181 | print_bool(str: "Overflow" , pfx, check, CHECK_MS_OVERFLOW); |
182 | } |
183 | |
184 | static void print_err_info(const char *pfx, u8 err_type, u64 check) |
185 | { |
186 | u16 validation_bits = CHECK_VALID_BITS(check); |
187 | |
188 | /* |
189 | * The MS Check structure varies a lot from the others, so use a |
190 | * separate function for decoding. |
191 | */ |
192 | if (err_type == ERR_TYPE_MS) |
193 | return print_err_info_ms(pfx, validation_bits, check); |
194 | |
195 | if (validation_bits & CHECK_VALID_TRANS_TYPE) { |
196 | u8 trans_type = CHECK_TRANS_TYPE(check); |
197 | |
198 | printk("%sTransaction Type: %u, %s\n" , pfx, trans_type, |
199 | trans_type < ARRAY_SIZE(ia_check_trans_type_strs) ? |
200 | ia_check_trans_type_strs[trans_type] : "unknown" ); |
201 | } |
202 | |
203 | if (validation_bits & CHECK_VALID_OPERATION) { |
204 | u8 op = CHECK_OPERATION(check); |
205 | |
206 | /* |
207 | * CACHE has more operation types than TLB or BUS, though the |
208 | * name and the order are the same. |
209 | */ |
210 | u8 max_ops = (err_type == ERR_TYPE_CACHE) ? 9 : 7; |
211 | |
212 | printk("%sOperation: %u, %s\n" , pfx, op, |
213 | op < max_ops ? ia_check_op_strs[op] : "unknown" ); |
214 | } |
215 | |
216 | if (validation_bits & CHECK_VALID_LEVEL) |
217 | printk("%sLevel: %llu\n" , pfx, CHECK_LEVEL(check)); |
218 | |
219 | if (validation_bits & CHECK_VALID_PCC) |
220 | print_bool(str: "Processor Context Corrupt" , pfx, check, CHECK_PCC); |
221 | |
222 | if (validation_bits & CHECK_VALID_UNCORRECTED) |
223 | print_bool(str: "Uncorrected" , pfx, check, CHECK_UNCORRECTED); |
224 | |
225 | if (validation_bits & CHECK_VALID_PRECISE_IP) |
226 | print_bool(str: "Precise IP" , pfx, check, CHECK_PRECISE_IP); |
227 | |
228 | if (validation_bits & CHECK_VALID_RESTARTABLE_IP) |
229 | print_bool(str: "Restartable IP" , pfx, check, CHECK_RESTARTABLE_IP); |
230 | |
231 | if (validation_bits & CHECK_VALID_OVERFLOW) |
232 | print_bool(str: "Overflow" , pfx, check, CHECK_OVERFLOW); |
233 | |
234 | if (err_type != ERR_TYPE_BUS) |
235 | return; |
236 | |
237 | if (validation_bits & CHECK_VALID_BUS_PART_TYPE) { |
238 | u8 part_type = CHECK_BUS_PART_TYPE(check); |
239 | |
240 | printk("%sParticipation Type: %u, %s\n" , pfx, part_type, |
241 | part_type < ARRAY_SIZE(ia_check_bus_part_type_strs) ? |
242 | ia_check_bus_part_type_strs[part_type] : "unknown" ); |
243 | } |
244 | |
245 | if (validation_bits & CHECK_VALID_BUS_TIME_OUT) |
246 | print_bool(str: "Time Out" , pfx, check, CHECK_BUS_TIME_OUT); |
247 | |
248 | if (validation_bits & CHECK_VALID_BUS_ADDR_SPACE) { |
249 | u8 addr_space = CHECK_BUS_ADDR_SPACE(check); |
250 | |
251 | printk("%sAddress Space: %u, %s\n" , pfx, addr_space, |
252 | addr_space < ARRAY_SIZE(ia_check_bus_addr_space_strs) ? |
253 | ia_check_bus_addr_space_strs[addr_space] : "unknown" ); |
254 | } |
255 | } |
256 | |
257 | void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc) |
258 | { |
259 | int i; |
260 | struct cper_ia_err_info *err_info; |
261 | struct cper_ia_proc_ctx *ctx_info; |
262 | char newpfx[64], infopfx[64]; |
263 | u8 err_type; |
264 | |
265 | if (proc->validation_bits & VALID_LAPIC_ID) |
266 | printk("%sLocal APIC_ID: 0x%llx\n" , pfx, proc->lapic_id); |
267 | |
268 | if (proc->validation_bits & VALID_CPUID_INFO) { |
269 | printk("%sCPUID Info:\n" , pfx); |
270 | print_hex_dump(level: pfx, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 16, groupsize: 4, buf: proc->cpuid, |
271 | len: sizeof(proc->cpuid), ascii: 0); |
272 | } |
273 | |
274 | snprintf(buf: newpfx, size: sizeof(newpfx), fmt: "%s " , pfx); |
275 | |
276 | err_info = (struct cper_ia_err_info *)(proc + 1); |
277 | for (i = 0; i < VALID_PROC_ERR_INFO_NUM(proc->validation_bits); i++) { |
278 | printk("%sError Information Structure %d:\n" , pfx, i); |
279 | |
280 | err_type = cper_get_err_type(err_type: &err_info->err_type); |
281 | printk("%sError Structure Type: %s\n" , newpfx, |
282 | err_type < ARRAY_SIZE(cper_proc_error_type_strs) ? |
283 | cper_proc_error_type_strs[err_type] : "unknown" ); |
284 | |
285 | if (err_type >= N_ERR_TYPES) { |
286 | printk("%sError Structure Type: %pUl\n" , newpfx, |
287 | &err_info->err_type); |
288 | } |
289 | |
290 | if (err_info->validation_bits & INFO_VALID_CHECK_INFO) { |
291 | printk("%sCheck Information: 0x%016llx\n" , newpfx, |
292 | err_info->check_info); |
293 | |
294 | if (err_type < N_ERR_TYPES) { |
295 | snprintf(buf: infopfx, size: sizeof(infopfx), fmt: "%s " , |
296 | newpfx); |
297 | |
298 | print_err_info(pfx: infopfx, err_type, |
299 | check: err_info->check_info); |
300 | } |
301 | } |
302 | |
303 | if (err_info->validation_bits & INFO_VALID_TARGET_ID) { |
304 | printk("%sTarget Identifier: 0x%016llx\n" , |
305 | newpfx, err_info->target_id); |
306 | } |
307 | |
308 | if (err_info->validation_bits & INFO_VALID_REQUESTOR_ID) { |
309 | printk("%sRequestor Identifier: 0x%016llx\n" , |
310 | newpfx, err_info->requestor_id); |
311 | } |
312 | |
313 | if (err_info->validation_bits & INFO_VALID_RESPONDER_ID) { |
314 | printk("%sResponder Identifier: 0x%016llx\n" , |
315 | newpfx, err_info->responder_id); |
316 | } |
317 | |
318 | if (err_info->validation_bits & INFO_VALID_IP) { |
319 | printk("%sInstruction Pointer: 0x%016llx\n" , |
320 | newpfx, err_info->ip); |
321 | } |
322 | |
323 | err_info++; |
324 | } |
325 | |
326 | ctx_info = (struct cper_ia_proc_ctx *)err_info; |
327 | for (i = 0; i < VALID_PROC_CXT_INFO_NUM(proc->validation_bits); i++) { |
328 | int size = sizeof(*ctx_info) + ctx_info->reg_arr_size; |
329 | int groupsize = 4; |
330 | |
331 | printk("%sContext Information Structure %d:\n" , pfx, i); |
332 | |
333 | printk("%sRegister Context Type: %s\n" , newpfx, |
334 | ctx_info->reg_ctx_type < ARRAY_SIZE(ia_reg_ctx_strs) ? |
335 | ia_reg_ctx_strs[ctx_info->reg_ctx_type] : "unknown" ); |
336 | |
337 | printk("%sRegister Array Size: 0x%04x\n" , newpfx, |
338 | ctx_info->reg_arr_size); |
339 | |
340 | if (ctx_info->reg_ctx_type == CTX_TYPE_MSR) { |
341 | groupsize = 8; /* MSRs are 8 bytes wide. */ |
342 | printk("%sMSR Address: 0x%08x\n" , newpfx, |
343 | ctx_info->msr_addr); |
344 | } |
345 | |
346 | if (ctx_info->reg_ctx_type == CTX_TYPE_MMREG) { |
347 | printk("%sMM Register Address: 0x%016llx\n" , newpfx, |
348 | ctx_info->mm_reg_addr); |
349 | } |
350 | |
351 | if (ctx_info->reg_ctx_type != CTX_TYPE_MSR || |
352 | arch_apei_report_x86_error(ctx_info, lapic_id: proc->lapic_id)) { |
353 | printk("%sRegister Array:\n" , newpfx); |
354 | print_hex_dump(level: newpfx, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 16, |
355 | groupsize, buf: (ctx_info + 1), |
356 | len: ctx_info->reg_arr_size, ascii: 0); |
357 | } |
358 | |
359 | ctx_info = (struct cper_ia_proc_ctx *)((long)ctx_info + size); |
360 | } |
361 | } |
362 | |