1d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
2d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * APEI Generic Hardware Error Source support
3d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
4d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Generic Hardware Error Source provides a way to report platform
5d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * hardware errors (such as that from chipset). It works in so called
6d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * "Firmware First" mode, that is, hardware errors are reported to
7d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * firmware firstly, then reported to Linux by firmware. This way,
8d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * some non-standard hardware error registers or non-standard hardware
9d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * link can be checked by firmware to produce more hardware error
10d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * information for Linux.
11d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
12d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * For more information about Generic Hardware Error Source, please
13d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * refer to ACPI Specification version 4.0, section 17.3.2.6
14d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
1567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * Copyright 2010,2011 Intel Corp.
16d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *   Author: Huang Ying <ying.huang@intel.com>
17d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
18d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is free software; you can redistribute it and/or
19d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * modify it under the terms of the GNU General Public License version
20d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 2 as published by the Free Software Foundation;
21d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
22d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is distributed in the hope that it will be useful,
23d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * but WITHOUT ANY WARRANTY; without even the implied warranty of
24d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * GNU General Public License for more details.
26d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
27d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * You should have received a copy of the GNU General Public License
28d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * along with this program; if not, write to the Free Software
29d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
31d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
32d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kernel.h>
33d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/module.h>
34d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/init.h>
35d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/acpi.h>
36d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/io.h>
37d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/interrupt.h>
3881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/timer.h>
39d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/cper.h>
40d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kdebug.h>
417ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/platform_device.h>
427ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/mutex.h>
4332c361f574f85fa47600d84900598e2efc99082eHuang Ying#include <linux/ratelimit.h>
4481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/vmalloc.h>
4567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/irq_work.h>
4667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/llist.h>
4767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/genalloc.h>
48a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/pci.h>
49a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/aer.h>
5044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#include <linux/nmi.h>
5140e064153814ce534a28714b25cb98259f107d96Mauro Carvalho Chehab
5240e064153814ce534a28714b25cb98259f107d96Mauro Carvalho Chehab#include <acpi/ghes.h>
539dae3d0d9e64c3cb8bb172f041d4e66d4b92088aTomasz Nowicki#include <acpi/apei.h>
5481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h>
55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h"
57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX	"GHES: "
59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
60d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE		65536
6167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESOURCE_PREALLOC_MAX_SIZE	65536
6267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
6367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
6467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
65152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* This is just an estimation for memory pool allocation */
66152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_AVG_SIZE	512
67152cef40a808d3034e383465b3f7d6783613e458Huang Ying
68152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHES_SIZE	4
69152cef40a808d3034e383465b3f7d6783613e458Huang Ying
7070cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown#define GHES_ESTATUS_IN_CACHE_MAX_NSEC	10000000000ULL
71152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* Prevent too many caches are allocated because of RCU */
72152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_ALLOCED_MAX	(GHES_ESTATUS_CACHES_SIZE * 3 / 2)
73152cef40a808d3034e383465b3f7d6783613e458Huang Ying
74152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_LEN(estatus_len)			\
75152cef40a808d3034e383465b3f7d6783613e458Huang Ying	(sizeof(struct ghes_estatus_cache) + (estatus_len))
76152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_FROM_CACHE(estatus_cache)			\
770a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	((struct acpi_hest_generic_status *)				\
78152cef40a808d3034e383465b3f7d6783613e458Huang Ying	 ((struct ghes_estatus_cache *)(estatus_cache) + 1))
79152cef40a808d3034e383465b3f7d6783613e458Huang Ying
8067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_NODE_LEN(estatus_len)			\
8167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	(sizeof(struct ghes_estatus_node) + (estatus_len))
8288f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong#define GHES_ESTATUS_FROM_NODE(estatus_node)			\
830a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	((struct acpi_hest_generic_status *)				\
8467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 ((struct ghes_estatus_node *)(estatus_node) + 1))
85d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
8690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool ghes_disable;
87b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0);
88b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
9081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function,
9181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one.  This is applied
9281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too.
93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
9481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for
9581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing.
96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci);
987ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex);
99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
10181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information
10281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer
10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so
10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that.
10581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
10681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
10781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
108594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki * Two virtual pages are used, one for IRQ/PROCESS context, the other for
109594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki * NMI context (optionally).
11081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
111594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#ifdef CONFIG_HAVE_ACPI_APEI_NMI
112594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_PAGES           2
113594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#else
114594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_PAGES           1
115594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#endif
116594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_IRQ_PAGE(base)	(base)
117594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_NMI_PAGE(base)	((base) + PAGE_SIZE)
11881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
11981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */
12081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area;
12181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
12281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory
12381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously.
12481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
12581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
12867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct gen_pool *ghes_estatus_pool;
12967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_estatus_pool_size_request;
13067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
131152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
132152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic atomic_t ghes_estatus_cache_alloced;
133152cef40a808d3034e383465b3f7d6783613e458Huang Ying
13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void)
13581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
13681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		VM_IOREMAP, VMALLOC_START, VMALLOC_END);
13881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!ghes_ioremap_area) {
13981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n");
14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return -ENOMEM;
14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void)
14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	free_vm_area(ghes_ioremap_area);
14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr);
15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn)
16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr);
16781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
16881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
16981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
17081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
17181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
17281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
17381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr)
17481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
17581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
17681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
17781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
17881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base));
17981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
180594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki	arch_apei_flush_tlb_one(vaddr);
18181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
18281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
18381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr)
18481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
18581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
18681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
18781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
18881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base));
18981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
190594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki	arch_apei_flush_tlb_one(vaddr);
19181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
19281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
19367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_init(void)
19467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
19567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
19667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (!ghes_estatus_pool)
19767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		return -ENOMEM;
19867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return 0;
19967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
20067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
20167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
20267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					      struct gen_pool_chunk *chunk,
20367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					      void *data)
20467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
20567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	free_page(chunk->start_addr);
20667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
20767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
20867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_exit(void)
20967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
21067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	gen_pool_for_each_chunk(ghes_estatus_pool,
21167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				ghes_estatus_pool_free_chunk_page, NULL);
21267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	gen_pool_destroy(ghes_estatus_pool);
21367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
21467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
21567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_expand(unsigned long len)
21667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
21767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long i, pages, size, addr;
21867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	int ret;
21967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
22067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_size_request += PAGE_ALIGN(len);
22167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	size = gen_pool_size(ghes_estatus_pool);
22267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (size >= ghes_estatus_pool_size_request)
22367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		return 0;
22467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
22567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	for (i = 0; i < pages; i++) {
22667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		addr = __get_free_page(GFP_KERNEL);
22767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (!addr)
22867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			return -ENOMEM;
22967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
23067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (ret)
23167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			return ret;
23267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
23367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
23467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return 0;
23567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
23667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
237d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic)
238d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
239d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
240d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned int error_block_length;
241d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
242d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
243d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
244d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes)
245d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return ERR_PTR(-ENOMEM);
246d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->generic = generic;
24734ddeb035d704eafdcdb3cbc781894300136c3c4Huang Ying	rc = apei_map_generic_address(&generic->error_status_address);
248d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
249d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_free;
250d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	error_block_length = generic->error_block_length;
251d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
252d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
253d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Error status block length is too long: %u for "
254d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "generic hardware error source: %d.\n",
255d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   error_block_length, generic->header.source_id);
256d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		error_block_length = GHES_ESTATUS_MAX_SIZE;
257d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
258d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
259d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus) {
260d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENOMEM;
261d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_unmap;
262d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
263d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
264d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ghes;
265d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
266d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap:
26734ddeb035d704eafdcdb3cbc781894300136c3c4Huang Ying	apei_unmap_generic_address(&generic->error_status_address);
268d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free:
269d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes);
270d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ERR_PTR(rc);
271d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
272d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
273d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes)
274d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
275d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes->estatus);
27634ddeb035d704eafdcdb3cbc781894300136c3c4Huang Ying	apei_unmap_generic_address(&ghes->generic->error_status_address);
277d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
278d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
279d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity)
280d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
281d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (severity) {
282ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_INFORMATIONAL:
283ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_NO;
284ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_CORRECTED:
285ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_CORRECTED;
286ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_RECOVERABLE:
287ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_RECOVERABLE;
288ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_FATAL:
289ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
290d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
29125985edcedea6396277003854657b5f3cb31a628Lucas De Marchi		/* Unknown, go panic */
292ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
29681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
29781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				  int from_phys)
298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
29981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void __iomem *vaddr;
30081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long flags = 0;
30181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int in_nmi = in_nmi();
30281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u64 offset;
30381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u32 trunk;
30481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
30581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	while (len > 0) {
30681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		offset = paddr - (paddr & PAGE_MASK);
30781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
30881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_lock(&ghes_ioremap_lock_nmi);
30981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT);
31081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
31181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_lock_irqsave(&ghes_ioremap_lock_irq, flags);
31281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT);
31381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
31481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = PAGE_SIZE - offset;
31581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = min(trunk, len);
31681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (from_phys)
31781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_fromio(buffer, vaddr + offset, trunk);
31881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		else
31981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_toio(vaddr + offset, buffer, trunk);
32081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		len -= trunk;
32181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		paddr += trunk;
32281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		buffer += trunk;
32381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
32481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_nmi(vaddr);
32581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_unlock(&ghes_ioremap_lock_nmi);
32681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
32781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_irq(vaddr);
32881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags);
32981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
33081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent)
334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *g = ghes->generic;
336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buf_paddr;
337d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u32 len;
338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
340700130b41f4ee54520ac2ef2f7f1d072789711a4Myron Stowe	rc = apei_read(&buf_paddr, &g->error_status_address);
341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!silent && printk_ratelimit())
343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			pr_warning(FW_WARN GHES_PFX
344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n",
345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   g->header.source_id);
346d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EIO;
347d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
348d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!buf_paddr)
349d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
350d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
35181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
35281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      sizeof(*ghes->estatus), 1);
353d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus->block_status)
354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->buffer_paddr = buf_paddr;
357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags |= GHES_TO_CLEAR;
358d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = -EIO;
36088f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	len = cper_estatus_len(ghes->estatus);
361d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len < sizeof(*ghes->estatus))
362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
363d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len > ghes->generic->error_block_length)
364d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
36588f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	if (cper_estatus_check_header(ghes->estatus))
366d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
36781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus + 1,
36881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      buf_paddr + sizeof(*ghes->estatus),
36981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      len - sizeof(*ghes->estatus), 1);
37088f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	if (cper_estatus_check(ghes->estatus))
371d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
372d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = 0;
373d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
374d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block:
37581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc && !silent && printk_ratelimit())
376d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
377d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Failed to read error status block!\n");
378d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
379d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
380d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
381d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes)
382d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
383d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus->block_status = 0;
384d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!(ghes->flags & GHES_TO_CLEAR))
385d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return;
386d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
387d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			      sizeof(ghes->estatus->block_status), 0);
388d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags &= ~GHES_TO_CLEAR;
389d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
390d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
3910a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zhengstatic void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev)
392cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao{
393cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
394cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao	unsigned long pfn;
395ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	int flags = -1;
396cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao	int sec_sev = ghes_severity(gdata->error_severity);
397cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao	struct cper_sec_mem_err *mem_err;
398cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao	mem_err = (struct cper_sec_mem_err *)(gdata + 1);
399cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao
400ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	if (!(mem_err->validation_bits & CPER_MEM_VALID_PA))
401ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		return;
402ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong
403ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	pfn = mem_err->physical_addr >> PAGE_SHIFT;
404ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	if (!pfn_valid(pfn)) {
405ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		pr_warn_ratelimited(FW_WARN GHES_PFX
406ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		"Invalid address in generic error data: %#llx\n",
407ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		mem_err->physical_addr);
408ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		return;
409cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao	}
410ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong
411ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	/* iff following two events can be handled properly by now */
412ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	if (sec_sev == GHES_SEV_CORRECTED &&
413ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	    (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
414ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		flags = MF_SOFT_OFFLINE;
415ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
416ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		flags = 0;
417ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong
418ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong	if (flags != -1)
419ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong		memory_failure_queue(pfn, 0, flags);
420cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao#endif
421cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao}
422cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao
42321480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehabstatic void ghes_do_proc(struct ghes *ghes,
4240a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng			 const struct acpi_hest_generic_status *estatus)
425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
426ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying	int sev, sec_sev;
4270a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_data *gdata;
428d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
42967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	sev = ghes_severity(estatus->error_severity);
43067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	apei_estatus_for_each_section(estatus, gdata) {
431ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying		sec_sev = ghes_severity(gdata->error_severity);
432d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
433d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 CPER_SEC_PLATFORM_MEM)) {
434ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			struct cper_sec_mem_err *mem_err;
435ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			mem_err = (struct cper_sec_mem_err *)(gdata+1);
43621480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab			ghes_edac_report_mem_error(ghes, sev, mem_err);
43721480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab
4389dae3d0d9e64c3cb8bb172f041d4e66d4b92088aTomasz Nowicki			arch_apei_report_mem_error(sev, mem_err);
439cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao			ghes_handle_memory_failure(gdata, sev);
440ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying		}
441a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#ifdef CONFIG_ACPI_APEI_PCIEAER
442a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying		else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
443a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				      CPER_SEC_PCIE)) {
444a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			struct cper_sec_pcie *pcie_err;
445a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			pcie_err = (struct cper_sec_pcie *)(gdata+1);
446a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			if (sev == GHES_SEV_RECOVERABLE &&
447a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			    sec_sev == GHES_SEV_RECOVERABLE &&
448a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			    pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID &&
449a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			    pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) {
450a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				unsigned int devfn;
451a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				int aer_severity;
4520ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall
453a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				devfn = PCI_DEVFN(pcie_err->device_id.device,
454a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying						  pcie_err->device_id.function);
455a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				aer_severity = cper_severity_to_aer(sev);
4560ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall
4570ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall				/*
4580ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall				 * If firmware reset the component to contain
4590ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall				 * the error, we must reinitialize it before
4600ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall				 * use, so treat it as a fatal AER error.
4610ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall				 */
4620ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall				if (gdata->flags & CPER_SEC_RESET)
4630ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall					aer_severity = AER_FATAL;
4640ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall
465a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				aer_recover_queue(pcie_err->device_id.segment,
466a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying						  pcie_err->device_id.bus,
46737448adfc7ce0d6d5892b87aa8d57edde4126f49Lance Ortiz						  devfn, aer_severity,
46837448adfc7ce0d6d5892b87aa8d57edde4126f49Lance Ortiz						  (struct aer_capability_regs *)
46937448adfc7ce0d6d5892b87aa8d57edde4126f49Lance Ortiz						  pcie_err->aer_info);
470a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			}
471a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying
472a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying		}
473a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#endif
474d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
47532c361f574f85fa47600d84900598e2efc99082eHuang Ying}
476d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
47767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void __ghes_print_estatus(const char *pfx,
47867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 const struct acpi_hest_generic *generic,
4790a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng				 const struct acpi_hest_generic_status *estatus)
48032c361f574f85fa47600d84900598e2efc99082eHuang Ying{
4815ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	static atomic_t seqno;
4825ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	unsigned int curr_seqno;
4835ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	char pfx_seq[64];
4845ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying
48532c361f574f85fa47600d84900598e2efc99082eHuang Ying	if (pfx == NULL) {
48667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (ghes_severity(estatus->error_severity) <=
48732c361f574f85fa47600d84900598e2efc99082eHuang Ying		    GHES_SEV_CORRECTED)
4885ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying			pfx = KERN_WARNING;
48932c361f574f85fa47600d84900598e2efc99082eHuang Ying		else
4905ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying			pfx = KERN_ERR;
49132c361f574f85fa47600d84900598e2efc99082eHuang Ying	}
4925ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	curr_seqno = atomic_inc_return(&seqno);
4935ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno);
4945588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
4955ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	       pfx_seq, generic->header.source_id);
49688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	cper_estatus_print(pfx_seq, estatus);
4975588340d46a484da53bbce8136184d9c7fbc259cHuang Ying}
4985588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
499152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_print_estatus(const char *pfx,
500152cef40a808d3034e383465b3f7d6783613e458Huang Ying			      const struct acpi_hest_generic *generic,
5010a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng			      const struct acpi_hest_generic_status *estatus)
5025588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{
5035588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	/* Not more than 2 messages every 5 seconds */
50467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
50567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
50667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct ratelimit_state *ratelimit;
5075588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
50867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
50967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ratelimit = &ratelimit_corrected;
51067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	else
51167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ratelimit = &ratelimit_uncorrected;
512152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (__ratelimit(ratelimit)) {
51367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		__ghes_print_estatus(pfx, generic, estatus);
514152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return 1;
515152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
516152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return 0;
517152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
518152cef40a808d3034e383465b3f7d6783613e458Huang Ying
519152cef40a808d3034e383465b3f7d6783613e458Huang Ying/*
520152cef40a808d3034e383465b3f7d6783613e458Huang Ying * GHES error status reporting throttle, to report more kinds of
521152cef40a808d3034e383465b3f7d6783613e458Huang Ying * errors, instead of just most frequently occurred errors.
522152cef40a808d3034e383465b3f7d6783613e458Huang Ying */
5230a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zhengstatic int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
524152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
525152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len;
526152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int i, cached = 0;
527152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long now;
528152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
5290a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_status *cache_estatus;
530152cef40a808d3034e383465b3f7d6783613e458Huang Ying
53188f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	len = cper_estatus_len(estatus);
532152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_lock();
533152cef40a808d3034e383465b3f7d6783613e458Huang Ying	for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
534152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache = rcu_dereference(ghes_estatus_caches[i]);
535152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (cache == NULL)
536152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
537152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (len != cache->estatus_len)
538152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
539152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
540152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (memcmp(estatus, cache_estatus, len))
541152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
542152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_inc(&cache->count);
543152cef40a808d3034e383465b3f7d6783613e458Huang Ying		now = sched_clock();
544152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
545152cef40a808d3034e383465b3f7d6783613e458Huang Ying			cached = 1;
546152cef40a808d3034e383465b3f7d6783613e458Huang Ying		break;
547152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
548152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_unlock();
549152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return cached;
550152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
551152cef40a808d3034e383465b3f7d6783613e458Huang Ying
552152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic struct ghes_estatus_cache *ghes_estatus_cache_alloc(
553152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic,
5540a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_status *estatus)
555152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
556152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int alloced;
557152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len, cache_len;
558152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
5590a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_status *cache_estatus;
560152cef40a808d3034e383465b3f7d6783613e458Huang Ying
561152cef40a808d3034e383465b3f7d6783613e458Huang Ying	alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
562152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
563152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_dec(&ghes_estatus_cache_alloced);
564152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return NULL;
565152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
56688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	len = cper_estatus_len(estatus);
567152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache_len = GHES_ESTATUS_CACHE_LEN(len);
568152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
569152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (!cache) {
570152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_dec(&ghes_estatus_cache_alloced);
571152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return NULL;
572152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
573152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
574152cef40a808d3034e383465b3f7d6783613e458Huang Ying	memcpy(cache_estatus, estatus, len);
575152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->estatus_len = len;
576152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_set(&cache->count, 0);
577152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->generic = generic;
578152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->time_in = sched_clock();
579152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return cache;
580152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
581152cef40a808d3034e383465b3f7d6783613e458Huang Ying
582152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
583152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
584152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len;
585152cef40a808d3034e383465b3f7d6783613e458Huang Ying
58688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong	len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
587152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = GHES_ESTATUS_CACHE_LEN(len);
588152cef40a808d3034e383465b3f7d6783613e458Huang Ying	gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
589152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_dec(&ghes_estatus_cache_alloced);
590152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
591152cef40a808d3034e383465b3f7d6783613e458Huang Ying
592152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_rcu_free(struct rcu_head *head)
593152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
594152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
595152cef40a808d3034e383465b3f7d6783613e458Huang Ying
596152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache = container_of(head, struct ghes_estatus_cache, rcu);
597152cef40a808d3034e383465b3f7d6783613e458Huang Ying	ghes_estatus_cache_free(cache);
598152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
599152cef40a808d3034e383465b3f7d6783613e458Huang Ying
600152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_add(
601152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic,
6020a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_status *estatus)
603152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
604152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int i, slot = -1, count;
605152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long now, duration, period, max_period = 0;
606152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
607152cef40a808d3034e383465b3f7d6783613e458Huang Ying
608152cef40a808d3034e383465b3f7d6783613e458Huang Ying	new_cache = ghes_estatus_cache_alloc(generic, estatus);
609152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (new_cache == NULL)
610152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return;
611152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_lock();
612152cef40a808d3034e383465b3f7d6783613e458Huang Ying	now = sched_clock();
613152cef40a808d3034e383465b3f7d6783613e458Huang Ying	for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
614152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache = rcu_dereference(ghes_estatus_caches[i]);
615152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (cache == NULL) {
616152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
617152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = NULL;
618152cef40a808d3034e383465b3f7d6783613e458Huang Ying			break;
619152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
620152cef40a808d3034e383465b3f7d6783613e458Huang Ying		duration = now - cache->time_in;
621152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
622152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
623152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = cache;
624152cef40a808d3034e383465b3f7d6783613e458Huang Ying			break;
625152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
626152cef40a808d3034e383465b3f7d6783613e458Huang Ying		count = atomic_read(&cache->count);
62770cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown		period = duration;
62870cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown		do_div(period, (count + 1));
629152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (period > max_period) {
630152cef40a808d3034e383465b3f7d6783613e458Huang Ying			max_period = period;
631152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
632152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = cache;
633152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
634152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
635152cef40a808d3034e383465b3f7d6783613e458Huang Ying	/* new_cache must be put into array after its contents are written */
636152cef40a808d3034e383465b3f7d6783613e458Huang Ying	smp_wmb();
637152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
638152cef40a808d3034e383465b3f7d6783613e458Huang Ying				  slot_cache, new_cache) == slot_cache) {
639152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (slot_cache)
640152cef40a808d3034e383465b3f7d6783613e458Huang Ying			call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
641152cef40a808d3034e383465b3f7d6783613e458Huang Ying	} else
642152cef40a808d3034e383465b3f7d6783613e458Huang Ying		ghes_estatus_cache_free(new_cache);
643152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_unlock();
644d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
645d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
646d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes)
647d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
648d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
649d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
650d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_read_estatus(ghes, 0);
651d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
652d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto out;
653152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (!ghes_estatus_cached(ghes->estatus)) {
654152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
655152cef40a808d3034e383465b3f7d6783613e458Huang Ying			ghes_estatus_cache_add(ghes->generic, ghes->estatus);
656152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
65721480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab	ghes_do_proc(ghes, ghes->estatus);
658d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout:
659d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_clear_estatus(ghes);
660d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
661d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
662d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
66381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes)
66481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
66581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct acpi_hest_generic *g = ghes->generic;
66681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long expire;
66781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
66881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!g->notify.poll_interval) {
66981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n",
67081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   g->header.source_id);
67181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return;
67281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
67381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	expire = jiffies + msecs_to_jiffies(g->notify.poll_interval);
67481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->timer.expires = round_jiffies_relative(expire);
67581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	add_timer(&ghes->timer);
67681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
67781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
67881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data)
67981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
68081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = (void *)data;
68181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
68281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_proc(ghes);
68381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!(ghes->flags & GHES_EXITING))
68481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
68581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
68681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
68781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data)
68881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
68981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = data;
69081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
69181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
69281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_proc(ghes);
69381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
69481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return IRQ_NONE;
69581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
69681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return IRQ_HANDLED;
69781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
69881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
699d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this,
700d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				  unsigned long event, void *data)
701d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
702d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
703d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ret = NOTIFY_DONE;
704d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
705d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_lock();
706d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_rcu(ghes, &ghes_sci, list) {
707d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!ghes_proc(ghes))
708d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			ret = NOTIFY_OK;
709d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
710d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_unlock();
711d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
712d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ret;
713d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
714d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
71544a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic struct notifier_block ghes_notifier_sci = {
71644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	.notifier_call = ghes_notify_sci,
71744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki};
71844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
71944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#ifdef CONFIG_HAVE_ACPI_APEI_NMI
72044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki/*
72144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * printk is not safe in NMI context.  So in NMI handler, we allocate
72244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * required memory from lock-less memory allocator
72344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * (ghes_estatus_pool), save estatus into it, put them into lock-less
72444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * list (ghes_estatus_llist), then delay printk into IRQ context via
72544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * irq_work (ghes_proc_irq_work).  ghes_estatus_size_request record
72644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * required pool size by all NMI error source.
72744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki */
72844a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic struct llist_head ghes_estatus_llist;
72944a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic struct irq_work ghes_proc_irq_work;
73044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
73144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki/*
73244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * NMI may be triggered on any CPU, so ghes_nmi_lock is used for
73344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * mutual exclusion.
73444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki */
73544a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock);
73644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
73744a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic LIST_HEAD(ghes_nmi);
73844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
73944a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic int ghes_panic_timeout	__read_mostly = 30;
74044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
74146d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic struct llist_node *llist_nodes_reverse(struct llist_node *llnode)
74246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{
74346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct llist_node *next, *tail = NULL;
74446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
74546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	while (llnode) {
74646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		next = llnode->next;
74746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		llnode->next = tail;
74846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		tail = llnode;
74946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		llnode = next;
75046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	}
75146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
75246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	return tail;
75346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying}
75446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
75567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_proc_in_irq(struct irq_work *irq_work)
75667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
75746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct llist_node *llnode, *next;
75867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct ghes_estatus_node *estatus_node;
759152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic;
7600a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_status *estatus;
76167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	u32 len, node_len;
76267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
76346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_del_all(&ghes_estatus_llist);
76467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	/*
76567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 * Because the time order of estatus in list is reversed,
76667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 * revert it back to proper order.
76767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 */
76846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_nodes_reverse(llnode);
76967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	while (llnode) {
77067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		next = llnode->next;
77167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus_node = llist_entry(llnode, struct ghes_estatus_node,
77267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					   llnode);
77367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
77488f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong		len = cper_estatus_len(estatus);
77567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
77621480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab		ghes_do_proc(estatus_node->ghes, estatus);
777152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (!ghes_estatus_cached(estatus)) {
778152cef40a808d3034e383465b3f7d6783613e458Huang Ying			generic = estatus_node->generic;
779152cef40a808d3034e383465b3f7d6783613e458Huang Ying			if (ghes_print_estatus(NULL, generic, estatus))
780152cef40a808d3034e383465b3f7d6783613e458Huang Ying				ghes_estatus_cache_add(generic, estatus);
781152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
78267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
78367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			      node_len);
78467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		llnode = next;
78567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
78667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
78767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
78846d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic void ghes_print_queued_estatus(void)
78946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{
79046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct llist_node *llnode;
79146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct ghes_estatus_node *estatus_node;
79246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct acpi_hest_generic *generic;
7930a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	struct acpi_hest_generic_status *estatus;
79446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	u32 len, node_len;
79546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
79646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_del_all(&ghes_estatus_llist);
79746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	/*
79846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	 * Because the time order of estatus in list is reversed,
79946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	 * revert it back to proper order.
80046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	 */
80146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_nodes_reverse(llnode);
80246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	while (llnode) {
80346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		estatus_node = llist_entry(llnode, struct ghes_estatus_node,
80446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying					   llnode);
80546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
80688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong		len = cper_estatus_len(estatus);
80746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
80846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		generic = estatus_node->generic;
80946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		ghes_print_estatus(NULL, generic, estatus);
81046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		llnode = llnode->next;
81146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	}
81246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying}
81346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
8149c48f1c629ecfa114850c03f875c6691003214deDon Zickusstatic int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
81581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
81681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes, *ghes_global = NULL;
81781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int sev, sev_global = -1;
8189c48f1c629ecfa114850c03f875c6691003214deDon Zickus	int ret = NMI_DONE;
81981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
82081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_lock(&ghes_nmi_lock);
82181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
82281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (ghes_read_estatus(ghes, 1)) {
82381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_clear_estatus(ghes);
82481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
82581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
82681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		sev = ghes_severity(ghes->estatus->error_severity);
82781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (sev > sev_global) {
82881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			sev_global = sev;
82981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_global = ghes;
83081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
8319c48f1c629ecfa114850c03f875c6691003214deDon Zickus		ret = NMI_HANDLED;
83281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
83381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
8349c48f1c629ecfa114850c03f875c6691003214deDon Zickus	if (ret == NMI_DONE)
83581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto out;
83681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
83781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (sev_global >= GHES_SEV_PANIC) {
83881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		oops_begin();
83946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		ghes_print_queued_estatus();
8405ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying		__ghes_print_estatus(KERN_EMERG, ghes_global->generic,
84167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				     ghes_global->estatus);
84281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* reboot to log the error! */
84381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (panic_timeout == 0)
84481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			panic_timeout = ghes_panic_timeout;
84581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		panic("Fatal hardware error!");
84681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
84781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
84881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
84967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
85067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		u32 len, node_len;
85167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		struct ghes_estatus_node *estatus_node;
8520a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng		struct acpi_hest_generic_status *estatus;
85367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
85481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (!(ghes->flags & GHES_TO_CLEAR))
85581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
85667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
857152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (ghes_estatus_cached(ghes->estatus))
858152cef40a808d3034e383465b3f7d6783613e458Huang Ying			goto next;
85967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		/* Save estatus for further processing in IRQ context */
86088f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong		len = cper_estatus_len(ghes->estatus);
86167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
86267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
86367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying						      node_len);
86467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (estatus_node) {
86521480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab			estatus_node->ghes = ghes;
86667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			estatus_node->generic = ghes->generic;
86767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
86867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			memcpy(estatus, ghes->estatus, len);
86967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			llist_add(&estatus_node->llnode, &ghes_estatus_llist);
87067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		}
871152cef40a808d3034e383465b3f7d6783613e458Huang Yingnext:
87267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
87381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_clear_estatus(ghes);
87481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
87567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
87667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	irq_work_queue(&ghes_proc_irq_work);
87767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
87881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
87981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout:
88081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_unlock(&ghes_nmi_lock);
88181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return ret;
88281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
88381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
88467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_esource_prealloc_size(
88567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	const struct acpi_hest_generic *generic)
88667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
88767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long block_length, prealloc_records, prealloc_size;
88867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
88967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	block_length = min_t(unsigned long, generic->error_block_length,
89067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			     GHES_ESTATUS_MAX_SIZE);
89167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	prealloc_records = max_t(unsigned long,
89267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 generic->records_to_preallocate, 1);
89367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	prealloc_size = min_t(unsigned long, block_length * prealloc_records,
89467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			      GHES_ESOURCE_PREALLOC_MAX_SIZE);
89567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
89667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return prealloc_size;
89767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
89867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
89944a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_estatus_pool_shrink(unsigned long len)
90044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
90144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
90244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
90344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
90444a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_nmi_add(struct ghes *ghes)
90544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
90644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	unsigned long len;
90744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
90844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	len = ghes_esource_prealloc_size(ghes->generic);
90944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	ghes_estatus_pool_expand(len);
91044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	mutex_lock(&ghes_list_mutex);
91144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	if (list_empty(&ghes_nmi))
91244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes");
91344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	list_add_rcu(&ghes->list, &ghes_nmi);
91444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	mutex_unlock(&ghes_list_mutex);
91544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
91644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
91744a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_nmi_remove(struct ghes *ghes)
91844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
91944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	unsigned long len;
92044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
92144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	mutex_lock(&ghes_list_mutex);
92244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	list_del_rcu(&ghes->list);
92344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	if (list_empty(&ghes_nmi))
92444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		unregister_nmi_handler(NMI_LOCAL, "ghes");
92544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	mutex_unlock(&ghes_list_mutex);
92644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	/*
92744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	 * To synchronize with NMI handler, ghes can only be
92844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	 * freed after NMI handler finishes.
92944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	 */
93044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	synchronize_rcu();
93144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	len = ghes_esource_prealloc_size(ghes->generic);
93244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	ghes_estatus_pool_shrink(len);
93344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
93444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
93544a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_nmi_init_cxt(void)
93644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
93744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
93844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
93944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#else /* CONFIG_HAVE_ACPI_APEI_NMI */
94044a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic inline void ghes_nmi_add(struct ghes *ghes)
94144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
94244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n",
94344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	       ghes->generic->header.source_id);
94444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	BUG();
94544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
94644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
94744a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic inline void ghes_nmi_remove(struct ghes *ghes)
94844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
94944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n",
95044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	       ghes->generic->header.source_id);
95144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	BUG();
95244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
95344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
95444a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic inline void ghes_nmi_init_cxt(void)
95544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{
95644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}
95744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#endif /* CONFIG_HAVE_ACPI_APEI_NMI */
95844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
959da095fd3d5063f2dd03468d71f7df39a0430d86fBill Pembertonstatic int ghes_probe(struct platform_device *ghes_dev)
960d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
961d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
962d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes = NULL;
96344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki
9647ad6e9435596f692ff65f399da12816c94960185Huang Ying	int rc = -EINVAL;
965d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9661dd6b20e368765223c31569d364219785b24700bJin Dongming	generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
967d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!generic->enabled)
9687ad6e9435596f692ff65f399da12816c94960185Huang Ying		return -ENODEV;
969d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
97081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
97181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
97281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
97381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
97444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		break;
97581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
97644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
97744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki			pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
97844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki				generic->header.source_id);
97944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki			goto err;
98044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		}
98181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
98281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_LOCAL:
98381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
984d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
985d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
98681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
98781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
98881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->notify.type, generic->header.source_id);
98981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
990d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
99181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
99281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = -EIO;
99381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (generic->error_block_length <
9940a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng	    sizeof(struct acpi_hest_generic_status)) {
99581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n",
99681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->error_block_length,
997d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
998d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
999d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
1000d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = ghes_new(generic);
1001d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (IS_ERR(ghes)) {
1002d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = PTR_ERR(ghes);
1003d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes = NULL;
1004d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
1005d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
100621480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab
100721480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab	rc = ghes_edac_register(ghes, &ghes_dev->dev);
100821480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab	if (rc < 0)
100921480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab		goto err;
101021480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab
101181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
101281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
101381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.function = ghes_poll_func;
101481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.data = (unsigned long)ghes;
101581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		init_timer_deferrable(&ghes->timer);
101681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
101781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
101881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
101981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* External interrupt vector is GSI */
1020a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun		rc = acpi_gsi_to_irq(generic->notify.vector, &ghes->irq);
1021a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun		if (rc) {
102281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
102381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
102421480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab			goto err_edac_unreg;
102581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
1026a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun		rc = request_irq(ghes->irq, ghes_irq_func, 0, "GHES IRQ", ghes);
1027a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun		if (rc) {
102881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
102981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
103021480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab			goto err_edac_unreg;
103181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
103281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
103381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
10347ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
1035d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (list_empty(&ghes_sci))
1036d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			register_acpi_hed_notifier(&ghes_notifier_sci);
1037d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_add_rcu(&ghes->list, &ghes_sci);
10387ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
103981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
104081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
104144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		ghes_nmi_add(ghes);
104281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
104381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
104481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		BUG();
1045d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
10467ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, ghes);
1047d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1048d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
104921480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehaberr_edac_unreg:
105021480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab	ghes_edac_unregister(ghes);
1051d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr:
10527ad6e9435596f692ff65f399da12816c94960185Huang Ying	if (ghes) {
1053d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
10547ad6e9435596f692ff65f399da12816c94960185Huang Ying		kfree(ghes);
10557ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
1056d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
1057d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1058d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1059b59bc2fbb4bb67e486c40cdb6a306c06acbaec06Bill Pembertonstatic int ghes_remove(struct platform_device *ghes_dev)
1060d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
10617ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct ghes *ghes;
10627ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct acpi_hest_generic *generic;
1063d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10647ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes = platform_get_drvdata(ghes_dev);
10657ad6e9435596f692ff65f399da12816c94960185Huang Ying	generic = ghes->generic;
10667ad6e9435596f692ff65f399da12816c94960185Huang Ying
106781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->flags |= GHES_EXITING;
10687ad6e9435596f692ff65f399da12816c94960185Huang Ying	switch (generic->notify.type) {
106981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
107081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		del_timer_sync(&ghes->timer);
107181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
107281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
107381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		free_irq(ghes->irq, ghes);
107481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
10757ad6e9435596f692ff65f399da12816c94960185Huang Ying	case ACPI_HEST_NOTIFY_SCI:
10767ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
10777ad6e9435596f692ff65f399da12816c94960185Huang Ying		list_del_rcu(&ghes->list);
10787ad6e9435596f692ff65f399da12816c94960185Huang Ying		if (list_empty(&ghes_sci))
10797ad6e9435596f692ff65f399da12816c94960185Huang Ying			unregister_acpi_hed_notifier(&ghes_notifier_sci);
10807ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
10817ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
108281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
108344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki		ghes_nmi_remove(ghes);
108481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
10857ad6e9435596f692ff65f399da12816c94960185Huang Ying	default:
10867ad6e9435596f692ff65f399da12816c94960185Huang Ying		BUG();
10877ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
10887ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
1089d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10907ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes_fini(ghes);
109121480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab
109221480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab	ghes_edac_unregister(ghes);
109321480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab
10947ad6e9435596f692ff65f399da12816c94960185Huang Ying	kfree(ghes);
1095d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10967ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, NULL);
10977ad6e9435596f692ff65f399da12816c94960185Huang Ying
10987ad6e9435596f692ff65f399da12816c94960185Huang Ying	return 0;
1099d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1100d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
11017ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = {
11027ad6e9435596f692ff65f399da12816c94960185Huang Ying	.driver		= {
11037ad6e9435596f692ff65f399da12816c94960185Huang Ying		.name	= "GHES",
11047ad6e9435596f692ff65f399da12816c94960185Huang Ying		.owner	= THIS_MODULE,
11057ad6e9435596f692ff65f399da12816c94960185Huang Ying	},
11067ad6e9435596f692ff65f399da12816c94960185Huang Ying	.probe		= ghes_probe,
11077ad6e9435596f692ff65f399da12816c94960185Huang Ying	.remove		= ghes_remove,
11087ad6e9435596f692ff65f399da12816c94960185Huang Ying};
11097ad6e9435596f692ff65f399da12816c94960185Huang Ying
1110d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void)
1111d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
111281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
111381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
1114d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (acpi_disabled)
1115d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENODEV;
1116d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1117d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_disable) {
1118d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX "HEST is not enabled!\n");
1119d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EINVAL;
1120d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
1121d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1122b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	if (ghes_disable) {
1123b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		pr_info(GHES_PFX "GHES is not enabled!\n");
1124b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		return -EINVAL;
1125b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	}
1126b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
112744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki	ghes_nmi_init_cxt();
112867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
112981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_ioremap_init();
113081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
113181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
113281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
113367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	rc = ghes_estatus_pool_init();
113481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
113581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err_ioremap_exit;
113681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
1137152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
1138152cef40a808d3034e383465b3f7d6783613e458Huang Ying				      GHES_ESTATUS_CACHE_ALLOCED_MAX);
1139152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (rc)
1140152cef40a808d3034e383465b3f7d6783613e458Huang Ying		goto err_pool_exit;
1141152cef40a808d3034e383465b3f7d6783613e458Huang Ying
114267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	rc = platform_driver_register(&ghes_platform_driver);
114367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (rc)
114467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		goto err_pool_exit;
114567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
11469fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	rc = apei_osc_setup();
11479fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	if (rc == 0 && osc_sb_apei_support_acked)
11489fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
11499fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc == 0 && !osc_sb_apei_support_acked)
11509fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
11519fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc && osc_sb_apei_support_acked)
11529fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
11539fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else
11549fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
11559fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying
115681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
115767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingerr_pool_exit:
115867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_exit();
115981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit:
116081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
116181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr:
116281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return rc;
1163d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1164d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1165d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void)
1166d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
11677ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_driver_unregister(&ghes_platform_driver);
116867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_exit();
116981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
1170d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1171d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1172d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init);
1173d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit);
1174d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1175d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying");
1176d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
1177d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL");
11787ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES");
1179