1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * APEI Hardware Error Source 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 /* 41 * Since GHES_ASSIST is not supported, skip initialization of GHES_ASSIST 42 * structures for MCA. 43 * During HEST parsing, detected MCA error sources are cached from early 44 * table entries so that the Flags and Source Id fields from these cached 45 * values are then referred to in later table entries to determine if the 46 * encountered GHES_ASSIST structure should be initialized. 47 */ 48 static struct { 49 struct acpi_hest_ia_corrected *cmc; 50 struct acpi_hest_ia_machine_check *mc; 51 struct acpi_hest_ia_deferred_check *dmc; 52 } mces; 53 54 static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { 55 [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ 56 [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, 57 [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi), 58 [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root), 59 [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer), 60 [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge), 61 [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic), 62 [ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2), 63 [ACPI_HEST_TYPE_IA32_DEFERRED_CHECK] = -1, 64 }; 65 66 static inline bool is_generic_error(struct acpi_hest_header *hest_hdr) 67 { 68 return hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR || 69 hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2; 70 } 71 72 static int hest_esrc_len(struct acpi_hest_header *hest_hdr) 73 { 74 u16 hest_type = hest_hdr->type; 75 int len; 76 77 if (hest_type >= ACPI_HEST_TYPE_RESERVED) 78 return 0; 79 80 len = hest_esrc_len_tab[hest_type]; 81 82 if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) { 83 struct acpi_hest_ia_corrected *cmc; 84 cmc = (struct acpi_hest_ia_corrected *)hest_hdr; 85 len = sizeof(*cmc) + cmc->num_hardware_banks * 86 sizeof(struct acpi_hest_ia_error_bank); 87 mces.cmc = cmc; 88 } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) { 89 struct acpi_hest_ia_machine_check *mc; 90 mc = (struct acpi_hest_ia_machine_check *)hest_hdr; 91 len = sizeof(*mc) + mc->num_hardware_banks * 92 sizeof(struct acpi_hest_ia_error_bank); 93 mces.mc = mc; 94 } else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) { 95 struct acpi_hest_ia_deferred_check *mc; 96 mc = (struct acpi_hest_ia_deferred_check *)hest_hdr; 97 len = sizeof(*mc) + mc->num_hardware_banks * 98 sizeof(struct acpi_hest_ia_error_bank); 99 mces.dmc = mc; 100 } 101 BUG_ON(len == -1); 102 103 return len; 104 }; 105 106 /* 107 * GHES and GHESv2 structures share the same format, starting from 108 * Source Id and ending in Error Status Block Length (inclusive). 109 */ 110 static bool is_ghes_assist_struct(struct acpi_hest_header *hest_hdr) 111 { 112 struct acpi_hest_generic *ghes; 113 u16 related_source_id; 114 115 if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR && 116 hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2) 117 return false; 118 119 ghes = (struct acpi_hest_generic *)hest_hdr; 120 related_source_id = ghes->related_source_id; 121 122 if (mces.cmc && mces.cmc->flags & ACPI_HEST_GHES_ASSIST && 123 related_source_id == mces.cmc->header.source_id) 124 return true; 125 if (mces.mc && mces.mc->flags & ACPI_HEST_GHES_ASSIST && 126 related_source_id == mces.mc->header.source_id) 127 return true; 128 if (mces.dmc && mces.dmc->flags & ACPI_HEST_GHES_ASSIST && 129 related_source_id == mces.dmc->header.source_id) 130 return true; 131 132 return false; 133 } 134 135 typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data); 136 137 static int apei_hest_parse(apei_hest_func_t func, void *data) 138 { 139 struct acpi_hest_header *hest_hdr; 140 int i, rc, len; 141 142 if (hest_disable || !hest_tab) 143 return -EINVAL; 144 145 hest_hdr = (struct acpi_hest_header *)(hest_tab + 1); 146 for (i = 0; i < hest_tab->error_source_count; i++) { 147 len = hest_esrc_len(hest_hdr); 148 if (!len) { 149 pr_warn(FW_WARN HEST_PFX 150 "Unknown or unused hardware error source " 151 "type: %d for hardware error source: %d.\n", 152 hest_hdr->type, hest_hdr->source_id); 153 return -EINVAL; 154 } 155 if ((void *)hest_hdr + len > 156 (void *)hest_tab + hest_tab->header.length) { 157 pr_warn(FW_BUG HEST_PFX 158 "Table contents overflow for hardware error source: %d.\n", 159 hest_hdr->source_id); 160 return -EINVAL; 161 } 162 163 if (is_ghes_assist_struct(hest_hdr)) { 164 hest_hdr = (void *)hest_hdr + len; 165 continue; 166 } 167 168 rc = func(hest_hdr, data); 169 if (rc) 170 return rc; 171 172 hest_hdr = (void *)hest_hdr + len; 173 } 174 175 return 0; 176 } 177 178 /* 179 * Check if firmware advertises firmware first mode. We need FF bit to be set 180 * along with a set of MC banks which work in FF mode. 181 */ 182 static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) 183 { 184 if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) 185 return 0; 186 187 if (!acpi_disable_cmcff) 188 return !arch_apei_enable_cmcff(hest_hdr, data); 189 190 return 0; 191 } 192 193 struct ghes_arr { 194 struct platform_device **ghes_devs; 195 unsigned int count; 196 }; 197 198 static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data) 199 { 200 int *count = data; 201 202 if (is_generic_error(hest_hdr)) 203 (*count)++; 204 return 0; 205 } 206 207 static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data) 208 { 209 struct platform_device *ghes_dev; 210 struct ghes_arr *ghes_arr = data; 211 int rc, i; 212 213 if (!is_generic_error(hest_hdr)) 214 return 0; 215 216 if (!((struct acpi_hest_generic *)hest_hdr)->enabled) 217 return 0; 218 for (i = 0; i < ghes_arr->count; i++) { 219 struct acpi_hest_header *hdr; 220 ghes_dev = ghes_arr->ghes_devs[i]; 221 hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data; 222 if (hdr->source_id == hest_hdr->source_id) { 223 pr_warn(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n", 224 hdr->source_id); 225 return -EIO; 226 } 227 } 228 ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id); 229 if (!ghes_dev) 230 return -ENOMEM; 231 232 rc = platform_device_add_data(ghes_dev, &hest_hdr, sizeof(void *)); 233 if (rc) 234 goto err; 235 236 rc = platform_device_add(ghes_dev); 237 if (rc) 238 goto err; 239 ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev; 240 241 return 0; 242 err: 243 platform_device_put(ghes_dev); 244 return rc; 245 } 246 247 static int __init hest_ghes_dev_register(unsigned int ghes_count) 248 { 249 int rc, i; 250 struct ghes_arr ghes_arr; 251 252 ghes_arr.count = 0; 253 ghes_arr.ghes_devs = kmalloc_array(ghes_count, sizeof(void *), 254 GFP_KERNEL); 255 if (!ghes_arr.ghes_devs) 256 return -ENOMEM; 257 258 rc = apei_hest_parse(hest_parse_ghes, &ghes_arr); 259 if (rc) 260 goto err; 261 262 rc = ghes_estatus_pool_init(ghes_count); 263 if (rc) 264 goto err; 265 266 out: 267 kfree(ghes_arr.ghes_devs); 268 return rc; 269 err: 270 for (i = 0; i < ghes_arr.count; i++) 271 platform_device_unregister(ghes_arr.ghes_devs[i]); 272 goto out; 273 } 274 275 static int __init setup_hest_disable(char *str) 276 { 277 hest_disable = HEST_DISABLED; 278 return 1; 279 } 280 281 __setup("hest_disable", setup_hest_disable); 282 283 void __init acpi_hest_init(void) 284 { 285 acpi_status status; 286 int rc; 287 unsigned int ghes_count = 0; 288 289 if (hest_disable) { 290 pr_info(HEST_PFX "Table parsing disabled.\n"); 291 return; 292 } 293 294 status = acpi_get_table(ACPI_SIG_HEST, 0, 295 (struct acpi_table_header **)&hest_tab); 296 if (status == AE_NOT_FOUND) { 297 hest_disable = HEST_NOT_FOUND; 298 return; 299 } else if (ACPI_FAILURE(status)) { 300 const char *msg = acpi_format_exception(status); 301 pr_err(HEST_PFX "Failed to get table, %s\n", msg); 302 hest_disable = HEST_DISABLED; 303 return; 304 } 305 306 rc = apei_hest_parse(hest_parse_cmc, NULL); 307 if (rc) 308 goto err; 309 310 if (!ghes_disable) { 311 rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); 312 if (rc) 313 goto err; 314 315 if (ghes_count) 316 rc = hest_ghes_dev_register(ghes_count); 317 if (rc) 318 goto err; 319 } 320 321 pr_info(HEST_PFX "Table parsing has been initialized.\n"); 322 return; 323 err: 324 hest_disable = HEST_DISABLED; 325 acpi_put_table((struct acpi_table_header *)hest_tab); 326 } 327