.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * UEFI Common Platform Error Record (CPER) support |
---|
3 | 4 | * |
---|
.. | .. |
---|
9 | 10 | * |
---|
10 | 11 | * For more information about CPER, please refer to Appendix N of UEFI |
---|
11 | 12 | * Specification version 2.4. |
---|
12 | | - * |
---|
13 | | - * This program is free software; you can redistribute it and/or |
---|
14 | | - * modify it under the terms of the GNU General Public License version |
---|
15 | | - * 2 as published by the Free Software Foundation. |
---|
16 | | - * |
---|
17 | | - * This program is distributed in the hope that it will be useful, |
---|
18 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
19 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
20 | | - * GNU General Public License for more details. |
---|
21 | | - * |
---|
22 | | - * You should have received a copy of the GNU General Public License |
---|
23 | | - * along with this program; if not, write to the Free Software |
---|
24 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
25 | 13 | */ |
---|
26 | 14 | |
---|
27 | 15 | #include <linux/kernel.h> |
---|
.. | .. |
---|
111 | 99 | if (!len) |
---|
112 | 100 | len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); |
---|
113 | 101 | else |
---|
114 | | - len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); |
---|
| 102 | + len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str); |
---|
115 | 103 | } |
---|
116 | 104 | if (len) |
---|
117 | 105 | printk("%s\n", buf); |
---|
.. | .. |
---|
242 | 230 | n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); |
---|
243 | 231 | if (mem->validation_bits & CPER_MEM_VALID_BANK) |
---|
244 | 232 | n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); |
---|
| 233 | + if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) |
---|
| 234 | + n += scnprintf(msg + n, len - n, "bank_group: %d ", |
---|
| 235 | + mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); |
---|
| 236 | + if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) |
---|
| 237 | + n += scnprintf(msg + n, len - n, "bank_address: %d ", |
---|
| 238 | + mem->bank & CPER_MEM_BANK_ADDRESS_MASK); |
---|
245 | 239 | if (mem->validation_bits & CPER_MEM_VALID_DEVICE) |
---|
246 | 240 | n += scnprintf(msg + n, len - n, "device: %d ", mem->device); |
---|
247 | | - if (mem->validation_bits & CPER_MEM_VALID_ROW) |
---|
248 | | - n += scnprintf(msg + n, len - n, "row: %d ", mem->row); |
---|
| 241 | + if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { |
---|
| 242 | + u32 row = mem->row; |
---|
| 243 | + |
---|
| 244 | + row |= cper_get_mem_extension(mem->validation_bits, mem->extended); |
---|
| 245 | + n += scnprintf(msg + n, len - n, "row: %d ", row); |
---|
| 246 | + } |
---|
249 | 247 | if (mem->validation_bits & CPER_MEM_VALID_COLUMN) |
---|
250 | 248 | n += scnprintf(msg + n, len - n, "column: %d ", mem->column); |
---|
251 | 249 | if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) |
---|
.. | .. |
---|
260 | 258 | if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) |
---|
261 | 259 | scnprintf(msg + n, len - n, "target_id: 0x%016llx ", |
---|
262 | 260 | mem->target_id); |
---|
| 261 | + if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) |
---|
| 262 | + scnprintf(msg + n, len - n, "chip_id: %d ", |
---|
| 263 | + mem->extended >> CPER_MEM_CHIP_ID_SHIFT); |
---|
263 | 264 | |
---|
264 | 265 | msg[n] = '\0'; |
---|
265 | 266 | return n; |
---|
.. | .. |
---|
300 | 301 | cmem->requestor_id = mem->requestor_id; |
---|
301 | 302 | cmem->responder_id = mem->responder_id; |
---|
302 | 303 | cmem->target_id = mem->target_id; |
---|
| 304 | + cmem->extended = mem->extended; |
---|
303 | 305 | cmem->rank = mem->rank; |
---|
304 | 306 | cmem->mem_array_handle = mem->mem_array_handle; |
---|
305 | 307 | cmem->mem_dev_handle = mem->mem_dev_handle; |
---|
.. | .. |
---|
417 | 419 | } |
---|
418 | 420 | } |
---|
419 | 421 | |
---|
| 422 | +static const char * const fw_err_rec_type_strs[] = { |
---|
| 423 | + "IPF SAL Error Record", |
---|
| 424 | + "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", |
---|
| 425 | + "SOC Firmware Error Record Type2", |
---|
| 426 | +}; |
---|
| 427 | + |
---|
| 428 | +static void cper_print_fw_err(const char *pfx, |
---|
| 429 | + struct acpi_hest_generic_data *gdata, |
---|
| 430 | + const struct cper_sec_fw_err_rec_ref *fw_err) |
---|
| 431 | +{ |
---|
| 432 | + void *buf = acpi_hest_get_payload(gdata); |
---|
| 433 | + u32 offset, length = gdata->error_data_length; |
---|
| 434 | + |
---|
| 435 | + printk("%s""Firmware Error Record Type: %s\n", pfx, |
---|
| 436 | + fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? |
---|
| 437 | + fw_err_rec_type_strs[fw_err->record_type] : "unknown"); |
---|
| 438 | + printk("%s""Revision: %d\n", pfx, fw_err->revision); |
---|
| 439 | + |
---|
| 440 | + /* Record Type based on UEFI 2.7 */ |
---|
| 441 | + if (fw_err->revision == 0) { |
---|
| 442 | + printk("%s""Record Identifier: %08llx\n", pfx, |
---|
| 443 | + fw_err->record_identifier); |
---|
| 444 | + } else if (fw_err->revision == 2) { |
---|
| 445 | + printk("%s""Record Identifier: %pUl\n", pfx, |
---|
| 446 | + &fw_err->record_identifier_guid); |
---|
| 447 | + } |
---|
| 448 | + |
---|
| 449 | + /* |
---|
| 450 | + * The FW error record may contain trailing data beyond the |
---|
| 451 | + * structure defined by the specification. As the fields |
---|
| 452 | + * defined (and hence the offset of any trailing data) vary |
---|
| 453 | + * with the revision, set the offset to account for this |
---|
| 454 | + * variation. |
---|
| 455 | + */ |
---|
| 456 | + if (fw_err->revision == 0) { |
---|
| 457 | + /* record_identifier_guid not defined */ |
---|
| 458 | + offset = offsetof(struct cper_sec_fw_err_rec_ref, |
---|
| 459 | + record_identifier_guid); |
---|
| 460 | + } else if (fw_err->revision == 1) { |
---|
| 461 | + /* record_identifier not defined */ |
---|
| 462 | + offset = offsetof(struct cper_sec_fw_err_rec_ref, |
---|
| 463 | + record_identifier); |
---|
| 464 | + } else { |
---|
| 465 | + offset = sizeof(*fw_err); |
---|
| 466 | + } |
---|
| 467 | + |
---|
| 468 | + buf += offset; |
---|
| 469 | + length -= offset; |
---|
| 470 | + |
---|
| 471 | + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); |
---|
| 472 | +} |
---|
| 473 | + |
---|
420 | 474 | static void cper_print_tstamp(const char *pfx, |
---|
421 | 475 | struct acpi_hest_generic_data_v300 *gdata) |
---|
422 | 476 | { |
---|
.. | .. |
---|
504 | 558 | else |
---|
505 | 559 | goto err_section_too_small; |
---|
506 | 560 | #endif |
---|
| 561 | + } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { |
---|
| 562 | + struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); |
---|
| 563 | + |
---|
| 564 | + printk("%ssection_type: Firmware Error Record Reference\n", |
---|
| 565 | + newpfx); |
---|
| 566 | + /* The minimal FW Error Record contains 16 bytes */ |
---|
| 567 | + if (gdata->error_data_length >= SZ_16) |
---|
| 568 | + cper_print_fw_err(newpfx, gdata, fw_err); |
---|
| 569 | + else |
---|
| 570 | + goto err_section_too_small; |
---|
507 | 571 | } else { |
---|
508 | 572 | const void *err = acpi_hest_get_payload(gdata); |
---|
509 | 573 | |
---|