18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NFIT - Machine Check Handler 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright(c) 2013-2016 Intel Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/notifier.h> 88c2ecf20Sopenharmony_ci#include <linux/acpi.h> 98c2ecf20Sopenharmony_ci#include <linux/nd.h> 108c2ecf20Sopenharmony_ci#include <asm/mce.h> 118c2ecf20Sopenharmony_ci#include "nfit.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic int nfit_handle_mce(struct notifier_block *nb, unsigned long val, 148c2ecf20Sopenharmony_ci void *data) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct mce *mce = (struct mce *)data; 178c2ecf20Sopenharmony_ci struct acpi_nfit_desc *acpi_desc; 188c2ecf20Sopenharmony_ci struct nfit_spa *nfit_spa; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci /* We only care about uncorrectable memory errors */ 218c2ecf20Sopenharmony_ci if (!mce_is_memory_error(mce) || mce_is_correctable(mce)) 228c2ecf20Sopenharmony_ci return NOTIFY_DONE; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci /* Verify the address reported in the MCE is valid. */ 258c2ecf20Sopenharmony_ci if (!mce_usable_address(mce)) 268c2ecf20Sopenharmony_ci return NOTIFY_DONE; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* 298c2ecf20Sopenharmony_ci * mce->addr contains the physical addr accessed that caused the 308c2ecf20Sopenharmony_ci * machine check. We need to walk through the list of NFITs, and see 318c2ecf20Sopenharmony_ci * if any of them matches that address, and only then start a scrub. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci mutex_lock(&acpi_desc_lock); 348c2ecf20Sopenharmony_ci list_for_each_entry(acpi_desc, &acpi_descs, list) { 358c2ecf20Sopenharmony_ci struct device *dev = acpi_desc->dev; 368c2ecf20Sopenharmony_ci int found_match = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci mutex_lock(&acpi_desc->init_mutex); 398c2ecf20Sopenharmony_ci list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { 408c2ecf20Sopenharmony_ci struct acpi_nfit_system_address *spa = nfit_spa->spa; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (nfit_spa_type(spa) != NFIT_SPA_PM) 438c2ecf20Sopenharmony_ci continue; 448c2ecf20Sopenharmony_ci /* find the spa that covers the mce addr */ 458c2ecf20Sopenharmony_ci if (spa->address > mce->addr) 468c2ecf20Sopenharmony_ci continue; 478c2ecf20Sopenharmony_ci if ((spa->address + spa->length - 1) < mce->addr) 488c2ecf20Sopenharmony_ci continue; 498c2ecf20Sopenharmony_ci found_match = 1; 508c2ecf20Sopenharmony_ci dev_dbg(dev, "addr in SPA %d (0x%llx, 0x%llx)\n", 518c2ecf20Sopenharmony_ci spa->range_index, spa->address, spa->length); 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * We can break at the first match because we're going 548c2ecf20Sopenharmony_ci * to rescan all the SPA ranges. There shouldn't be any 558c2ecf20Sopenharmony_ci * aliasing anyway. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci mutex_unlock(&acpi_desc->init_mutex); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (!found_match) 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* If this fails due to an -ENOMEM, there is little we can do */ 658c2ecf20Sopenharmony_ci nvdimm_bus_add_badrange(acpi_desc->nvdimm_bus, 668c2ecf20Sopenharmony_ci ALIGN(mce->addr, L1_CACHE_BYTES), 678c2ecf20Sopenharmony_ci L1_CACHE_BYTES); 688c2ecf20Sopenharmony_ci nvdimm_region_notify(nfit_spa->nd_region, 698c2ecf20Sopenharmony_ci NVDIMM_REVALIDATE_POISON); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) { 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * We can ignore an -EBUSY here because if an ARS is 748c2ecf20Sopenharmony_ci * already in progress, just let that be the last 758c2ecf20Sopenharmony_ci * authoritative one 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci acpi_nfit_ars_rescan(acpi_desc, 0); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci mce->kflags |= MCE_HANDLED_NFIT; 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci mutex_unlock(&acpi_desc_lock); 848c2ecf20Sopenharmony_ci return NOTIFY_DONE; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic struct notifier_block nfit_mce_dec = { 888c2ecf20Sopenharmony_ci .notifier_call = nfit_handle_mce, 898c2ecf20Sopenharmony_ci .priority = MCE_PRIO_NFIT, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_civoid nfit_mce_register(void) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci mce_register_decode_chain(&nfit_mce_dec); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_civoid nfit_mce_unregister(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci mce_unregister_decode_chain(&nfit_mce_dec); 1008c2ecf20Sopenharmony_ci} 101