root/drivers/acpi/apei/hest.c

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

DEFINITIONS

This source file includes following definitions.
  1. hest_esrc_len
  2. apei_hest_parse
  3. hest_parse_cmc
  4. hest_parse_ghes_count
  5. hest_parse_ghes
  6. hest_ghes_dev_register
  7. setup_hest_disable
  8. acpi_hest_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * APEI Hardware Error Souce Table support
   4  *
   5  * HEST describes error sources in detail; communicates operational
   6  * parameters (i.e. severity levels, masking bits, and threshold
   7  * values) to Linux as necessary. It also allows the BIOS to report
   8  * non-standard error sources to Linux (for example, chipset-specific
   9  * error registers).
  10  *
  11  * For more information about HEST, please refer to ACPI Specification
  12  * version 4.0, section 17.3.2.
  13  *
  14  * Copyright 2009 Intel Corp.
  15  *   Author: Huang Ying <ying.huang@intel.com>
  16  */
  17 
  18 #include <linux/kernel.h>
  19 #include <linux/module.h>
  20 #include <linux/init.h>
  21 #include <linux/acpi.h>
  22 #include <linux/kdebug.h>
  23 #include <linux/highmem.h>
  24 #include <linux/io.h>
  25 #include <linux/platform_device.h>
  26 #include <acpi/apei.h>
  27 #include <acpi/ghes.h>
  28 
  29 #include "apei-internal.h"
  30 
  31 #define HEST_PFX "HEST: "
  32 
  33 int hest_disable;
  34 EXPORT_SYMBOL_GPL(hest_disable);
  35 
  36 /* HEST table parsing */
  37 
  38 static struct acpi_table_hest *__read_mostly hest_tab;
  39 
  40 static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
  41         [ACPI_HEST_TYPE_IA32_CHECK] = -1,       /* need further calculation */
  42         [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
  43         [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
  44         [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
  45         [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
  46         [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
  47         [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
  48         [ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2),
  49         [ACPI_HEST_TYPE_IA32_DEFERRED_CHECK] = -1,
  50 };
  51 
  52 static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
  53 {
  54         u16 hest_type = hest_hdr->type;
  55         int len;
  56 
  57         if (hest_type >= ACPI_HEST_TYPE_RESERVED)
  58                 return 0;
  59 
  60         len = hest_esrc_len_tab[hest_type];
  61 
  62         if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
  63                 struct acpi_hest_ia_corrected *cmc;
  64                 cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
  65                 len = sizeof(*cmc) + cmc->num_hardware_banks *
  66                         sizeof(struct acpi_hest_ia_error_bank);
  67         } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
  68                 struct acpi_hest_ia_machine_check *mc;
  69                 mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
  70                 len = sizeof(*mc) + mc->num_hardware_banks *
  71                         sizeof(struct acpi_hest_ia_error_bank);
  72         } else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) {
  73                 struct acpi_hest_ia_deferred_check *mc;
  74                 mc = (struct acpi_hest_ia_deferred_check *)hest_hdr;
  75                 len = sizeof(*mc) + mc->num_hardware_banks *
  76                         sizeof(struct acpi_hest_ia_error_bank);
  77         }
  78         BUG_ON(len == -1);
  79 
  80         return len;
  81 };
  82 
  83 int apei_hest_parse(apei_hest_func_t func, void *data)
  84 {
  85         struct acpi_hest_header *hest_hdr;
  86         int i, rc, len;
  87 
  88         if (hest_disable || !hest_tab)
  89                 return -EINVAL;
  90 
  91         hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
  92         for (i = 0; i < hest_tab->error_source_count; i++) {
  93                 len = hest_esrc_len(hest_hdr);
  94                 if (!len) {
  95                         pr_warning(FW_WARN HEST_PFX
  96                                    "Unknown or unused hardware error source "
  97                                    "type: %d for hardware error source: %d.\n",
  98                                    hest_hdr->type, hest_hdr->source_id);
  99                         return -EINVAL;
 100                 }
 101                 if ((void *)hest_hdr + len >
 102                     (void *)hest_tab + hest_tab->header.length) {
 103                         pr_warning(FW_BUG HEST_PFX
 104                 "Table contents overflow for hardware error source: %d.\n",
 105                                 hest_hdr->source_id);
 106                         return -EINVAL;
 107                 }
 108 
 109                 rc = func(hest_hdr, data);
 110                 if (rc)
 111                         return rc;
 112 
 113                 hest_hdr = (void *)hest_hdr + len;
 114         }
 115 
 116         return 0;
 117 }
 118 EXPORT_SYMBOL_GPL(apei_hest_parse);
 119 
 120 /*
 121  * Check if firmware advertises firmware first mode. We need FF bit to be set
 122  * along with a set of MC banks which work in FF mode.
 123  */
 124 static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data)
 125 {
 126         if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK)
 127                 return 0;
 128 
 129         if (!acpi_disable_cmcff)
 130                 return !arch_apei_enable_cmcff(hest_hdr, data);
 131 
 132         return 0;
 133 }
 134 
 135 struct ghes_arr {
 136         struct platform_device **ghes_devs;
 137         unsigned int count;
 138 };
 139 
 140 static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data)
 141 {
 142         int *count = data;
 143 
 144         if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR ||
 145             hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2)
 146                 (*count)++;
 147         return 0;
 148 }
 149 
 150 static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data)
 151 {
 152         struct platform_device *ghes_dev;
 153         struct ghes_arr *ghes_arr = data;
 154         int rc, i;
 155 
 156         if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR &&
 157             hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2)
 158                 return 0;
 159 
 160         if (!((struct acpi_hest_generic *)hest_hdr)->enabled)
 161                 return 0;
 162         for (i = 0; i < ghes_arr->count; i++) {
 163                 struct acpi_hest_header *hdr;
 164                 ghes_dev = ghes_arr->ghes_devs[i];
 165                 hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data;
 166                 if (hdr->source_id == hest_hdr->source_id) {
 167                         pr_warning(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n",
 168                                    hdr->source_id);
 169                         return -EIO;
 170                 }
 171         }
 172         ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id);
 173         if (!ghes_dev)
 174                 return -ENOMEM;
 175 
 176         rc = platform_device_add_data(ghes_dev, &hest_hdr, sizeof(void *));
 177         if (rc)
 178                 goto err;
 179 
 180         rc = platform_device_add(ghes_dev);
 181         if (rc)
 182                 goto err;
 183         ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev;
 184 
 185         return 0;
 186 err:
 187         platform_device_put(ghes_dev);
 188         return rc;
 189 }
 190 
 191 static int __init hest_ghes_dev_register(unsigned int ghes_count)
 192 {
 193         int rc, i;
 194         struct ghes_arr ghes_arr;
 195 
 196         ghes_arr.count = 0;
 197         ghes_arr.ghes_devs = kmalloc_array(ghes_count, sizeof(void *),
 198                                            GFP_KERNEL);
 199         if (!ghes_arr.ghes_devs)
 200                 return -ENOMEM;
 201 
 202         rc = apei_hest_parse(hest_parse_ghes, &ghes_arr);
 203         if (rc)
 204                 goto err;
 205 
 206         rc = ghes_estatus_pool_init(ghes_count);
 207         if (rc)
 208                 goto err;
 209 
 210 out:
 211         kfree(ghes_arr.ghes_devs);
 212         return rc;
 213 err:
 214         for (i = 0; i < ghes_arr.count; i++)
 215                 platform_device_unregister(ghes_arr.ghes_devs[i]);
 216         goto out;
 217 }
 218 
 219 static int __init setup_hest_disable(char *str)
 220 {
 221         hest_disable = HEST_DISABLED;
 222         return 0;
 223 }
 224 
 225 __setup("hest_disable", setup_hest_disable);
 226 
 227 void __init acpi_hest_init(void)
 228 {
 229         acpi_status status;
 230         int rc = -ENODEV;
 231         unsigned int ghes_count = 0;
 232 
 233         if (hest_disable) {
 234                 pr_info(HEST_PFX "Table parsing disabled.\n");
 235                 return;
 236         }
 237 
 238         status = acpi_get_table(ACPI_SIG_HEST, 0,
 239                                 (struct acpi_table_header **)&hest_tab);
 240         if (status == AE_NOT_FOUND) {
 241                 hest_disable = HEST_NOT_FOUND;
 242                 return;
 243         } else if (ACPI_FAILURE(status)) {
 244                 const char *msg = acpi_format_exception(status);
 245                 pr_err(HEST_PFX "Failed to get table, %s\n", msg);
 246                 rc = -EINVAL;
 247                 goto err;
 248         }
 249 
 250         rc = apei_hest_parse(hest_parse_cmc, NULL);
 251         if (rc)
 252                 goto err;
 253 
 254         if (!ghes_disable) {
 255                 rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
 256                 if (rc)
 257                         goto err;
 258 
 259                 if (ghes_count)
 260                         rc = hest_ghes_dev_register(ghes_count);
 261                 if (rc)
 262                         goto err;
 263         }
 264 
 265         pr_info(HEST_PFX "Table parsing has been initialized.\n");
 266         return;
 267 err:
 268         hest_disable = HEST_DISABLED;
 269 }

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