ghes.c revision 9fb0bfe1408d5506b7b83d13d1eed573fd71d67d
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 *
15d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Copyright 2010 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>
45d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h>
46d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/atomicio.h>
47d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h>
48d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h>
4981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h>
50d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h"
52d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX	"GHES: "
54d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE		65536
56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
5881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * One struct ghes is created for each generic hardware error source.
59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
6081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler.
61d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
62d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during
63d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing.
64d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
65d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR		0x0001
6681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_EXITING		0x0002
67d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
68d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes {
69d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
70d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_status *estatus;
71d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buffer_paddr;
72d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned long flags;
7381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	union {
7481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		struct list_head list;
7581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		struct timer_list timer;
7681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		unsigned int irq;
7781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	};
78d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
79d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
80b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingint ghes_disable;
81b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0);
82b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
8381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_panic_timeout	__read_mostly = 30;
8481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
85d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
8681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function,
8781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one.  This is applied
8881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too.
89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
9081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for
9181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing.
92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci);
9481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic LIST_HEAD(ghes_nmi);
957ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex);
96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
9881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * NMI may be triggered on any CPU, so ghes_nmi_lock is used for
9981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * mutual exclusion.
10081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
10181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock);
10281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information
10581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer
10681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so
10781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that.
10881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
10981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
11081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
11181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Two virtual pages are used, one for NMI context, the other for
11281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * IRQ/PROCESS context
11381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
11481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_PAGES		2
11581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_NMI_PAGE(base)	(base)
11681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_IRQ_PAGE(base)	((base) + PAGE_SIZE)
11781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
11881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */
11981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area;
12081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
12181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory
12281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously.
12381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
12481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
12581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void)
12881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
12981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
13081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		VM_IOREMAP, VMALLOC_START, VMALLOC_END);
13181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!ghes_ioremap_area) {
13281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n");
13381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return -ENOMEM;
13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
13581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
13681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
13881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
13981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void)
14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	free_vm_area(ghes_ioremap_area);
14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr);
14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn)
15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr);
16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr)
16781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
16881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
16981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
17081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
17181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base));
17281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
17381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	__flush_tlb_one(vaddr);
17481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
17581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
17681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr)
17781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
17881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
17981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
18081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
18181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base));
18281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
18381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	__flush_tlb_one(vaddr);
18481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
18581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
186d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic)
187d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
188d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
189d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned int error_block_length;
190d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
191d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
192d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
193d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes)
194d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return ERR_PTR(-ENOMEM);
195d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->generic = generic;
196d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_pre_map_gar(&generic->error_status_address);
197d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
198d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_free;
199d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	error_block_length = generic->error_block_length;
200d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
201d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
202d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Error status block length is too long: %u for "
203d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "generic hardware error source: %d.\n",
204d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   error_block_length, generic->header.source_id);
205d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		error_block_length = GHES_ESTATUS_MAX_SIZE;
206d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
207d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
208d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus) {
209d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENOMEM;
210d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_unmap;
211d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
212d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
213d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ghes;
214d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
215d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap:
216d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&generic->error_status_address);
217d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free:
218d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes);
219d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ERR_PTR(rc);
220d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
221d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
222d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes)
223d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
224d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes->estatus);
225d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&ghes->generic->error_status_address);
226d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
227d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
228d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum {
229ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_NO = 0x0,
230ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_CORRECTED = 0x1,
231ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_RECOVERABLE = 0x2,
232ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_PANIC = 0x3,
233d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
234d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
235d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity)
236d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
237d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (severity) {
238ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_INFORMATIONAL:
239ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_NO;
240ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_CORRECTED:
241ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_CORRECTED;
242ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_RECOVERABLE:
243ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_RECOVERABLE;
244ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_FATAL:
245ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
246d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
24725985edcedea6396277003854657b5f3cb31a628Lucas De Marchi		/* Unknown, go panic */
248ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
249d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
250d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
251d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
25281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
25381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				  int from_phys)
254d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
25581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void __iomem *vaddr;
25681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long flags = 0;
25781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int in_nmi = in_nmi();
25881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u64 offset;
25981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u32 trunk;
26081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
26181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	while (len > 0) {
26281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		offset = paddr - (paddr & PAGE_MASK);
26381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
26481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_lock(&ghes_ioremap_lock_nmi);
26581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT);
26681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
26781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_lock_irqsave(&ghes_ioremap_lock_irq, flags);
26881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT);
26981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
27081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = PAGE_SIZE - offset;
27181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = min(trunk, len);
27281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (from_phys)
27381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_fromio(buffer, vaddr + offset, trunk);
27481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		else
27581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_toio(vaddr + offset, buffer, trunk);
27681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		len -= trunk;
27781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		paddr += trunk;
27881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		buffer += trunk;
27981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
28081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_nmi(vaddr);
28181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_unlock(&ghes_ioremap_lock_nmi);
28281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
28381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_irq(vaddr);
28481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags);
28581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
28681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
287d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
288d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
289d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent)
290d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
291d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *g = ghes->generic;
292d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buf_paddr;
293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u32 len;
294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!silent && printk_ratelimit())
299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			pr_warning(FW_WARN GHES_PFX
300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n",
301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   g->header.source_id);
302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EIO;
303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
304d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!buf_paddr)
305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
30781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
30881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      sizeof(*ghes->estatus), 1);
309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus->block_status)
310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->buffer_paddr = buf_paddr;
313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags |= GHES_TO_CLEAR;
314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = -EIO;
316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	len = apei_estatus_len(ghes->estatus);
317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len < sizeof(*ghes->estatus))
318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len > ghes->generic->error_block_length)
320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check_header(ghes->estatus))
322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
32381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus + 1,
32481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      buf_paddr + sizeof(*ghes->estatus),
32581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      len - sizeof(*ghes->estatus), 1);
326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check(ghes->estatus))
327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = 0;
329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block:
33181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc && !silent && printk_ratelimit())
332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Failed to read error status block!\n");
334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
337d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes)
338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus->block_status = 0;
340d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!(ghes->flags & GHES_TO_CLEAR))
341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return;
342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			      sizeof(ghes->estatus->block_status), 0);
344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags &= ~GHES_TO_CLEAR;
345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
346d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
347d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_do_proc(struct ghes *ghes)
348d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
349ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	int sev, processed = 0;
350d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_data *gdata;
351d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
352ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	sev = ghes_severity(ghes->estatus->error_severity);
353d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	apei_estatus_for_each_section(ghes->estatus, gdata) {
354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#ifdef CONFIG_X86_MCE
355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 CPER_SEC_PLATFORM_MEM)) {
357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			apei_mce_report_mem_error(
358ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying				sev == GHES_SEV_CORRECTED,
359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				(struct cper_sec_mem_err *)(gdata+1));
360d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			processed = 1;
361d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		}
362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif
363d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
36432c361f574f85fa47600d84900598e2efc99082eHuang Ying}
365d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
3665588340d46a484da53bbce8136184d9c7fbc259cHuang Yingstatic void __ghes_print_estatus(const char *pfx, struct ghes *ghes)
36732c361f574f85fa47600d84900598e2efc99082eHuang Ying{
36832c361f574f85fa47600d84900598e2efc99082eHuang Ying	if (pfx == NULL) {
36932c361f574f85fa47600d84900598e2efc99082eHuang Ying		if (ghes_severity(ghes->estatus->error_severity) <=
37032c361f574f85fa47600d84900598e2efc99082eHuang Ying		    GHES_SEV_CORRECTED)
37132c361f574f85fa47600d84900598e2efc99082eHuang Ying			pfx = KERN_WARNING HW_ERR;
37232c361f574f85fa47600d84900598e2efc99082eHuang Ying		else
37332c361f574f85fa47600d84900598e2efc99082eHuang Ying			pfx = KERN_ERR HW_ERR;
37432c361f574f85fa47600d84900598e2efc99082eHuang Ying	}
3755588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
3765588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	       pfx, ghes->generic->header.source_id);
3775588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	apei_estatus_print(pfx, ghes->estatus);
3785588340d46a484da53bbce8136184d9c7fbc259cHuang Ying}
3795588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
3805588340d46a484da53bbce8136184d9c7fbc259cHuang Yingstatic void ghes_print_estatus(const char *pfx, struct ghes *ghes)
3815588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{
3825588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	/* Not more than 2 messages every 5 seconds */
3835588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2);
3845588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
3855588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	if (__ratelimit(&ratelimit))
3865588340d46a484da53bbce8136184d9c7fbc259cHuang Ying		__ghes_print_estatus(pfx, ghes);
387d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
388d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
389d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes)
390d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
391d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
392d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
393d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_read_estatus(ghes, 0);
394d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto out;
39632c361f574f85fa47600d84900598e2efc99082eHuang Ying	ghes_print_estatus(NULL, ghes);
397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_do_proc(ghes);
398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout:
400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_clear_estatus(ghes);
401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
40481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes)
40581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
40681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct acpi_hest_generic *g = ghes->generic;
40781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long expire;
40881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
40981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!g->notify.poll_interval) {
41081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n",
41181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   g->header.source_id);
41281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return;
41381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
41481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	expire = jiffies + msecs_to_jiffies(g->notify.poll_interval);
41581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->timer.expires = round_jiffies_relative(expire);
41681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	add_timer(&ghes->timer);
41781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
41881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
41981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data)
42081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
42181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = (void *)data;
42281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
42381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_proc(ghes);
42481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!(ghes->flags & GHES_EXITING))
42581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
42681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
42781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
42881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data)
42981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
43081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = data;
43181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
43281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
43381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_proc(ghes);
43481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
43581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return IRQ_NONE;
43681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
43781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return IRQ_HANDLED;
43881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
43981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
440d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this,
441d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				  unsigned long event, void *data)
442d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
443d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
444d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ret = NOTIFY_DONE;
445d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
446d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_lock();
447d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_rcu(ghes, &ghes_sci, list) {
448d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!ghes_proc(ghes))
449d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			ret = NOTIFY_OK;
450d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
451d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_unlock();
452d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
453d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ret;
454d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
455d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
45681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_notify_nmi(struct notifier_block *this,
45781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				  unsigned long cmd, void *data)
45881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
45981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes, *ghes_global = NULL;
46081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int sev, sev_global = -1;
46181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int ret = NOTIFY_DONE;
46281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
46381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (cmd != DIE_NMI)
46481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return ret;
46581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
46681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_lock(&ghes_nmi_lock);
46781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
46881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (ghes_read_estatus(ghes, 1)) {
46981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_clear_estatus(ghes);
47081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
47181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
47281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		sev = ghes_severity(ghes->estatus->error_severity);
47381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (sev > sev_global) {
47481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			sev_global = sev;
47581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_global = ghes;
47681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
47781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ret = NOTIFY_STOP;
47881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
47981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
48081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (ret == NOTIFY_DONE)
48181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto out;
48281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
48381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (sev_global >= GHES_SEV_PANIC) {
48481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		oops_begin();
4855588340d46a484da53bbce8136184d9c7fbc259cHuang Ying		__ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global);
48681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* reboot to log the error! */
48781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (panic_timeout == 0)
48881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			panic_timeout = ghes_panic_timeout;
48981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		panic("Fatal hardware error!");
49081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
49181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
49281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
49381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (!(ghes->flags & GHES_TO_CLEAR))
49481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
49581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* Do not print estatus because printk is not NMI safe */
49681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_do_proc(ghes);
49781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_clear_estatus(ghes);
49881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
49981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
50081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout:
50181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_unlock(&ghes_nmi_lock);
50281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return ret;
50381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
50481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
505d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = {
506d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	.notifier_call = ghes_notify_sci,
507d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
508d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
50981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct notifier_block ghes_notifier_nmi = {
51081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	.notifier_call = ghes_notify_nmi,
51181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying};
51281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
5137ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev)
514d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
515d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
516d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes = NULL;
5177ad6e9435596f692ff65f399da12816c94960185Huang Ying	int rc = -EINVAL;
518d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
5191dd6b20e368765223c31569d364219785b24700bJin Dongming	generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
520d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!generic->enabled)
5217ad6e9435596f692ff65f399da12816c94960185Huang Ying		return -ENODEV;
522d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
52381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
52481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
52581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
52681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
52781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
52881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
52981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_LOCAL:
53081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
531d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
532d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
53381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
53481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
53581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->notify.type, generic->header.source_id);
53681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
537d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
53881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
53981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = -EIO;
54081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (generic->error_block_length <
54181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	    sizeof(struct acpi_hest_generic_status)) {
54281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n",
54381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->error_block_length,
544d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
545d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
546d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
547d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = ghes_new(generic);
548d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (IS_ERR(ghes)) {
549d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = PTR_ERR(ghes);
550d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes = NULL;
551d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
552d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
55381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
55481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
55581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.function = ghes_poll_func;
55681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.data = (unsigned long)ghes;
55781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		init_timer_deferrable(&ghes->timer);
55881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
55981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
56081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
56181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* External interrupt vector is GSI */
56281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) {
56381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
56481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
56581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			goto err;
56681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
56781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (request_irq(ghes->irq, ghes_irq_func,
56881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				0, "GHES IRQ", ghes)) {
56981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
57081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
57181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			goto err;
57281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
57381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
57481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
5757ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
576d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (list_empty(&ghes_sci))
577d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			register_acpi_hed_notifier(&ghes_notifier_sci);
578d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_add_rcu(&ghes->list, &ghes_sci);
5797ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
58081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
58181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
58281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_lock(&ghes_list_mutex);
58381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (list_empty(&ghes_nmi))
58481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			register_die_notifier(&ghes_notifier_nmi);
58581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		list_add_rcu(&ghes->list, &ghes_nmi);
58681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_unlock(&ghes_list_mutex);
58781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
58881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
58981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		BUG();
590d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
5917ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, ghes);
592d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
593d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
594d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr:
5957ad6e9435596f692ff65f399da12816c94960185Huang Ying	if (ghes) {
596d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
5977ad6e9435596f692ff65f399da12816c94960185Huang Ying		kfree(ghes);
5987ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
599d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
600d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
601d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
6027ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev)
603d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
6047ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct ghes *ghes;
6057ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct acpi_hest_generic *generic;
606d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
6077ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes = platform_get_drvdata(ghes_dev);
6087ad6e9435596f692ff65f399da12816c94960185Huang Ying	generic = ghes->generic;
6097ad6e9435596f692ff65f399da12816c94960185Huang Ying
61081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->flags |= GHES_EXITING;
6117ad6e9435596f692ff65f399da12816c94960185Huang Ying	switch (generic->notify.type) {
61281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
61381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		del_timer_sync(&ghes->timer);
61481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
61581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
61681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		free_irq(ghes->irq, ghes);
61781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
6187ad6e9435596f692ff65f399da12816c94960185Huang Ying	case ACPI_HEST_NOTIFY_SCI:
6197ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
6207ad6e9435596f692ff65f399da12816c94960185Huang Ying		list_del_rcu(&ghes->list);
6217ad6e9435596f692ff65f399da12816c94960185Huang Ying		if (list_empty(&ghes_sci))
6227ad6e9435596f692ff65f399da12816c94960185Huang Ying			unregister_acpi_hed_notifier(&ghes_notifier_sci);
6237ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
6247ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
62581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
62681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_lock(&ghes_list_mutex);
62781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		list_del_rcu(&ghes->list);
62881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (list_empty(&ghes_nmi))
62981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			unregister_die_notifier(&ghes_notifier_nmi);
63081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_unlock(&ghes_list_mutex);
63181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/*
63281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 * To synchronize with NMI handler, ghes can only be
63381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 * freed after NMI handler finishes.
63481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 */
63581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		synchronize_rcu();
63681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
6377ad6e9435596f692ff65f399da12816c94960185Huang Ying	default:
6387ad6e9435596f692ff65f399da12816c94960185Huang Ying		BUG();
6397ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
6407ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
641d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
6427ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes_fini(ghes);
6437ad6e9435596f692ff65f399da12816c94960185Huang Ying	kfree(ghes);
644d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
6457ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, NULL);
6467ad6e9435596f692ff65f399da12816c94960185Huang Ying
6477ad6e9435596f692ff65f399da12816c94960185Huang Ying	return 0;
648d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
649d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
6507ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = {
6517ad6e9435596f692ff65f399da12816c94960185Huang Ying	.driver		= {
6527ad6e9435596f692ff65f399da12816c94960185Huang Ying		.name	= "GHES",
6537ad6e9435596f692ff65f399da12816c94960185Huang Ying		.owner	= THIS_MODULE,
6547ad6e9435596f692ff65f399da12816c94960185Huang Ying	},
6557ad6e9435596f692ff65f399da12816c94960185Huang Ying	.probe		= ghes_probe,
6567ad6e9435596f692ff65f399da12816c94960185Huang Ying	.remove		= ghes_remove,
6577ad6e9435596f692ff65f399da12816c94960185Huang Ying};
6587ad6e9435596f692ff65f399da12816c94960185Huang Ying
659d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void)
660d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
66181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
66281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
663d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (acpi_disabled)
664d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENODEV;
665d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
666d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_disable) {
667d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX "HEST is not enabled!\n");
668d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EINVAL;
669d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
670d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
671b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	if (ghes_disable) {
672b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		pr_info(GHES_PFX "GHES is not enabled!\n");
673b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		return -EINVAL;
674b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	}
675b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
67681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_ioremap_init();
67781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
67881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
67981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
68081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = platform_driver_register(&ghes_platform_driver);
68181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
68281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err_ioremap_exit;
68381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
6849fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	rc = apei_osc_setup();
6859fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	if (rc == 0 && osc_sb_apei_support_acked)
6869fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
6879fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc == 0 && !osc_sb_apei_support_acked)
6889fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
6899fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc && osc_sb_apei_support_acked)
6909fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
6919fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else
6929fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
6939fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying
69481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
69581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit:
69681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
69781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr:
69881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return rc;
699d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
700d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
701d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void)
702d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
7037ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_driver_unregister(&ghes_platform_driver);
70481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
705d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
706d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
707d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init);
708d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit);
709d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
710d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying");
711d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
712d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL");
7137ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES");
714