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 26 #include <linux/kernel.h> 27 #include <linux/module.h> 28 #include <linux/init.h> 29 #include <linux/acpi.h> 30 #include <linux/kdebug.h> 31 #include <linux/highmem.h> 32 #include <linux/io.h> 33 #include <linux/platform_device.h> 34 #include <acpi/apei.h> 35 #include <acpi/ghes.h> 36 37 #include "apei-internal.h" 38 39 #define HEST_PFX "HEST: " 40 41 int hest_disable; 42 EXPORT_SYMBOL_GPL(hest_disable); 43 44 /* HEST table parsing */ 45 46 static struct acpi_table_hest *__read_mostly hest_tab; 47 48 static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { 49 [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ 50 [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, 51 [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi), 52 [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root), 53 [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer), 54 [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge), 55 [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic), 56 [ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2), 57 [ACPI_HEST_TYPE_IA32_DEFERRED_CHECK] = -1, 58 }; 59 60 static int hest_esrc_len(struct acpi_hest_header *hest_hdr) 61 { 62 u16 hest_type = hest_hdr->type; 63 int len; 64 65 if (hest_type >= ACPI_HEST_TYPE_RESERVED) 66 return 0; 67 68 len = hest_esrc_len_tab[hest_type]; 69 70 if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) { 71 struct acpi_hest_ia_corrected *cmc; 72 cmc = (struct acpi_hest_ia_corrected *)hest_hdr; 73 len = sizeof(*cmc) + cmc->num_hardware_banks * 74 sizeof(struct acpi_hest_ia_error_bank); 75 } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) { 76 struct acpi_hest_ia_machine_check *mc; 77 mc = (struct acpi_hest_ia_machine_check *)hest_hdr; 78 len = sizeof(*mc) + mc->num_hardware_banks * 79 sizeof(struct acpi_hest_ia_error_bank); 80 } else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) { 81 struct acpi_hest_ia_deferred_check *mc; 82 mc = (struct acpi_hest_ia_deferred_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 91 int 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 || !hest_tab) 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 } 126 EXPORT_SYMBOL_GPL(apei_hest_parse); 127 128 /* 129 * Check if firmware advertises firmware first mode. We need FF bit to be set 130 * along with a set of MC banks which work in FF mode. 131 */ 132 static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) 133 { 134 if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) 135 return 0; 136 137 if (!acpi_disable_cmcff) 138 return !arch_apei_enable_cmcff(hest_hdr, data); 139 140 return 0; 141 } 142 143 struct ghes_arr { 144 struct platform_device **ghes_devs; 145 unsigned int count; 146 }; 147 148 static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data) 149 { 150 int *count = data; 151 152 if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR || 153 hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2) 154 (*count)++; 155 return 0; 156 } 157 158 static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data) 159 { 160 struct platform_device *ghes_dev; 161 struct ghes_arr *ghes_arr = data; 162 int rc, i; 163 164 if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR && 165 hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2) 166 return 0; 167 168 if (!((struct acpi_hest_generic *)hest_hdr)->enabled) 169 return 0; 170 for (i = 0; i < ghes_arr->count; i++) { 171 struct acpi_hest_header *hdr; 172 ghes_dev = ghes_arr->ghes_devs[i]; 173 hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data; 174 if (hdr->source_id == hest_hdr->source_id) { 175 pr_warning(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n", 176 hdr->source_id); 177 return -EIO; 178 } 179 } 180 ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id); 181 if (!ghes_dev) 182 return -ENOMEM; 183 184 rc = platform_device_add_data(ghes_dev, &hest_hdr, sizeof(void *)); 185 if (rc) 186 goto err; 187 188 rc = platform_device_add(ghes_dev); 189 if (rc) 190 goto err; 191 ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev; 192 193 return 0; 194 err: 195 platform_device_put(ghes_dev); 196 return rc; 197 } 198 199 static int __init hest_ghes_dev_register(unsigned int ghes_count) 200 { 201 int rc, i; 202 struct ghes_arr ghes_arr; 203 204 ghes_arr.count = 0; 205 ghes_arr.ghes_devs = kmalloc_array(ghes_count, sizeof(void *), 206 GFP_KERNEL); 207 if (!ghes_arr.ghes_devs) 208 return -ENOMEM; 209 210 rc = apei_hest_parse(hest_parse_ghes, &ghes_arr); 211 if (rc) 212 goto err; 213 214 rc = ghes_estatus_pool_init(ghes_count); 215 if (rc) 216 goto err; 217 218 out: 219 kfree(ghes_arr.ghes_devs); 220 return rc; 221 err: 222 for (i = 0; i < ghes_arr.count; i++) 223 platform_device_unregister(ghes_arr.ghes_devs[i]); 224 goto out; 225 } 226 227 static int __init setup_hest_disable(char *str) 228 { 229 hest_disable = HEST_DISABLED; 230 return 0; 231 } 232 233 __setup("hest_disable", setup_hest_disable); 234 235 void __init acpi_hest_init(void) 236 { 237 acpi_status status; 238 int rc = -ENODEV; 239 unsigned int ghes_count = 0; 240 241 if (hest_disable) { 242 pr_info(HEST_PFX "Table parsing disabled.\n"); 243 return; 244 } 245 246 status = acpi_get_table(ACPI_SIG_HEST, 0, 247 (struct acpi_table_header **)&hest_tab); 248 if (status == AE_NOT_FOUND) { 249 hest_disable = HEST_NOT_FOUND; 250 return; 251 } else if (ACPI_FAILURE(status)) { 252 const char *msg = acpi_format_exception(status); 253 pr_err(HEST_PFX "Failed to get table, %s\n", msg); 254 rc = -EINVAL; 255 goto err; 256 } 257 258 rc = apei_hest_parse(hest_parse_cmc, NULL); 259 if (rc) 260 goto err; 261 262 if (!ghes_disable) { 263 rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); 264 if (rc) 265 goto err; 266 267 if (ghes_count) 268 rc = hest_ghes_dev_register(ghes_count); 269 if (rc) 270 goto err; 271 } 272 273 pr_info(HEST_PFX "Table parsing has been initialized.\n"); 274 return; 275 err: 276 hest_disable = HEST_DISABLED; 277 } 278