1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * UEFI Common Platform Error Record (CPER) support |
4 | * |
5 | * Copyright (C) 2017, The Linux Foundation. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/time.h> |
11 | #include <linux/cper.h> |
12 | #include <linux/dmi.h> |
13 | #include <linux/acpi.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/printk.h> |
16 | #include <linux/bcd.h> |
17 | #include <acpi/ghes.h> |
18 | #include <ras/ras_event.h> |
19 | |
20 | static const char * const arm_reg_ctx_strs[] = { |
21 | "AArch32 general purpose registers" , |
22 | "AArch32 EL1 context registers" , |
23 | "AArch32 EL2 context registers" , |
24 | "AArch32 secure context registers" , |
25 | "AArch64 general purpose registers" , |
26 | "AArch64 EL1 context registers" , |
27 | "AArch64 EL2 context registers" , |
28 | "AArch64 EL3 context registers" , |
29 | "Misc. system register structure" , |
30 | }; |
31 | |
32 | static const char * const arm_err_trans_type_strs[] = { |
33 | "Instruction" , |
34 | "Data Access" , |
35 | "Generic" , |
36 | }; |
37 | |
38 | static const char * const arm_bus_err_op_strs[] = { |
39 | "Generic error (type cannot be determined)" , |
40 | "Generic read (type of instruction or data request cannot be determined)" , |
41 | "Generic write (type of instruction of data request cannot be determined)" , |
42 | "Data read" , |
43 | "Data write" , |
44 | "Instruction fetch" , |
45 | "Prefetch" , |
46 | }; |
47 | |
48 | static const char * const arm_cache_err_op_strs[] = { |
49 | "Generic error (type cannot be determined)" , |
50 | "Generic read (type of instruction or data request cannot be determined)" , |
51 | "Generic write (type of instruction of data request cannot be determined)" , |
52 | "Data read" , |
53 | "Data write" , |
54 | "Instruction fetch" , |
55 | "Prefetch" , |
56 | "Eviction" , |
57 | "Snooping (processor initiated a cache snoop that resulted in an error)" , |
58 | "Snooped (processor raised a cache error caused by another processor or device snooping its cache)" , |
59 | "Management" , |
60 | }; |
61 | |
62 | static const char * const arm_tlb_err_op_strs[] = { |
63 | "Generic error (type cannot be determined)" , |
64 | "Generic read (type of instruction or data request cannot be determined)" , |
65 | "Generic write (type of instruction of data request cannot be determined)" , |
66 | "Data read" , |
67 | "Data write" , |
68 | "Instruction fetch" , |
69 | "Prefetch" , |
70 | "Local management operation (processor initiated a TLB management operation that resulted in an error)" , |
71 | "External management operation (processor raised a TLB error caused by another processor or device broadcasting TLB operations)" , |
72 | }; |
73 | |
74 | static const char * const arm_bus_err_part_type_strs[] = { |
75 | "Local processor originated request" , |
76 | "Local processor responded to request" , |
77 | "Local processor observed" , |
78 | "Generic" , |
79 | }; |
80 | |
81 | static const char * const arm_bus_err_addr_space_strs[] = { |
82 | "External Memory Access" , |
83 | "Internal Memory Access" , |
84 | "Unknown" , |
85 | "Device Memory Access" , |
86 | }; |
87 | |
88 | static void cper_print_arm_err_info(const char *pfx, u32 type, |
89 | u64 error_info) |
90 | { |
91 | u8 trans_type, op_type, level, participation_type, address_space; |
92 | u16 mem_attributes; |
93 | bool proc_context_corrupt, corrected, precise_pc, restartable_pc; |
94 | bool time_out, access_mode; |
95 | |
96 | /* If the type is unknown, bail. */ |
97 | if (type > CPER_ARM_MAX_TYPE) |
98 | return; |
99 | |
100 | /* |
101 | * Vendor type errors have error information values that are vendor |
102 | * specific. |
103 | */ |
104 | if (type == CPER_ARM_VENDOR_ERROR) |
105 | return; |
106 | |
107 | if (error_info & CPER_ARM_ERR_VALID_TRANSACTION_TYPE) { |
108 | trans_type = ((error_info >> CPER_ARM_ERR_TRANSACTION_SHIFT) |
109 | & CPER_ARM_ERR_TRANSACTION_MASK); |
110 | if (trans_type < ARRAY_SIZE(arm_err_trans_type_strs)) { |
111 | printk("%stransaction type: %s\n" , pfx, |
112 | arm_err_trans_type_strs[trans_type]); |
113 | } |
114 | } |
115 | |
116 | if (error_info & CPER_ARM_ERR_VALID_OPERATION_TYPE) { |
117 | op_type = ((error_info >> CPER_ARM_ERR_OPERATION_SHIFT) |
118 | & CPER_ARM_ERR_OPERATION_MASK); |
119 | switch (type) { |
120 | case CPER_ARM_CACHE_ERROR: |
121 | if (op_type < ARRAY_SIZE(arm_cache_err_op_strs)) { |
122 | printk("%soperation type: %s\n" , pfx, |
123 | arm_cache_err_op_strs[op_type]); |
124 | } |
125 | break; |
126 | case CPER_ARM_TLB_ERROR: |
127 | if (op_type < ARRAY_SIZE(arm_tlb_err_op_strs)) { |
128 | printk("%soperation type: %s\n" , pfx, |
129 | arm_tlb_err_op_strs[op_type]); |
130 | } |
131 | break; |
132 | case CPER_ARM_BUS_ERROR: |
133 | if (op_type < ARRAY_SIZE(arm_bus_err_op_strs)) { |
134 | printk("%soperation type: %s\n" , pfx, |
135 | arm_bus_err_op_strs[op_type]); |
136 | } |
137 | break; |
138 | } |
139 | } |
140 | |
141 | if (error_info & CPER_ARM_ERR_VALID_LEVEL) { |
142 | level = ((error_info >> CPER_ARM_ERR_LEVEL_SHIFT) |
143 | & CPER_ARM_ERR_LEVEL_MASK); |
144 | switch (type) { |
145 | case CPER_ARM_CACHE_ERROR: |
146 | printk("%scache level: %d\n" , pfx, level); |
147 | break; |
148 | case CPER_ARM_TLB_ERROR: |
149 | printk("%sTLB level: %d\n" , pfx, level); |
150 | break; |
151 | case CPER_ARM_BUS_ERROR: |
152 | printk("%saffinity level at which the bus error occurred: %d\n" , |
153 | pfx, level); |
154 | break; |
155 | } |
156 | } |
157 | |
158 | if (error_info & CPER_ARM_ERR_VALID_PROC_CONTEXT_CORRUPT) { |
159 | proc_context_corrupt = ((error_info >> CPER_ARM_ERR_PC_CORRUPT_SHIFT) |
160 | & CPER_ARM_ERR_PC_CORRUPT_MASK); |
161 | if (proc_context_corrupt) |
162 | printk("%sprocessor context corrupted\n" , pfx); |
163 | else |
164 | printk("%sprocessor context not corrupted\n" , pfx); |
165 | } |
166 | |
167 | if (error_info & CPER_ARM_ERR_VALID_CORRECTED) { |
168 | corrected = ((error_info >> CPER_ARM_ERR_CORRECTED_SHIFT) |
169 | & CPER_ARM_ERR_CORRECTED_MASK); |
170 | if (corrected) |
171 | printk("%sthe error has been corrected\n" , pfx); |
172 | else |
173 | printk("%sthe error has not been corrected\n" , pfx); |
174 | } |
175 | |
176 | if (error_info & CPER_ARM_ERR_VALID_PRECISE_PC) { |
177 | precise_pc = ((error_info >> CPER_ARM_ERR_PRECISE_PC_SHIFT) |
178 | & CPER_ARM_ERR_PRECISE_PC_MASK); |
179 | if (precise_pc) |
180 | printk("%sPC is precise\n" , pfx); |
181 | else |
182 | printk("%sPC is imprecise\n" , pfx); |
183 | } |
184 | |
185 | if (error_info & CPER_ARM_ERR_VALID_RESTARTABLE_PC) { |
186 | restartable_pc = ((error_info >> CPER_ARM_ERR_RESTARTABLE_PC_SHIFT) |
187 | & CPER_ARM_ERR_RESTARTABLE_PC_MASK); |
188 | if (restartable_pc) |
189 | printk("%sProgram execution can be restarted reliably at the PC associated with the error.\n" , pfx); |
190 | } |
191 | |
192 | /* The rest of the fields are specific to bus errors */ |
193 | if (type != CPER_ARM_BUS_ERROR) |
194 | return; |
195 | |
196 | if (error_info & CPER_ARM_ERR_VALID_PARTICIPATION_TYPE) { |
197 | participation_type = ((error_info >> CPER_ARM_ERR_PARTICIPATION_TYPE_SHIFT) |
198 | & CPER_ARM_ERR_PARTICIPATION_TYPE_MASK); |
199 | if (participation_type < ARRAY_SIZE(arm_bus_err_part_type_strs)) { |
200 | printk("%sparticipation type: %s\n" , pfx, |
201 | arm_bus_err_part_type_strs[participation_type]); |
202 | } |
203 | } |
204 | |
205 | if (error_info & CPER_ARM_ERR_VALID_TIME_OUT) { |
206 | time_out = ((error_info >> CPER_ARM_ERR_TIME_OUT_SHIFT) |
207 | & CPER_ARM_ERR_TIME_OUT_MASK); |
208 | if (time_out) |
209 | printk("%srequest timed out\n" , pfx); |
210 | } |
211 | |
212 | if (error_info & CPER_ARM_ERR_VALID_ADDRESS_SPACE) { |
213 | address_space = ((error_info >> CPER_ARM_ERR_ADDRESS_SPACE_SHIFT) |
214 | & CPER_ARM_ERR_ADDRESS_SPACE_MASK); |
215 | if (address_space < ARRAY_SIZE(arm_bus_err_addr_space_strs)) { |
216 | printk("%saddress space: %s\n" , pfx, |
217 | arm_bus_err_addr_space_strs[address_space]); |
218 | } |
219 | } |
220 | |
221 | if (error_info & CPER_ARM_ERR_VALID_MEM_ATTRIBUTES) { |
222 | mem_attributes = ((error_info >> CPER_ARM_ERR_MEM_ATTRIBUTES_SHIFT) |
223 | & CPER_ARM_ERR_MEM_ATTRIBUTES_MASK); |
224 | printk("%smemory access attributes:0x%x\n" , pfx, mem_attributes); |
225 | } |
226 | |
227 | if (error_info & CPER_ARM_ERR_VALID_ACCESS_MODE) { |
228 | access_mode = ((error_info >> CPER_ARM_ERR_ACCESS_MODE_SHIFT) |
229 | & CPER_ARM_ERR_ACCESS_MODE_MASK); |
230 | if (access_mode) |
231 | printk("%saccess mode: normal\n" , pfx); |
232 | else |
233 | printk("%saccess mode: secure\n" , pfx); |
234 | } |
235 | } |
236 | |
237 | void cper_print_proc_arm(const char *pfx, |
238 | const struct cper_sec_proc_arm *proc) |
239 | { |
240 | int i, len, max_ctx_type; |
241 | struct cper_arm_err_info *err_info; |
242 | struct cper_arm_ctx_info *ctx_info; |
243 | char newpfx[64], infopfx[64]; |
244 | |
245 | printk("%sMIDR: 0x%016llx\n" , pfx, proc->midr); |
246 | |
247 | len = proc->section_length - (sizeof(*proc) + |
248 | proc->err_info_num * (sizeof(*err_info))); |
249 | if (len < 0) { |
250 | printk("%ssection length: %d\n" , pfx, proc->section_length); |
251 | printk("%ssection length is too small\n" , pfx); |
252 | printk("%sfirmware-generated error record is incorrect\n" , pfx); |
253 | printk("%sERR_INFO_NUM is %d\n" , pfx, proc->err_info_num); |
254 | return; |
255 | } |
256 | |
257 | if (proc->validation_bits & CPER_ARM_VALID_MPIDR) |
258 | printk("%sMultiprocessor Affinity Register (MPIDR): 0x%016llx\n" , |
259 | pfx, proc->mpidr); |
260 | |
261 | if (proc->validation_bits & CPER_ARM_VALID_AFFINITY_LEVEL) |
262 | printk("%serror affinity level: %d\n" , pfx, |
263 | proc->affinity_level); |
264 | |
265 | if (proc->validation_bits & CPER_ARM_VALID_RUNNING_STATE) { |
266 | printk("%srunning state: 0x%x\n" , pfx, proc->running_state); |
267 | printk("%sPower State Coordination Interface state: %d\n" , |
268 | pfx, proc->psci_state); |
269 | } |
270 | |
271 | snprintf(buf: newpfx, size: sizeof(newpfx), fmt: "%s " , pfx); |
272 | |
273 | err_info = (struct cper_arm_err_info *)(proc + 1); |
274 | for (i = 0; i < proc->err_info_num; i++) { |
275 | printk("%sError info structure %d:\n" , pfx, i); |
276 | |
277 | printk("%snum errors: %d\n" , pfx, err_info->multiple_error + 1); |
278 | |
279 | if (err_info->validation_bits & CPER_ARM_INFO_VALID_FLAGS) { |
280 | if (err_info->flags & CPER_ARM_INFO_FLAGS_FIRST) |
281 | printk("%sfirst error captured\n" , newpfx); |
282 | if (err_info->flags & CPER_ARM_INFO_FLAGS_LAST) |
283 | printk("%slast error captured\n" , newpfx); |
284 | if (err_info->flags & CPER_ARM_INFO_FLAGS_PROPAGATED) |
285 | printk("%spropagated error captured\n" , |
286 | newpfx); |
287 | if (err_info->flags & CPER_ARM_INFO_FLAGS_OVERFLOW) |
288 | printk("%soverflow occurred, error info is incomplete\n" , |
289 | newpfx); |
290 | } |
291 | |
292 | printk("%serror_type: %d, %s\n" , newpfx, err_info->type, |
293 | err_info->type < ARRAY_SIZE(cper_proc_error_type_strs) ? |
294 | cper_proc_error_type_strs[err_info->type] : "unknown" ); |
295 | if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO) { |
296 | printk("%serror_info: 0x%016llx\n" , newpfx, |
297 | err_info->error_info); |
298 | snprintf(buf: infopfx, size: sizeof(infopfx), fmt: "%s " , newpfx); |
299 | cper_print_arm_err_info(pfx: infopfx, type: err_info->type, |
300 | error_info: err_info->error_info); |
301 | } |
302 | if (err_info->validation_bits & CPER_ARM_INFO_VALID_VIRT_ADDR) |
303 | printk("%svirtual fault address: 0x%016llx\n" , |
304 | newpfx, err_info->virt_fault_addr); |
305 | if (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR) |
306 | printk("%sphysical fault address: 0x%016llx\n" , |
307 | newpfx, err_info->physical_fault_addr); |
308 | err_info += 1; |
309 | } |
310 | |
311 | ctx_info = (struct cper_arm_ctx_info *)err_info; |
312 | max_ctx_type = ARRAY_SIZE(arm_reg_ctx_strs) - 1; |
313 | for (i = 0; i < proc->context_info_num; i++) { |
314 | int size = sizeof(*ctx_info) + ctx_info->size; |
315 | |
316 | printk("%sContext info structure %d:\n" , pfx, i); |
317 | if (len < size) { |
318 | printk("%ssection length is too small\n" , newpfx); |
319 | printk("%sfirmware-generated error record is incorrect\n" , pfx); |
320 | return; |
321 | } |
322 | if (ctx_info->type > max_ctx_type) { |
323 | printk("%sInvalid context type: %d (max: %d)\n" , |
324 | newpfx, ctx_info->type, max_ctx_type); |
325 | return; |
326 | } |
327 | printk("%sregister context type: %s\n" , newpfx, |
328 | arm_reg_ctx_strs[ctx_info->type]); |
329 | print_hex_dump(level: newpfx, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 16, groupsize: 4, |
330 | buf: (ctx_info + 1), len: ctx_info->size, ascii: 0); |
331 | len -= size; |
332 | ctx_info = (struct cper_arm_ctx_info *)((long)ctx_info + size); |
333 | } |
334 | |
335 | if (len > 0) { |
336 | printk("%sVendor specific error info has %u bytes:\n" , pfx, |
337 | len); |
338 | print_hex_dump(level: newpfx, prefix_str: "" , prefix_type: DUMP_PREFIX_OFFSET, rowsize: 16, groupsize: 4, buf: ctx_info, |
339 | len, ascii: true); |
340 | } |
341 | } |
342 | |