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
cxl_handle_mce(struct notifier_block * nb,unsigned long val,void * data)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
cxl_unregister_mce_notifier(void * mce_notifier)50 static void cxl_unregister_mce_notifier(void *mce_notifier)
51 {
52 mce_unregister_decode_chain(mce_notifier);
53 }
54
devm_cxl_register_mce_notifier(struct device * dev,struct notifier_block * mce_notifier)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