| .. | .. |
|---|
| 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 | |
|---|