cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bert.c (4066B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * APEI Boot Error Record Table (BERT) support
      4 *
      5 * Copyright 2011 Intel Corp.
      6 *   Author: Huang Ying <ying.huang@intel.com>
      7 *
      8 * Under normal circumstances, when a hardware error occurs, the error
      9 * handler receives control and processes the error. This gives OSPM a
     10 * chance to process the error condition, report it, and optionally attempt
     11 * recovery. In some cases, the system is unable to process an error.
     12 * For example, system firmware or a management controller may choose to
     13 * reset the system or the system might experience an uncontrolled crash
     14 * or reset.The boot error source is used to report unhandled errors that
     15 * occurred in a previous boot. This mechanism is described in the BERT
     16 * table.
     17 *
     18 * For more information about BERT, please refer to ACPI Specification
     19 * version 4.0, section 17.3.1
     20 */
     21
     22#include <linux/kernel.h>
     23#include <linux/module.h>
     24#include <linux/init.h>
     25#include <linux/acpi.h>
     26#include <linux/io.h>
     27
     28#include "apei-internal.h"
     29
     30#undef pr_fmt
     31#define pr_fmt(fmt) "BERT: " fmt
     32#define ACPI_BERT_PRINT_MAX_LEN 1024
     33
     34static int bert_disable;
     35
     36static void __init bert_print_all(struct acpi_bert_region *region,
     37				  unsigned int region_len)
     38{
     39	struct acpi_hest_generic_status *estatus =
     40		(struct acpi_hest_generic_status *)region;
     41	int remain = region_len;
     42	u32 estatus_len;
     43
     44	while (remain >= sizeof(struct acpi_bert_region)) {
     45		estatus_len = cper_estatus_len(estatus);
     46		if (remain < estatus_len) {
     47			pr_err(FW_BUG "Truncated status block (length: %u).\n",
     48			       estatus_len);
     49			return;
     50		}
     51
     52		/* No more error records. */
     53		if (!estatus->block_status)
     54			return;
     55
     56		if (cper_estatus_check(estatus)) {
     57			pr_err(FW_BUG "Invalid error record.\n");
     58			return;
     59		}
     60
     61		pr_info_once("Error records from previous boot:\n");
     62		if (region_len < ACPI_BERT_PRINT_MAX_LEN)
     63			cper_estatus_print(KERN_INFO HW_ERR, estatus);
     64		else
     65			pr_info_once("Max print length exceeded, table data is available at:\n"
     66				     "/sys/firmware/acpi/tables/data/BERT");
     67
     68		/*
     69		 * Because the boot error source is "one-time polled" type,
     70		 * clear Block Status of current Generic Error Status Block,
     71		 * once it's printed.
     72		 */
     73		estatus->block_status = 0;
     74
     75		estatus = (void *)estatus + estatus_len;
     76		remain -= estatus_len;
     77	}
     78}
     79
     80static int __init setup_bert_disable(char *str)
     81{
     82	bert_disable = 1;
     83
     84	return 1;
     85}
     86__setup("bert_disable", setup_bert_disable);
     87
     88static int __init bert_check_table(struct acpi_table_bert *bert_tab)
     89{
     90	if (bert_tab->header.length < sizeof(struct acpi_table_bert) ||
     91	    bert_tab->region_length < sizeof(struct acpi_bert_region))
     92		return -EINVAL;
     93
     94	return 0;
     95}
     96
     97static int __init bert_init(void)
     98{
     99	struct apei_resources bert_resources;
    100	struct acpi_bert_region *boot_error_region;
    101	struct acpi_table_bert *bert_tab;
    102	unsigned int region_len;
    103	acpi_status status;
    104	int rc = 0;
    105
    106	if (acpi_disabled)
    107		return 0;
    108
    109	if (bert_disable) {
    110		pr_info("Boot Error Record Table support is disabled.\n");
    111		return 0;
    112	}
    113
    114	status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab);
    115	if (status == AE_NOT_FOUND)
    116		return 0;
    117
    118	if (ACPI_FAILURE(status)) {
    119		pr_err("get table failed, %s.\n", acpi_format_exception(status));
    120		return -EINVAL;
    121	}
    122
    123	rc = bert_check_table(bert_tab);
    124	if (rc) {
    125		pr_err(FW_BUG "table invalid.\n");
    126		goto out_put_bert_tab;
    127	}
    128
    129	region_len = bert_tab->region_length;
    130	apei_resources_init(&bert_resources);
    131	rc = apei_resources_add(&bert_resources, bert_tab->address,
    132				region_len, true);
    133	if (rc)
    134		goto out_put_bert_tab;
    135	rc = apei_resources_request(&bert_resources, "APEI BERT");
    136	if (rc)
    137		goto out_fini;
    138	boot_error_region = ioremap_cache(bert_tab->address, region_len);
    139	if (boot_error_region) {
    140		bert_print_all(boot_error_region, region_len);
    141		iounmap(boot_error_region);
    142	} else {
    143		rc = -ENOMEM;
    144	}
    145
    146	apei_resources_release(&bert_resources);
    147out_fini:
    148	apei_resources_fini(&bert_resources);
    149out_put_bert_tab:
    150	acpi_put_table((struct acpi_table_header *)bert_tab);
    151
    152	return rc;
    153}
    154
    155late_initcall(bert_init);