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        n = 0;
 280        len = CPER_REC_LEN - 1;
 281        dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
 282        if (bank && device)
 283                n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
 284        else
 285                n = snprintf(msg, len,
 286                             "DIMM location: not present. DMI handle: 0x%.4x ",
 287                             mem->mem_dev_handle);
 288
 289        msg[n] = '\0';
 290        return n;
 291}
 292
 293void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
 294                       struct cper_mem_err_compact *cmem)
 295{
 296        cmem->validation_bits = mem->validation_bits;
 297        cmem->node = mem->node;
 298        cmem->card = mem->card;
 299        cmem->module = mem->module;
 300        cmem->bank = mem->bank;
 301        cmem->device = mem->device;
 302        cmem->row = mem->row;
 303        cmem->column = mem->column;
 304        cmem->bit_pos = mem->bit_pos;
 305        cmem->requestor_id = mem->requestor_id;
 306        cmem->responder_id = mem->responder_id;
 307        cmem->target_id = mem->target_id;
 308        cmem->extended = mem->extended;
 309        cmem->rank = mem->rank;
 310        cmem->mem_array_handle = mem->mem_array_handle;
 311        cmem->mem_dev_handle = mem->mem_dev_handle;
 312}
 313
 314const char *cper_mem_err_unpack(struct trace_seq *p,
 315                                struct cper_mem_err_compact *cmem)
 316{
 317        const char *ret = trace_seq_buffer_ptr(p);
 318
 319        if (cper_mem_err_location(cmem, rcd_decode_str))
 320                trace_seq_printf(p, "%s", rcd_decode_str);
 321        if (cper_dimm_err_location(cmem, rcd_decode_str))
 322                trace_seq_printf(p, "%s", rcd_decode_str);
 323        trace_seq_putc(p, '\0');
 324
 325        return ret;
 326}
 327
 328static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
 329        int len)
 330{
 331        struct cper_mem_err_compact cmem;
 332
 333        /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
 334        if (len == sizeof(struct cper_sec_mem_err_old) &&
 335            (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
 336                pr_err(FW_WARN "valid bits set for fields beyond structure\n");
 337                return;
 338        }
 339        if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
 340                printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
 341        if (mem->validation_bits & CPER_MEM_VALID_PA)
 342                printk("%s""physical_address: 0x%016llx\n",
 343                       pfx, mem->physical_addr);
 344        if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
 345                printk("%s""physical_address_mask: 0x%016llx\n",
 346                       pfx, mem->physical_addr_mask);
 347        cper_mem_err_pack(mem, &cmem);
 348        if (cper_mem_err_location(&cmem, rcd_decode_str))
 349                printk("%s%s\n", pfx, rcd_decode_str);
 350        if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
 351                u8 etype = mem->error_type;
 352                printk("%s""error_type: %d, %s\n", pfx, etype,
 353                       cper_mem_err_type_str(etype));
 354        }
 355        if (cper_dimm_err_location(&cmem, rcd_decode_str))
 356                printk("%s%s\n", pfx, rcd_decode_str);
 357}
 358
 359static const char * const pcie_port_type_strs[] = {
 360        "PCIe end point",
 361        "legacy PCI end point",
 362        "unknown",
 363        "unknown",
 364        "root port",
 365        "upstream switch port",
 366        "downstream switch port",
 367        "PCIe to PCI/PCI-X bridge",
 368        "PCI/PCI-X to PCIe bridge",
 369        "root complex integrated endpoint device",
 370        "root complex event collector",
 371};
 372
 373static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
 374                            const struct acpi_hest_generic_data *gdata)
 375{
 376        if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
 377                printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
 378                       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
 379                       pcie_port_type_strs[pcie->port_type] : "unknown");
 380        if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
 381                printk("%s""version: %d.%d\n", pfx,
 382                       pcie->version.major, pcie->version.minor);
 383        if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
 384                printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
 385                       pcie->command, pcie->status);
 386        if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
 387                const __u8 *p;
 388                printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
 389                       pcie->device_id.segment, pcie->device_id.bus,
 390                       pcie->device_id.device, pcie->device_id.function);
 391                printk("%s""slot: %d\n", pfx,
 392                       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
 393                printk("%s""secondary_bus: 0x%02x\n", pfx,
 394                       pcie->device_id.secondary_bus);
 395                printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
 396                       pcie->device_id.vendor_id, pcie->device_id.device_id);
 397                p = pcie->device_id.class_code;
 398                printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
 399        }
 400        if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
 401                printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
 402                       pcie->serial_number.lower, pcie->serial_number.upper);
 403        if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
 404                printk(
 405        "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
 406        pfx, pcie->bridge.secondary_status, pcie->bridge.control);
 407
 408        /* Fatal errors call __ghes_panic() before AER handler prints this */
 409        if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
 410            (gdata->error_severity & CPER_SEV_FATAL)) {
 411                struct aer_capability_regs *aer;
 412
 413                aer = (struct aer_capability_regs *)pcie->aer_info;
 414                printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
 415                       pfx, aer->uncor_status, aer->uncor_mask);
 416                printk("%saer_uncor_severity: 0x%08x\n",
 417                       pfx, aer->uncor_severity);
 418                printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
 419                       aer->header_log.dw0, aer->header_log.dw1,
 420                       aer->header_log.dw2, aer->header_log.dw3);
 421        }
 422}
 423
 424static const char * const fw_err_rec_type_strs[] = {
 425        "IPF SAL Error Record",
 426        "SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
 427        "SOC Firmware Error Record Type2",
 428};
 429
 430static void cper_print_fw_err(const char *pfx,
 431                              struct acpi_hest_generic_data *gdata,
 432                              const struct cper_sec_fw_err_rec_ref *fw_err)
 433{
 434        void *buf = acpi_hest_get_payload(gdata);
 435        u32 offset, length = gdata->error_data_length;
 436
 437        printk("%s""Firmware Error Record Type: %s\n", pfx,
 438               fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
 439               fw_err_rec_type_strs[fw_err->record_type] : "unknown");
 440        printk("%s""Revision: %d\n", pfx, fw_err->revision);
 441
 442        /* Record Type based on UEFI 2.7 */
 443        if (fw_err->revision == 0) {
 444                printk("%s""Record Identifier: %08llx\n", pfx,
 445                       fw_err->record_identifier);
 446        } else if (fw_err->revision == 2) {
 447                printk("%s""Record Identifier: %pUl\n", pfx,
 448                       &fw_err->record_identifier_guid);
 449        }
 450
 451        /*
 452         * The FW error record may contain trailing data beyond the
 453         * structure defined by the specification. As the fields
 454         * defined (and hence the offset of any trailing data) vary
 455         * with the revision, set the offset to account for this
 456         * variation.
 457         */
 458        if (fw_err->revision == 0) {
 459                /* record_identifier_guid not defined */
 460                offset = offsetof(struct cper_sec_fw_err_rec_ref,
 461                                  record_identifier_guid);
 462        } else if (fw_err->revision == 1) {
 463                /* record_identifier not defined */
 464                offset = offsetof(struct cper_sec_fw_err_rec_ref,
 465                                  record_identifier);
 466        } else {
 467                offset = sizeof(*fw_err);
 468        }
 469
 470        buf += offset;
 471        length -= offset;
 472
 473        print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
 474}
 475
 476static void cper_print_tstamp(const char *pfx,
 477                                   struct acpi_hest_generic_data_v300 *gdata)
 478{
 479        __u8 hour, min, sec, day, mon, year, century, *timestamp;
 480
 481        if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
 482                timestamp = (__u8 *)&(gdata->time_stamp);
 483                sec       = bcd2bin(timestamp[0]);
 484                min       = bcd2bin(timestamp[1]);
 485                hour      = bcd2bin(timestamp[2]);
 486                day       = bcd2bin(timestamp[4]);
 487                mon       = bcd2bin(timestamp[5]);
 488                year      = bcd2bin(timestamp[6]);
 489                century   = bcd2bin(timestamp[7]);
 490
 491                printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
 492                       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
 493                       century, year, mon, day, hour, min, sec);
 494        }
 495}
 496
 497static void
 498cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
 499                           int sec_no)
 500{
 501        guid_t *sec_type = (guid_t *)gdata->section_type;
 502        __u16 severity;
 503        char newpfx[64];
 504
 505        if (acpi_hest_get_version(gdata) >= 3)
 506                cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
 507
 508        severity = gdata->error_severity;
 509        printk("%s""Error %d, type: %s\n", pfx, sec_no,
 510               cper_severity_str(severity));
 511        if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
 512                printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
 513        if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
 514                printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
 515
 516        snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
 517        if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
 518                struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
 519
 520                printk("%s""section_type: general processor error\n", newpfx);
 521                if (gdata->error_data_length >= sizeof(*proc_err))
 522                        cper_print_proc_generic(newpfx, proc_err);
 523                else
 524                        goto err_section_too_small;
 525        } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
 526                struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
 527
 528                printk("%s""section_type: memory error\n", newpfx);
 529                if (gdata->error_data_length >=
 530                    sizeof(struct cper_sec_mem_err_old))
 531                        cper_print_mem(newpfx, mem_err,
 532                                       gdata->error_data_length);
 533                else
 534                        goto err_section_too_small;
 535        } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
 536                struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
 537
 538                printk("%s""section_type: PCIe error\n", newpfx);
 539                if (gdata->error_data_length >= sizeof(*pcie))
 540                        cper_print_pcie(newpfx, pcie, gdata);
 541                else
 542                        goto err_section_too_small;
 543#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
 544        } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
 545                struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
 546
 547                printk("%ssection_type: ARM processor error\n", newpfx);
 548                if (gdata->error_data_length >= sizeof(*arm_err))
 549                        cper_print_proc_arm(newpfx, arm_err);
 550                else
 551                        goto err_section_too_small;
 552#endif
 553#if defined(CONFIG_UEFI_CPER_X86)
 554        } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
 555                struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
 556
 557                printk("%ssection_type: IA32/X64 processor error\n", newpfx);
 558                if (gdata->error_data_length >= sizeof(*ia_err))
 559                        cper_print_proc_ia(newpfx, ia_err);
 560                else
 561                        goto err_section_too_small;
 562#endif
 563        } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
 564                struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
 565
 566                printk("%ssection_type: Firmware Error Record Reference\n",
 567                       newpfx);
 568                /* The minimal FW Error Record contains 16 bytes */
 569                if (gdata->error_data_length >= SZ_16)
 570                        cper_print_fw_err(newpfx, gdata, fw_err);
 571                else
 572                        goto err_section_too_small;
 573        } else {
 574                const void *err = acpi_hest_get_payload(gdata);
 575
 576                printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
 577                printk("%ssection length: %#x\n", newpfx,
 578                       gdata->error_data_length);
 579                print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
 580                               gdata->error_data_length, true);
 581        }
 582
 583        return;
 584
 585err_section_too_small:
 586        pr_err(FW_WARN "error section length is too small\n");
 587}
 588
 589void cper_estatus_print(const char *pfx,
 590                        const struct acpi_hest_generic_status *estatus)
 591{
 592        struct acpi_hest_generic_data *gdata;
 593        int sec_no = 0;
 594        char newpfx[64];
 595        __u16 severity;
 596
 597        severity = estatus->error_severity;
 598        if (severity == CPER_SEV_CORRECTED)
 599                printk("%s%s\n", pfx,
 600                       "It has been corrected by h/w "
 601                       "and requires no further action");
 602        printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
 603        snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
 604
 605        apei_estatus_for_each_section(estatus, gdata) {
 606                cper_estatus_print_section(newpfx, gdata, sec_no);
 607                sec_no++;
 608        }
 609}
 610EXPORT_SYMBOL_GPL(cper_estatus_print);
 611
 612int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
 613{
 614        if (estatus->data_length &&
 615            estatus->data_length < sizeof(struct acpi_hest_generic_data))
 616                return -EINVAL;
 617        if (estatus->raw_data_length &&
 618            estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
 619                return -EINVAL;
 620
 621        return 0;
 622}
 623EXPORT_SYMBOL_GPL(cper_estatus_check_header);
 624
 625int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
 626{
 627        struct acpi_hest_generic_data *gdata;
 628        unsigned int data_len, record_size;
 629        int rc;
 630
 631        rc = cper_estatus_check_header(estatus);
 632        if (rc)
 633                return rc;
 634
 635        data_len = estatus->data_length;
 636
 637        apei_estatus_for_each_section(estatus, gdata) {
 638                if (sizeof(struct acpi_hest_generic_data) > data_len)
 639                        return -EINVAL;
 640
 641                record_size = acpi_hest_get_record_size(gdata);
 642                if (record_size > data_len)
 643                        return -EINVAL;
 644
 645                data_len -= record_size;
 646        }
 647        if (data_len)
 648                return -EINVAL;
 649
 650        return 0;
 651}
 652EXPORT_SYMBOL_GPL(cper_estatus_check);
 653