root/drivers/acpi/apei/bert.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bert_print_all
  2. setup_bert_disable
  3. bert_check_table
  4. bert_init

   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 
  33 static int bert_disable;
  34 
  35 static void __init bert_print_all(struct acpi_bert_region *region,
  36                                   unsigned int region_len)
  37 {
  38         struct acpi_hest_generic_status *estatus =
  39                 (struct acpi_hest_generic_status *)region;
  40         int remain = region_len;
  41         u32 estatus_len;
  42 
  43         while (remain >= sizeof(struct acpi_bert_region)) {
  44                 estatus_len = cper_estatus_len(estatus);
  45                 if (remain < estatus_len) {
  46                         pr_err(FW_BUG "Truncated status block (length: %u).\n",
  47                                estatus_len);
  48                         return;
  49                 }
  50 
  51                 /* No more error records. */
  52                 if (!estatus->block_status)
  53                         return;
  54 
  55                 if (cper_estatus_check(estatus)) {
  56                         pr_err(FW_BUG "Invalid error record.\n");
  57                         return;
  58                 }
  59 
  60                 pr_info_once("Error records from previous boot:\n");
  61 
  62                 cper_estatus_print(KERN_INFO HW_ERR, estatus);
  63 
  64                 /*
  65                  * Because the boot error source is "one-time polled" type,
  66                  * clear Block Status of current Generic Error Status Block,
  67                  * once it's printed.
  68                  */
  69                 estatus->block_status = 0;
  70 
  71                 estatus = (void *)estatus + estatus_len;
  72                 remain -= estatus_len;
  73         }
  74 }
  75 
  76 static int __init setup_bert_disable(char *str)
  77 {
  78         bert_disable = 1;
  79 
  80         return 0;
  81 }
  82 __setup("bert_disable", setup_bert_disable);
  83 
  84 static int __init bert_check_table(struct acpi_table_bert *bert_tab)
  85 {
  86         if (bert_tab->header.length < sizeof(struct acpi_table_bert) ||
  87             bert_tab->region_length < sizeof(struct acpi_bert_region))
  88                 return -EINVAL;
  89 
  90         return 0;
  91 }
  92 
  93 static int __init bert_init(void)
  94 {
  95         struct apei_resources bert_resources;
  96         struct acpi_bert_region *boot_error_region;
  97         struct acpi_table_bert *bert_tab;
  98         unsigned int region_len;
  99         acpi_status status;
 100         int rc = 0;
 101 
 102         if (acpi_disabled)
 103                 return 0;
 104 
 105         if (bert_disable) {
 106                 pr_info("Boot Error Record Table support is disabled.\n");
 107                 return 0;
 108         }
 109 
 110         status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab);
 111         if (status == AE_NOT_FOUND)
 112                 return 0;
 113 
 114         if (ACPI_FAILURE(status)) {
 115                 pr_err("get table failed, %s.\n", acpi_format_exception(status));
 116                 return -EINVAL;
 117         }
 118 
 119         rc = bert_check_table(bert_tab);
 120         if (rc) {
 121                 pr_err(FW_BUG "table invalid.\n");
 122                 return rc;
 123         }
 124 
 125         region_len = bert_tab->region_length;
 126         apei_resources_init(&bert_resources);
 127         rc = apei_resources_add(&bert_resources, bert_tab->address,
 128                                 region_len, true);
 129         if (rc)
 130                 return rc;
 131         rc = apei_resources_request(&bert_resources, "APEI BERT");
 132         if (rc)
 133                 goto out_fini;
 134         boot_error_region = ioremap_cache(bert_tab->address, region_len);
 135         if (boot_error_region) {
 136                 bert_print_all(boot_error_region, region_len);
 137                 iounmap(boot_error_region);
 138         } else {
 139                 rc = -ENOMEM;
 140         }
 141 
 142         apei_resources_release(&bert_resources);
 143 out_fini:
 144         apei_resources_fini(&bert_resources);
 145 
 146         return rc;
 147 }
 148 
 149 late_initcall(bert_init);

/* [<][>][^][v][top][bottom][index][help] */