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