1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2024 Intel Corporation. All rights reserved. */ 3 #include <linux/mm.h> 4 #include <linux/notifier.h> 5 #include <linux/set_memory.h> 6 #include <asm/mce.h> 7 #include <cxl.h> 8 #include "core.h" 9 #include "mce.h" 10 11 static int cxl_handle_mce(struct notifier_block *nb, unsigned long val, 12 void *data) 13 { 14 struct cxl_region *cxlr = container_of(nb, struct cxl_region, 15 mce_notifier); 16 struct cxl_region_params *p = &cxlr->params; 17 struct mce *mce = data; 18 u64 spa, spa_alias; 19 unsigned long pfn; 20 21 if (!mce || !mce_usable_address(mce)) 22 return NOTIFY_DONE; 23 24 spa = mce->addr & MCI_ADDR_PHYSADDR; 25 26 if (!cxl_resource_contains_addr(p->res, spa)) 27 return NOTIFY_DONE; 28 29 if (spa >= p->res->start + p->cache_size) 30 spa_alias = spa - p->cache_size; 31 else 32 spa_alias = spa + p->cache_size; 33 34 pfn = spa_alias >> PAGE_SHIFT; 35 if (!pfn_valid(pfn)) 36 return NOTIFY_DONE; 37 38 /* 39 * Take down the aliased memory page. The original memory page flagged 40 * by the MCE will be taken cared of by the standard MCE handler. 41 */ 42 dev_emerg(&cxlr->dev, "Offlining aliased SPA address0: %#llx\n", 43 spa_alias); 44 if (!memory_failure(pfn, 0)) 45 set_mce_nospec(pfn); 46 47 return NOTIFY_OK; 48 } 49 50 static void cxl_unregister_mce_notifier(void *mce_notifier) 51 { 52 mce_unregister_decode_chain(mce_notifier); 53 } 54 55 int devm_cxl_register_mce_notifier(struct device *dev, 56 struct notifier_block *mce_notifier) 57 { 58 mce_notifier->notifier_call = cxl_handle_mce; 59 mce_notifier->priority = MCE_PRIO_UC; 60 mce_register_decode_chain(mce_notifier); 61 62 return devm_add_action_or_reset(dev, cxl_unregister_mce_notifier, 63 mce_notifier); 64 } 65