linux/drivers/firmware/efi/cper.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * UEFI Common Platform Error Record (CPER) support
   4 *
   5 * Copyright (C) 2010, Intel Corp.
   6 *      Author: Huang Ying <ying.huang@intel.com>
   7 *
   8 * CPER is the format used to describe platform hardware error by
   9 * various tables, such as ERST, BERT and HEST etc.
  10 *
  11 * For more information about CPER, please refer to Appendix N of UEFI
  12 * Specification version 2.4.
  13 */
  14
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/time.h>
  18#include <linux/cper.h>
  19#include <linux/dmi.h>
  20#include <linux/acpi.h>
  21#include <linux/pci.h>
  22#include <linux/aer.h>
  23#include <linux/printk.h>
  24#include <linux/bcd.h>
  25#include <acpi/ghes.h>
  26#include <ras/ras_event.h>
  27
  28static char rcd_decode_str[CPER_REC_LEN];
  29
  30/*
  31 * CPER record ID need to be unique even after reboot, because record
  32 * ID is used as index for ERST storage, while CPER records from
  33 * multiple boot may co-exist in ERST.
  34 */
  35u64 cper_next_record_id(void)
  36{
  37        static atomic64_t seq;
  38
  39        if (!atomic64_read(&seq)) {
  40                time64_t time = ktime_get_real_seconds();
  41
  42                /*
  43                 * This code is unlikely to still be needed in year 2106,
  44                 * but just in case, let's use a few more bits for timestamps
  45                 * after y2038 to be sure they keep increasing monotonically
  46                 * for the next few hundred years...
  47                 */
  48                if (time < 0x80000000)
  49                        atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
  50                else
  51                        atomic64_set(&seq, 0x8000000000000000ull |
  52                                           ktime_get_real_seconds() << 24);
  53        }
  54
  55        return atomic64_inc_return(&seq);
  56}
  57EXPORT_SYMBOL_GPL(cper_next_record_id);
  58
  59static const char * const severity_strs[] = {
  60        "recoverable",
  61        "fatal",
  62        "corrected",
  63        "info",
  64};
  65
  66const char *cper_severity_str(unsigned int severity)
  67{
  68        return severity < ARRAY_SIZE(severity_strs) ?
  69                severity_strs[severity] : "unknown";
  70}
  71EXPORT_SYMBOL_GPL(cper_severity_str);
  72
  73/*
  74 * cper_print_bits - print strings for set bits
  75 * @pfx: prefix for each line, including log level and prefix string
  76 * @bits: bit mask
  77 * @strs: string array, indexed by bit position
  78 * @strs_size: size of the string array: @strs
  79 *
  80 * For each set bit in @bits, print the corresponding string in @strs.
  81 * If the output length is longer than 80, multiple line will be
  82 * printed, with @pfx is printed at the beginning of each line.
  83 */
  84void cper_print_bits(const char *pfx, unsigned int bits,
  85                     const char * const strs[], unsigned int strs_size)
  86{
  87        int i, len = 0;
  88        const char *str;
  89        char buf[84];
  90
  91        for (i = 0; i < strs_size; i++) {
  92                if (!(bits & (1U << i)))
  93                        continue;
  94                str = strs[i];
  95                if (!str)
  96                        continue;
  97                if (len && len + strlen(str) + 2 > 80) {
  98                        printk("%s\n", buf);
  99                        len = 0;
 100                }
 101                if (!len)
 102                        len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
 103                else
 104                        len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
 105        }
 106        if (len)
 107                printk("%s\n", buf);
 108}
 109
 110static const char * const proc_type_strs[] = {
 111        "IA32/X64",
 112        "IA64",
 113        "ARM",
 114};
 115
 116static const char * const proc_isa_strs[] = {
 117        "IA32",
 118        "IA64",
 119        "X64",
 120        "ARM A32/T32",
 121        "ARM A64",
 122};
 123
 124const char * const cper_proc_error_type_strs[] = {
 125        "cache error",
 126        "TLB error",
 127        "bus error",
 128        "micro-architectural error",
 129};
 130
 131static const char * const proc_op_strs[] = {
 132        "unknown or generic",
 133        "data read",
 134        "data write",
 135        "instruction execution",
 136};
 137
 138static const char * const proc_flag_strs[] = {
 139        "restartable",
 140        "precise IP",
 141        "overflow",
 142        "corrected",
 143};
 144
 145static void cper_print_proc_generic(const char *pfx,
 146                                    const struct cper_sec_proc_generic *proc)
 147{
 148        if (proc->validation_bits & CPER_PROC_VALID_TYPE)
 149                printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
 150                       proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
 151                       proc_type_strs[proc->proc_type] : "unknown");
 152        if (proc->validation_bits & CPER_PROC_VALID_ISA)
 153                printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
 154                       proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
 155                       proc_isa_strs[proc->proc_isa] : "unknown");
 156        if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
 157                printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
 158                cper_print_bits(pfx, proc->proc_error_type,
 159                                cper_proc_error_type_strs,
 160                                ARRAY_SIZE(cper_proc_error_type_strs));
 161        }
 162        if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
 163                printk("%s""operation: %d, %s\n", pfx, proc->operation,
 164                       proc->operation < ARRAY_SIZE(proc_op_strs) ?
 165                       proc_op_strs[proc->operation] : "unknown");
 166        if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
 167                printk("%s""flags: 0x%02x\n", pfx, proc->flags);
 168                cper_print_bits(pfx, proc->flags, proc_flag_strs,
 169                                ARRAY_SIZE(proc_flag_strs));
 170        }
 171        if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
 172                printk("%s""level: %d\n", pfx, proc->level);
 173        if (proc->validation_bits & CPER_PROC_VALID_VERSION)
 174                printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
 175        if (proc->validation_bits & CPER_PROC_VALID_ID)
 176                printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
 177        if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
 178                printk("%s""target_address: 0x%016llx\n",
 179                       pfx, proc->target_addr);
 180        if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
 181                printk("%s""requestor_id: 0x%016llx\n",
 182                       pfx, proc->requestor_id);
 183        if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
 184                printk("%s""responder_id: 0x%016llx\n",
 185                       pfx, proc->responder_id);
 186        if (proc->validation_bits & CPER_PROC_VALID_IP)
 187                printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
 188}
 189
 190static const char * const mem_err_type_strs[] = {
 191        "unknown",
 192        "no error",
 193        "single-bit ECC",
 194        "multi-bit ECC",
 195        "single-symbol chipkill ECC",
 196        "multi-symbol chipkill ECC",
 197        "master abort",
 198        "target abort",
 199        "parity error",
 200        "watchdog timeout",
 201        "invalid address",
 202        "mirror Broken",
 203        "memory sparing",
 204        "scrub corrected error",
 205        "scrub uncorrected error",
 206        "physical memory map-out event",
 207};
 208
 209const char *cper_mem_err_type_str(unsigned int etype)
 210{
 211        return etype < ARRAY_SIZE(mem_err_type_strs) ?
 212                mem_err_type_strs[etype] : "unknown";
 213}
 214EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
 215
 216static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
 217{
 218        u32 len, n;
 219
 220        if (!msg)
 221                return 0;
 222
 223        n = 0;
 224        len = CPER_REC_LEN - 1;
 225        if (mem->validation_bits & CPER_MEM_VALID_NODE)
 226                n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
 227        if (mem->validation_bits & CPER_MEM_VALID_CARD)
 228                n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
 229        if (mem->validation_bits & CPER_MEM_VALID_MODULE)
 230                n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
 231        if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
 232                n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
 233        if (mem->validation_bits & CPER_MEM_VALID_BANK)
 234                n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
 235        if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
 236                n += scnprintf(msg + n, len - n, "bank_group: %d ",
 237                               mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
 238        if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
 239                n += scnprintf(msg + n, len - n, "bank_address: %d ",
 240                               mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
 241        if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
 242                n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
 243        if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
 244                u32 row = mem->row;
 245
 246                row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
 247                n += scnprintf(msg + n, len - n, "row: %d ", row);
 248        }
 249        if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
 250                n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
 251        if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
 252                n += scnprintf(msg + n, len - n, "bit_position: %d ",
 253                               mem->bit_pos);
 254        if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
 255                n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
 256                               mem->requestor_id);
 257        if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
 258                n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
 259                               mem->responder_id);
 260        if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
 261                scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
 262                          mem->target_id);
 263        if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
 264                scnprintf(msg + n, len - n, "chip_id: %d ",
 265                          mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
 266
 267        msg[n] = '\0';
 268        return n;
 269}
 270
 271static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
 272{
 273        u32 len, n;
 274        const char *bank = NULL, *device = NULL;
 275
 276        if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
 277                return 0;
 278
 279        len = CPER_REC_LEN;
 280        dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
 281        if (bank && device)
 282                n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
 283        else
 284                n = snprintf(msg, len,
 285                             "DIMM location: not present. DMI handle: 0x%.4x ",
 286                             mem->mem_dev_handle);
 287
 288        return n;
 289}
 290
 291void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
 292                       struct cper_mem_err_compact *cmem)
 293{
 294        cmem->validation_bits = mem->validation_bits;
 295        cmem->node = mem->node;
 296        cmem->card = mem->card;
 297        cmem->module = mem->module;
 298        cmem->bank = mem->bank;
 299        cmem->device = mem->device;
 300        cmem->row = mem->row;
 301        cmem->column = mem->column;
 302        cmem->bit_pos = mem->bit_pos;
 303        cmem->requestor_id = mem->requestor_id;
 304        cmem->responder_id = mem->responder_id;
 305        cmem->target_id = mem->target_id;
 306        cmem->extended = mem->extended;
 307        cmem->rank = mem->rank;
 308        cmem->mem_array_handle = mem->mem_array_handle;
 309        cmem->mem_dev_handle = mem->mem_dev_handle;
 310}
 311
 312const char *cper_mem_err_unpack(struct trace_seq *p,
 313                                struct cper_mem_err_compact *cmem)
 314{
 315        const char *ret = trace_seq_buffer_ptr(p);
 316
 317        if (cper_mem_err_location(cmem, rcd_decode_str))
 318                trace_seq_printf(p, "%s", rcd_decode_str);
 319        if (cper_dimm_err_location(cmem, rcd_decode_str))
 320                trace_seq_printf(p, "%s", rcd_decode_str);
 321        trace_seq_putc(p, '\0');
 322
 323        return ret;
 324}
 325
 326static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
 327        int len)
 328{
 329        struct cper_mem_err_compact cmem;
 330
 331        /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
 332        if (len == sizeof(struct cper_sec_mem_err_old) &&
 333            (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
 334                pr_err(FW_WARN "valid bits set for fields beyond structure\n");
 335                return;
 336        }
 337        if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
 338                printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
 339        if (mem->validation_bits & CPER_MEM_VALID_PA)
 340                printk("%s""physical_address: 0x%016llx\n",
 341                       pfx, mem->physical_addr);
 342        if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
 343                printk("%s""physical_address_mask: 0x%016llx\n",
 344                       pfx, mem->physical_addr_mask);
 345        cper_mem_err_pack(mem, &cmem);
 346        if (cper_mem_err_location(&cmem, rcd_decode_str))
 347                printk("%s%s\n", pfx, rcd_decode_str);
 348        if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
 349                u8 etype = mem->error_type;
 350                printk("%s""error_type: %d, %s\n", pfx, etype,
 351                       cper_mem_err_type_str(etype));
 352        }
 353        if (cper_dimm_err_location(&cmem, rcd_decode_str))
 354                printk("%s%s\n", pfx, rcd_decode_str);
 355}
 356
 357static const char * const pcie_port_type_strs[] = {
 358        "PCIe end point",
 359        "legacy PCI end point",
 360        "unknown",
 361        "unknown",
 362        "root port",
 363        "upstream switch port",
 364        "downstream switch port",
 365        "PCIe to PCI/PCI-X bridge",
 366        "PCI/PCI-X to PCIe bridge",
 367        "root complex integrated endpoint device",
 368        "root complex event collector",
 369};
 370
 371static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
 372                            const struct acpi_hest_generic_data *gdata)
 373{
 374        if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
 375                printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
 376                       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
 377                       pcie_port_type_strs[pcie->port_type] : "unknown");
 378        if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
 379                printk("%s""version: %d.%d\n", pfx,
 380                       pcie->version.major, pcie->version.minor);
 381        if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
 382                printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
 383                       pcie->command, pcie->status);
 384        if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
 385                const __u8 *p;
 386                printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
 387                       pcie->device_id.segment, pcie->device_id.bus,
 388                       pcie->device_id.device, pcie->device_id.function);
 389                printk("%s""slot: %d\n", pfx,
 390                       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
 391                printk("%s""secondary_bus: 0x%02x\n", pfx,
 392                       pcie->device_id.secondary_bus);
 393                printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
 394                       pcie->device_id.vendor_id, pcie->device_id.device_id);
 395                p = pcie->device_id.class_code;
 396                printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
 397        }
 398        if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
 399                printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
 400                       pcie->serial_number.lower, pcie->serial_number.upper);
 401        if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
 402                printk(
 403        "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
 404        pfx, pcie->bridge.secondary_status, pcie->bridge.control);
 405
 406        /* Fatal errors call __ghes_panic() before AER handler prints this */
 407        if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
 408            (gdata->error_severity & CPER_SEV_FATAL)) {
 409                struct aer_capability_regs *aer;
 410
 411                aer = (struct aer_capability_regs *)pcie->aer_info;
 412                printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
 413                       pfx, aer->uncor_status, aer->uncor_mask);
 414                printk("%saer_uncor_severity: 0x%08x\n",
 415                       pfx, aer->uncor_severity);
 416                printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
 417                       aer->header_log.dw0, aer->header_log.dw1,
 418                       aer->header_log.dw2, aer->header_log.dw3);
 419        }
 420}
 421
 422static 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
 428static 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
 474static void cper_print_tstamp(const char *pfx,
 475                                   struct acpi_hest_generic_data_v300 *gdata)
 476{
 477        __u8 hour, min, sec, day, mon, year, century, *timestamp;
 478
 479        if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
 480                timestamp = (__u8 *)&(gdata->time_stamp);
 481                sec       = bcd2bin(timestamp[0]);
 482                min       = bcd2bin(timestamp[1]);
 483                hour      = bcd2bin(timestamp[2]);
 484                day       = bcd2bin(timestamp[4]);
 485                mon       = bcd2bin(timestamp[5]);
 486                year      = bcd2bin(timestamp[6]);
 487                century   = bcd2bin(timestamp[7]);
 488
 489                printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
 490                       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
 491                       century, year, mon, day, hour, min, sec);
 492        }
 493}
 494
 495static void
 496cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
 497                           int sec_no)
 498{
 499        guid_t *sec_type = (guid_t *)gdata->section_type;
 500        __u16 severity;
 501        char newpfx[64];
 502
 503        if (acpi_hest_get_version(gdata) >= 3)
 504                cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
 505
 506        severity = gdata->error_severity;
 507        printk("%s""Error %d, type: %s\n", pfx, sec_no,
 508               cper_severity_str(severity));
 509        if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
 510                printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
 511        if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
 512                printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
 513
 514        snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
 515        if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
 516                struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
 517
 518                printk("%s""section_type: general processor error\n", newpfx);
 519                if (gdata->error_data_length >= sizeof(*proc_err))
 520                        cper_print_proc_generic(newpfx, proc_err);
 521                else
 522                        goto err_section_too_small;
 523        } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
 524                struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
 525
 526                printk("%s""section_type: memory error\n", newpfx);
 527                if (gdata->error_data_length >=
 528                    sizeof(struct cper_sec_mem_err_old))
 529                        cper_print_mem(newpfx, mem_err,
 530                                       gdata->error_data_length);
 531                else
 532                        goto err_section_too_small;
 533        } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
 534                struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
 535
 536                printk("%s""section_type: PCIe error\n", newpfx);
 537                if (gdata->error_data_length >= sizeof(*pcie))
 538                        cper_print_pcie(newpfx, pcie, gdata);
 539                else
 540                        goto err_section_too_small;
 541#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
 542        } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
 543                struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
 544
 545                printk("%ssection_type: ARM processor error\n", newpfx);
 546                if (gdata->error_data_length >= sizeof(*arm_err))
 547                        cper_print_proc_arm(newpfx, arm_err);
 548                else
 549                        goto err_section_too_small;
 550#endif
 551#if defined(CONFIG_UEFI_CPER_X86)
 552        } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
 553                struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
 554
 555                printk("%ssection_type: IA32/X64 processor error\n", newpfx);
 556                if (gdata->error_data_length >= sizeof(*ia_err))
 557                        cper_print_proc_ia(newpfx, ia_err);
 558                else
 559                        goto err_section_too_small;
 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;
 571        } else {
 572                const void *err = acpi_hest_get_payload(gdata);
 573
 574                printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
 575                printk("%ssection length: %#x\n", newpfx,
 576                       gdata->error_data_length);
 577                print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
 578                               gdata->error_data_length, true);
 579        }
 580
 581        return;
 582
 583err_section_too_small:
 584        pr_err(FW_WARN "error section length is too small\n");
 585}
 586
 587void cper_estatus_print(const char *pfx,
 588                        const struct acpi_hest_generic_status *estatus)
 589{
 590        struct acpi_hest_generic_data *gdata;
 591        int sec_no = 0;
 592        char newpfx[64];
 593        __u16 severity;
 594
 595        severity = estatus->error_severity;
 596        if (severity == CPER_SEV_CORRECTED)
 597                printk("%s%s\n", pfx,
 598                       "It has been corrected by h/w "
 599                       "and requires no further action");
 600        printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
 601        snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
 602
 603        apei_estatus_for_each_section(estatus, gdata) {
 604                cper_estatus_print_section(newpfx, gdata, sec_no);
 605                sec_no++;
 606        }
 607}
 608EXPORT_SYMBOL_GPL(cper_estatus_print);
 609
 610int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
 611{
 612        if (estatus->data_length &&
 613            estatus->data_length < sizeof(struct acpi_hest_generic_data))
 614                return -EINVAL;
 615        if (estatus->raw_data_length &&
 616            estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
 617                return -EINVAL;
 618
 619        return 0;
 620}
 621EXPORT_SYMBOL_GPL(cper_estatus_check_header);
 622
 623int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
 624{
 625        struct acpi_hest_generic_data *gdata;
 626        unsigned int data_len, record_size;
 627        int rc;
 628
 629        rc = cper_estatus_check_header(estatus);
 630        if (rc)
 631                return rc;
 632
 633        data_len = estatus->data_length;
 634
 635        apei_estatus_for_each_section(estatus, gdata) {
 636                if (sizeof(struct acpi_hest_generic_data) > data_len)
 637                        return -EINVAL;
 638
 639                record_size = acpi_hest_get_record_size(gdata);
 640                if (record_size > data_len)
 641                        return -EINVAL;
 642
 643                data_len -= record_size;
 644        }
 645        if (data_len)
 646                return -EINVAL;
 647
 648        return 0;
 649}
 650EXPORT_SYMBOL_GPL(cper_estatus_check);
 651