hest.c revision 9dc966641677795f4d6b0a9ba630d6a3a3e24a57
1/* 2 * APEI Hardware Error Souce Table support 3 * 4 * HEST describes error sources in detail; communicates operational 5 * parameters (i.e. severity levels, masking bits, and threshold 6 * values) to Linux as necessary. It also allows the BIOS to report 7 * non-standard error sources to Linux (for example, chipset-specific 8 * error registers). 9 * 10 * For more information about HEST, please refer to ACPI Specification 11 * version 4.0, section 17.3.2. 12 * 13 * Copyright 2009 Intel Corp. 14 * Author: Huang Ying <ying.huang@intel.com> 15 * 16 * This program is free software; you can redistribute it and/or 17 * modify it under the terms of the GNU General Public License version 18 * 2 as published by the Free Software Foundation; 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 */ 29 30#include <linux/kernel.h> 31#include <linux/module.h> 32#include <linux/init.h> 33#include <linux/acpi.h> 34#include <linux/kdebug.h> 35#include <linux/highmem.h> 36#include <linux/io.h> 37#include <acpi/apei.h> 38 39#include "apei-internal.h" 40 41#define HEST_PFX "HEST: " 42 43int hest_disable; 44EXPORT_SYMBOL_GPL(hest_disable); 45 46/* HEST table parsing */ 47 48static struct acpi_table_hest *hest_tab; 49 50static int hest_void_parse(struct acpi_hest_header *hest_hdr, void *data) 51{ 52 return 0; 53} 54 55static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { 56 [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ 57 [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, 58 [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi), 59 [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root), 60 [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer), 61 [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge), 62 [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic), 63}; 64 65static int hest_esrc_len(struct acpi_hest_header *hest_hdr) 66{ 67 u16 hest_type = hest_hdr->type; 68 int len; 69 70 if (hest_type >= ACPI_HEST_TYPE_RESERVED) 71 return 0; 72 73 len = hest_esrc_len_tab[hest_type]; 74 75 if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) { 76 struct acpi_hest_ia_corrected *cmc; 77 cmc = (struct acpi_hest_ia_corrected *)hest_hdr; 78 len = sizeof(*cmc) + cmc->num_hardware_banks * 79 sizeof(struct acpi_hest_ia_error_bank); 80 } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) { 81 struct acpi_hest_ia_machine_check *mc; 82 mc = (struct acpi_hest_ia_machine_check *)hest_hdr; 83 len = sizeof(*mc) + mc->num_hardware_banks * 84 sizeof(struct acpi_hest_ia_error_bank); 85 } 86 BUG_ON(len == -1); 87 88 return len; 89}; 90 91int apei_hest_parse(apei_hest_func_t func, void *data) 92{ 93 struct acpi_hest_header *hest_hdr; 94 int i, rc, len; 95 96 if (hest_disable) 97 return -EINVAL; 98 99 hest_hdr = (struct acpi_hest_header *)(hest_tab + 1); 100 for (i = 0; i < hest_tab->error_source_count; i++) { 101 len = hest_esrc_len(hest_hdr); 102 if (!len) { 103 pr_warning(FW_WARN HEST_PFX 104 "Unknown or unused hardware error source " 105 "type: %d for hardware error source: %d.\n", 106 hest_hdr->type, hest_hdr->source_id); 107 return -EINVAL; 108 } 109 if ((void *)hest_hdr + len > 110 (void *)hest_tab + hest_tab->header.length) { 111 pr_warning(FW_BUG HEST_PFX 112 "Table contents overflow for hardware error source: %d.\n", 113 hest_hdr->source_id); 114 return -EINVAL; 115 } 116 117 rc = func(hest_hdr, data); 118 if (rc) 119 return rc; 120 121 hest_hdr = (void *)hest_hdr + len; 122 } 123 124 return 0; 125} 126EXPORT_SYMBOL_GPL(apei_hest_parse); 127 128static int __init setup_hest_disable(char *str) 129{ 130 hest_disable = 1; 131 return 0; 132} 133 134__setup("hest_disable", setup_hest_disable); 135 136static int __init hest_init(void) 137{ 138 acpi_status status; 139 int rc = -ENODEV; 140 141 if (acpi_disabled) 142 goto err; 143 144 if (hest_disable) { 145 pr_info(HEST_PFX "HEST tabling parsing is disabled.\n"); 146 goto err; 147 } 148 149 status = acpi_get_table(ACPI_SIG_HEST, 0, 150 (struct acpi_table_header **)&hest_tab); 151 if (status == AE_NOT_FOUND) { 152 pr_info(HEST_PFX "Table is not found!\n"); 153 goto err; 154 } else if (ACPI_FAILURE(status)) { 155 const char *msg = acpi_format_exception(status); 156 pr_err(HEST_PFX "Failed to get table, %s\n", msg); 157 rc = -EINVAL; 158 goto err; 159 } 160 161 rc = apei_hest_parse(hest_void_parse, NULL); 162 if (rc) 163 goto err; 164 165 pr_info(HEST_PFX "HEST table parsing is initialized.\n"); 166 167 return 0; 168err: 169 hest_disable = 1; 170 return rc; 171} 172 173subsys_initcall(hest_init); 174