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>
36700130b41f4ee54520ac2ef2f7f1d072789711a4Myron Stowe#include <linux/acpi_io.h>
37d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/io.h>
38d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/interrupt.h>
3981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/timer.h>
40d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/cper.h>
41d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kdebug.h>
427ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/platform_device.h>
437ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/mutex.h>
4432c361f574f85fa47600d84900598e2efc99082eHuang Ying#include <linux/ratelimit.h>
4581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/vmalloc.h>
4667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/irq_work.h>
4767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/llist.h>
4867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/genalloc.h>
49a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/pci.h>
50a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/aer.h>
51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h>
52d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h>
53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h>
5481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h>
559c48f1c629ecfa114850c03f875c6691003214deDon Zickus#include <asm/nmi.h>
56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h"
58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX	"GHES: "
60d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
61d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE		65536
6267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESOURCE_PREALLOC_MAX_SIZE	65536
6367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
6467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
6567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
66152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* This is just an estimation for memory pool allocation */
67152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_AVG_SIZE	512
68152cef40a808d3034e383465b3f7d6783613e458Huang Ying
69152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHES_SIZE	4
70152cef40a808d3034e383465b3f7d6783613e458Huang Ying
7170cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown#define GHES_ESTATUS_IN_CACHE_MAX_NSEC	10000000000ULL
72152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* Prevent too many caches are allocated because of RCU */
73152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_ALLOCED_MAX	(GHES_ESTATUS_CACHES_SIZE * 3 / 2)
74152cef40a808d3034e383465b3f7d6783613e458Huang Ying
75152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_LEN(estatus_len)			\
76152cef40a808d3034e383465b3f7d6783613e458Huang Ying	(sizeof(struct ghes_estatus_cache) + (estatus_len))
77152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_FROM_CACHE(estatus_cache)			\
78152cef40a808d3034e383465b3f7d6783613e458Huang Ying	((struct acpi_hest_generic_status *)			\
79152cef40a808d3034e383465b3f7d6783613e458Huang Ying	 ((struct ghes_estatus_cache *)(estatus_cache) + 1))
80152cef40a808d3034e383465b3f7d6783613e458Huang Ying
8167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_NODE_LEN(estatus_len)			\
8267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	(sizeof(struct ghes_estatus_node) + (estatus_len))
8367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_FROM_NODE(estatus_node)				\
8467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	((struct acpi_hest_generic_status *)				\
8567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 ((struct ghes_estatus_node *)(estatus_node) + 1))
86d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
87d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
8881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * One struct ghes is created for each generic hardware error source.
89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
9081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler.
91d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during
93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing.
94d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
95d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR		0x0001
9681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_EXITING		0x0002
97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
98d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes {
99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
100d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_status *estatus;
101d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buffer_paddr;
102d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned long flags;
10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	union {
10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		struct list_head list;
10581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		struct timer_list timer;
10681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		unsigned int irq;
10781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	};
108d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
109d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
11067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstruct ghes_estatus_node {
11167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct llist_node llnode;
11267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct acpi_hest_generic *generic;
11367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying};
11467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
115152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache {
116152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 estatus_len;
117152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_t count;
118152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic;
119152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long time_in;
120152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct rcu_head rcu;
121152cef40a808d3034e383465b3f7d6783613e458Huang Ying};
122152cef40a808d3034e383465b3f7d6783613e458Huang Ying
12390ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool ghes_disable;
124b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0);
125b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_panic_timeout	__read_mostly = 30;
12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
128d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
12981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function,
13081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one.  This is applied
13181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too.
132d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
13381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for
13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing.
135d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
136d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci);
13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic LIST_HEAD(ghes_nmi);
1387ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex);
139d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * NMI may be triggered on any CPU, so ghes_nmi_lock is used for
14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * mutual exclusion.
14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock);
14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information
14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer
14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so
15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that.
15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Two virtual pages are used, one for NMI context, the other for
15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * IRQ/PROCESS context
15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_PAGES		2
15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_NMI_PAGE(base)	(base)
15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_IRQ_PAGE(base)	((base) + PAGE_SIZE)
16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */
16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area;
16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory
16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously.
16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
16781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
16881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
16981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
17067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying/*
17167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * printk is not safe in NMI context.  So in NMI handler, we allocate
17267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required memory from lock-less memory allocator
17367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * (ghes_estatus_pool), save estatus into it, put them into lock-less
17467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * list (ghes_estatus_llist), then delay printk into IRQ context via
17567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * irq_work (ghes_proc_irq_work).  ghes_estatus_size_request record
17667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required pool size by all NMI error source.
17767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */
17867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct gen_pool *ghes_estatus_pool;
17967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_estatus_pool_size_request;
18067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct llist_head ghes_estatus_llist;
18167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct irq_work ghes_proc_irq_work;
18267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
183152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
184152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic atomic_t ghes_estatus_cache_alloced;
185152cef40a808d3034e383465b3f7d6783613e458Huang Ying
18681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void)
18781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
18881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
18981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		VM_IOREMAP, VMALLOC_START, VMALLOC_END);
19081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!ghes_ioremap_area) {
19181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n");
19281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return -ENOMEM;
19381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
19481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
19581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
19681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
19781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
19881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void)
19981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
20081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	free_vm_area(ghes_ioremap_area);
20181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
20281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
20381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
20481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
20581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
20681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
20781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr);
20881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
20981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
21081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
21181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
21281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
21381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
21481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn)
21581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
21681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
21781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
21881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr);
21981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
22081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
22181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
22281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
22381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
22481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
22581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr)
22681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
22781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
22881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
22981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
23081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base));
23181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
23281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	__flush_tlb_one(vaddr);
23381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
23481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
23581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr)
23681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
23781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
23881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
23981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
24081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base));
24181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
24281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	__flush_tlb_one(vaddr);
24381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
24481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
24567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_init(void)
24667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
24767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
24867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (!ghes_estatus_pool)
24967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		return -ENOMEM;
25067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return 0;
25167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
25267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
25367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
25467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					      struct gen_pool_chunk *chunk,
25567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					      void *data)
25667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
25767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	free_page(chunk->start_addr);
25867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
25967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
26067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_exit(void)
26167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
26267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	gen_pool_for_each_chunk(ghes_estatus_pool,
26367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				ghes_estatus_pool_free_chunk_page, NULL);
26467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	gen_pool_destroy(ghes_estatus_pool);
26567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
26667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
26767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_expand(unsigned long len)
26867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
26967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long i, pages, size, addr;
27067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	int ret;
27167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
27267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_size_request += PAGE_ALIGN(len);
27367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	size = gen_pool_size(ghes_estatus_pool);
27467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (size >= ghes_estatus_pool_size_request)
27567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		return 0;
27667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
27767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	for (i = 0; i < pages; i++) {
27867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		addr = __get_free_page(GFP_KERNEL);
27967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (!addr)
28067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			return -ENOMEM;
28167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
28267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (ret)
28367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			return ret;
28467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
28567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
28667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return 0;
28767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
28867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
28967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_shrink(unsigned long len)
29067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
29167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
29267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
29367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic)
295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned int error_block_length;
298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes)
302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return ERR_PTR(-ENOMEM);
303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->generic = generic;
304890b3d0ea620c942718dd29d601a7aaa4362280bHuang Ying	rc = apei_map_generic_address(&generic->error_status_address);
305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_free;
307d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	error_block_length = generic->error_block_length;
308d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Error status block length is too long: %u for "
311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "generic hardware error source: %d.\n",
312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   error_block_length, generic->header.source_id);
313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		error_block_length = GHES_ESTATUS_MAX_SIZE;
314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus) {
317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENOMEM;
318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_unmap;
319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ghes;
322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
323d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap:
324890b3d0ea620c942718dd29d601a7aaa4362280bHuang Ying	apei_unmap_generic_address(&generic->error_status_address);
325d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free:
326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes);
327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ERR_PTR(rc);
328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes)
331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes->estatus);
333890b3d0ea620c942718dd29d601a7aaa4362280bHuang Ying	apei_unmap_generic_address(&ghes->generic->error_status_address);
334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum {
337ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_NO = 0x0,
338ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_CORRECTED = 0x1,
339ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_RECOVERABLE = 0x2,
340ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_PANIC = 0x3,
341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity)
344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (severity) {
346ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_INFORMATIONAL:
347ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_NO;
348ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_CORRECTED:
349ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_CORRECTED;
350ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_RECOVERABLE:
351ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_RECOVERABLE;
352ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_FATAL:
353ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
35525985edcedea6396277003854657b5f3cb31a628Lucas De Marchi		/* Unknown, go panic */
356ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
358d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
36081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
36181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				  int from_phys)
362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
36381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void __iomem *vaddr;
36481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long flags = 0;
36581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int in_nmi = in_nmi();
36681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u64 offset;
36781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u32 trunk;
36881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
36981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	while (len > 0) {
37081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		offset = paddr - (paddr & PAGE_MASK);
37181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
37281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_lock(&ghes_ioremap_lock_nmi);
37381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT);
37481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
37581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_lock_irqsave(&ghes_ioremap_lock_irq, flags);
37681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT);
37781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
37881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = PAGE_SIZE - offset;
37981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = min(trunk, len);
38081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (from_phys)
38181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_fromio(buffer, vaddr + offset, trunk);
38281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		else
38381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_toio(vaddr + offset, buffer, trunk);
38481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		len -= trunk;
38581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		paddr += trunk;
38681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		buffer += trunk;
38781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
38881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_nmi(vaddr);
38981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_unlock(&ghes_ioremap_lock_nmi);
39081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
39181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_irq(vaddr);
39281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags);
39381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
39481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
396d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent)
398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *g = ghes->generic;
400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buf_paddr;
401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u32 len;
402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
404700130b41f4ee54520ac2ef2f7f1d072789711a4Myron Stowe	rc = apei_read(&buf_paddr, &g->error_status_address);
405d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
406d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!silent && printk_ratelimit())
407d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			pr_warning(FW_WARN GHES_PFX
408d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n",
409d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   g->header.source_id);
410d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EIO;
411d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
412d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!buf_paddr)
413d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
414d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
41581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
41681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      sizeof(*ghes->estatus), 1);
417d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus->block_status)
418d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
419d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
420d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->buffer_paddr = buf_paddr;
421d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags |= GHES_TO_CLEAR;
422d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
423d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = -EIO;
424d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	len = apei_estatus_len(ghes->estatus);
425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len < sizeof(*ghes->estatus))
426d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
427d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len > ghes->generic->error_block_length)
428d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
429d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check_header(ghes->estatus))
430d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
43181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus + 1,
43281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      buf_paddr + sizeof(*ghes->estatus),
43381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      len - sizeof(*ghes->estatus), 1);
434d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check(ghes->estatus))
435d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
436d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = 0;
437d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
438d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block:
43981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc && !silent && printk_ratelimit())
440d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
441d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Failed to read error status block!\n");
442d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
443d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
444d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
445d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes)
446d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
447d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus->block_status = 0;
448d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!(ghes->flags & GHES_TO_CLEAR))
449d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return;
450d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
451d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			      sizeof(ghes->estatus->block_status), 0);
452d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags &= ~GHES_TO_CLEAR;
453d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
454d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
45567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_do_proc(const struct acpi_hest_generic_status *estatus)
456d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
457ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying	int sev, sec_sev;
458d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_data *gdata;
459d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
46067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	sev = ghes_severity(estatus->error_severity);
46167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	apei_estatus_for_each_section(estatus, gdata) {
462ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying		sec_sev = ghes_severity(gdata->error_severity);
463d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
464d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 CPER_SEC_PLATFORM_MEM)) {
465ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			struct cper_sec_mem_err *mem_err;
466ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			mem_err = (struct cper_sec_mem_err *)(gdata+1);
467ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying#ifdef CONFIG_X86_MCE
468ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED,
469ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying						  mem_err);
470d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif
471ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
472ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			if (sev == GHES_SEV_RECOVERABLE &&
473ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			    sec_sev == GHES_SEV_RECOVERABLE &&
474ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			    mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
475ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying				unsigned long pfn;
476ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying				pfn = mem_err->physical_addr >> PAGE_SHIFT;
477ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying				memory_failure_queue(pfn, 0, 0);
478ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying			}
479ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying#endif
480ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying		}
481a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#ifdef CONFIG_ACPI_APEI_PCIEAER
482a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying		else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
483a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				      CPER_SEC_PCIE)) {
484a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			struct cper_sec_pcie *pcie_err;
485a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			pcie_err = (struct cper_sec_pcie *)(gdata+1);
486a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			if (sev == GHES_SEV_RECOVERABLE &&
487a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			    sec_sev == GHES_SEV_RECOVERABLE &&
488a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			    pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID &&
489a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			    pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) {
490a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				unsigned int devfn;
491a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				int aer_severity;
492a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				devfn = PCI_DEVFN(pcie_err->device_id.device,
493a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying						  pcie_err->device_id.function);
494a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				aer_severity = cper_severity_to_aer(sev);
495a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying				aer_recover_queue(pcie_err->device_id.segment,
496a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying						  pcie_err->device_id.bus,
497a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying						  devfn, aer_severity);
498a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying			}
499a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying
500a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying		}
501a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#endif
502d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
50332c361f574f85fa47600d84900598e2efc99082eHuang Ying}
504d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
50567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void __ghes_print_estatus(const char *pfx,
50667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 const struct acpi_hest_generic *generic,
50767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 const struct acpi_hest_generic_status *estatus)
50832c361f574f85fa47600d84900598e2efc99082eHuang Ying{
5095ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	static atomic_t seqno;
5105ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	unsigned int curr_seqno;
5115ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	char pfx_seq[64];
5125ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying
51332c361f574f85fa47600d84900598e2efc99082eHuang Ying	if (pfx == NULL) {
51467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (ghes_severity(estatus->error_severity) <=
51532c361f574f85fa47600d84900598e2efc99082eHuang Ying		    GHES_SEV_CORRECTED)
5165ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying			pfx = KERN_WARNING;
51732c361f574f85fa47600d84900598e2efc99082eHuang Ying		else
5185ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying			pfx = KERN_ERR;
51932c361f574f85fa47600d84900598e2efc99082eHuang Ying	}
5205ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	curr_seqno = atomic_inc_return(&seqno);
5215ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno);
5225588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
5235ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	       pfx_seq, generic->header.source_id);
5245ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying	apei_estatus_print(pfx_seq, estatus);
5255588340d46a484da53bbce8136184d9c7fbc259cHuang Ying}
5265588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
527152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_print_estatus(const char *pfx,
528152cef40a808d3034e383465b3f7d6783613e458Huang Ying			      const struct acpi_hest_generic *generic,
529152cef40a808d3034e383465b3f7d6783613e458Huang Ying			      const struct acpi_hest_generic_status *estatus)
5305588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{
5315588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	/* Not more than 2 messages every 5 seconds */
53267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
53367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
53467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct ratelimit_state *ratelimit;
5355588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
53667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
53767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ratelimit = &ratelimit_corrected;
53867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	else
53967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ratelimit = &ratelimit_uncorrected;
540152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (__ratelimit(ratelimit)) {
54167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		__ghes_print_estatus(pfx, generic, estatus);
542152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return 1;
543152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
544152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return 0;
545152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
546152cef40a808d3034e383465b3f7d6783613e458Huang Ying
547152cef40a808d3034e383465b3f7d6783613e458Huang Ying/*
548152cef40a808d3034e383465b3f7d6783613e458Huang Ying * GHES error status reporting throttle, to report more kinds of
549152cef40a808d3034e383465b3f7d6783613e458Huang Ying * errors, instead of just most frequently occurred errors.
550152cef40a808d3034e383465b3f7d6783613e458Huang Ying */
551152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
552152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
553152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len;
554152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int i, cached = 0;
555152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long now;
556152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
557152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *cache_estatus;
558152cef40a808d3034e383465b3f7d6783613e458Huang Ying
559152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = apei_estatus_len(estatus);
560152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_lock();
561152cef40a808d3034e383465b3f7d6783613e458Huang Ying	for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
562152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache = rcu_dereference(ghes_estatus_caches[i]);
563152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (cache == NULL)
564152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
565152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (len != cache->estatus_len)
566152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
567152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
568152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (memcmp(estatus, cache_estatus, len))
569152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
570152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_inc(&cache->count);
571152cef40a808d3034e383465b3f7d6783613e458Huang Ying		now = sched_clock();
572152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
573152cef40a808d3034e383465b3f7d6783613e458Huang Ying			cached = 1;
574152cef40a808d3034e383465b3f7d6783613e458Huang Ying		break;
575152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
576152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_unlock();
577152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return cached;
578152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
579152cef40a808d3034e383465b3f7d6783613e458Huang Ying
580152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic struct ghes_estatus_cache *ghes_estatus_cache_alloc(
581152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic,
582152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *estatus)
583152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
584152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int alloced;
585152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len, cache_len;
586152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
587152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *cache_estatus;
588152cef40a808d3034e383465b3f7d6783613e458Huang Ying
589152cef40a808d3034e383465b3f7d6783613e458Huang Ying	alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
590152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
591152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_dec(&ghes_estatus_cache_alloced);
592152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return NULL;
593152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
594152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = apei_estatus_len(estatus);
595152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache_len = GHES_ESTATUS_CACHE_LEN(len);
596152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
597152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (!cache) {
598152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_dec(&ghes_estatus_cache_alloced);
599152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return NULL;
600152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
601152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
602152cef40a808d3034e383465b3f7d6783613e458Huang Ying	memcpy(cache_estatus, estatus, len);
603152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->estatus_len = len;
604152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_set(&cache->count, 0);
605152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->generic = generic;
606152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->time_in = sched_clock();
607152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return cache;
608152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
609152cef40a808d3034e383465b3f7d6783613e458Huang Ying
610152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
611152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
612152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len;
613152cef40a808d3034e383465b3f7d6783613e458Huang Ying
614152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
615152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = GHES_ESTATUS_CACHE_LEN(len);
616152cef40a808d3034e383465b3f7d6783613e458Huang Ying	gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
617152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_dec(&ghes_estatus_cache_alloced);
618152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
619152cef40a808d3034e383465b3f7d6783613e458Huang Ying
620152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_rcu_free(struct rcu_head *head)
621152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
622152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
623152cef40a808d3034e383465b3f7d6783613e458Huang Ying
624152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache = container_of(head, struct ghes_estatus_cache, rcu);
625152cef40a808d3034e383465b3f7d6783613e458Huang Ying	ghes_estatus_cache_free(cache);
626152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
627152cef40a808d3034e383465b3f7d6783613e458Huang Ying
628152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_add(
629152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic,
630152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *estatus)
631152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
632152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int i, slot = -1, count;
633152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long now, duration, period, max_period = 0;
634152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
635152cef40a808d3034e383465b3f7d6783613e458Huang Ying
636152cef40a808d3034e383465b3f7d6783613e458Huang Ying	new_cache = ghes_estatus_cache_alloc(generic, estatus);
637152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (new_cache == NULL)
638152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return;
639152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_lock();
640152cef40a808d3034e383465b3f7d6783613e458Huang Ying	now = sched_clock();
641152cef40a808d3034e383465b3f7d6783613e458Huang Ying	for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
642152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache = rcu_dereference(ghes_estatus_caches[i]);
643152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (cache == NULL) {
644152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
645152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = NULL;
646152cef40a808d3034e383465b3f7d6783613e458Huang Ying			break;
647152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
648152cef40a808d3034e383465b3f7d6783613e458Huang Ying		duration = now - cache->time_in;
649152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
650152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
651152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = cache;
652152cef40a808d3034e383465b3f7d6783613e458Huang Ying			break;
653152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
654152cef40a808d3034e383465b3f7d6783613e458Huang Ying		count = atomic_read(&cache->count);
65570cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown		period = duration;
65670cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown		do_div(period, (count + 1));
657152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (period > max_period) {
658152cef40a808d3034e383465b3f7d6783613e458Huang Ying			max_period = period;
659152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
660152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = cache;
661152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
662152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
663152cef40a808d3034e383465b3f7d6783613e458Huang Ying	/* new_cache must be put into array after its contents are written */
664152cef40a808d3034e383465b3f7d6783613e458Huang Ying	smp_wmb();
665152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
666152cef40a808d3034e383465b3f7d6783613e458Huang Ying				  slot_cache, new_cache) == slot_cache) {
667152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (slot_cache)
668152cef40a808d3034e383465b3f7d6783613e458Huang Ying			call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
669152cef40a808d3034e383465b3f7d6783613e458Huang Ying	} else
670152cef40a808d3034e383465b3f7d6783613e458Huang Ying		ghes_estatus_cache_free(new_cache);
671152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_unlock();
672d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
673d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
674d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes)
675d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
676d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
677d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
678d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_read_estatus(ghes, 0);
679d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
680d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto out;
681152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (!ghes_estatus_cached(ghes->estatus)) {
682152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
683152cef40a808d3034e383465b3f7d6783613e458Huang Ying			ghes_estatus_cache_add(ghes->generic, ghes->estatus);
684152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
68567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_do_proc(ghes->estatus);
686d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout:
687d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_clear_estatus(ghes);
688d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
689d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
690d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
69181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes)
69281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
69381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct acpi_hest_generic *g = ghes->generic;
69481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long expire;
69581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
69681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!g->notify.poll_interval) {
69781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n",
69881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   g->header.source_id);
69981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return;
70081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
70181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	expire = jiffies + msecs_to_jiffies(g->notify.poll_interval);
70281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->timer.expires = round_jiffies_relative(expire);
70381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	add_timer(&ghes->timer);
70481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
70581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
70681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data)
70781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
70881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = (void *)data;
70981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
71081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_proc(ghes);
71181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!(ghes->flags & GHES_EXITING))
71281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
71381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
71481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
71581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data)
71681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
71781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = data;
71881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
71981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
72081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_proc(ghes);
72181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
72281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return IRQ_NONE;
72381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
72481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return IRQ_HANDLED;
72581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
72681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
727d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this,
728d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				  unsigned long event, void *data)
729d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
730d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
731d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ret = NOTIFY_DONE;
732d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
733d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_lock();
734d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_rcu(ghes, &ghes_sci, list) {
735d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!ghes_proc(ghes))
736d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			ret = NOTIFY_OK;
737d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
738d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_unlock();
739d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
740d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ret;
741d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
742d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
74346d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic struct llist_node *llist_nodes_reverse(struct llist_node *llnode)
74446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{
74546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct llist_node *next, *tail = NULL;
74646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
74746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	while (llnode) {
74846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		next = llnode->next;
74946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		llnode->next = tail;
75046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		tail = llnode;
75146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		llnode = next;
75246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	}
75346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
75446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	return tail;
75546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying}
75646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
75767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_proc_in_irq(struct irq_work *irq_work)
75867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
75946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct llist_node *llnode, *next;
76067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct ghes_estatus_node *estatus_node;
761152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic;
76267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct acpi_hest_generic_status *estatus;
76367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	u32 len, node_len;
76467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
76546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_del_all(&ghes_estatus_llist);
76667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	/*
76767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 * Because the time order of estatus in list is reversed,
76867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 * revert it back to proper order.
76967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 */
77046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_nodes_reverse(llnode);
77167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	while (llnode) {
77267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		next = llnode->next;
77367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus_node = llist_entry(llnode, struct ghes_estatus_node,
77467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					   llnode);
77567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
77667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = apei_estatus_len(estatus);
77767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
77867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ghes_do_proc(estatus);
779152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (!ghes_estatus_cached(estatus)) {
780152cef40a808d3034e383465b3f7d6783613e458Huang Ying			generic = estatus_node->generic;
781152cef40a808d3034e383465b3f7d6783613e458Huang Ying			if (ghes_print_estatus(NULL, generic, estatus))
782152cef40a808d3034e383465b3f7d6783613e458Huang Ying				ghes_estatus_cache_add(generic, estatus);
783152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
78467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
78567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			      node_len);
78667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		llnode = next;
78767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
78867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
78967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
79046d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic void ghes_print_queued_estatus(void)
79146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{
79246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct llist_node *llnode;
79346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct ghes_estatus_node *estatus_node;
79446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct acpi_hest_generic *generic;
79546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	struct acpi_hest_generic_status *estatus;
79646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	u32 len, node_len;
79746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
79846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_del_all(&ghes_estatus_llist);
79946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	/*
80046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	 * Because the time order of estatus in list is reversed,
80146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	 * revert it back to proper order.
80246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	 */
80346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	llnode = llist_nodes_reverse(llnode);
80446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	while (llnode) {
80546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		estatus_node = llist_entry(llnode, struct ghes_estatus_node,
80646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying					   llnode);
80746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
80846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		len = apei_estatus_len(estatus);
80946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
81046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		generic = estatus_node->generic;
81146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		ghes_print_estatus(NULL, generic, estatus);
81246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		llnode = llnode->next;
81346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying	}
81446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying}
81546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying
8169c48f1c629ecfa114850c03f875c6691003214deDon Zickusstatic int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
81781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
81881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes, *ghes_global = NULL;
81981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int sev, sev_global = -1;
8209c48f1c629ecfa114850c03f875c6691003214deDon Zickus	int ret = NMI_DONE;
82181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
82281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_lock(&ghes_nmi_lock);
82381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
82481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (ghes_read_estatus(ghes, 1)) {
82581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_clear_estatus(ghes);
82681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
82781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
82881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		sev = ghes_severity(ghes->estatus->error_severity);
82981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (sev > sev_global) {
83081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			sev_global = sev;
83181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_global = ghes;
83281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
8339c48f1c629ecfa114850c03f875c6691003214deDon Zickus		ret = NMI_HANDLED;
83481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
83581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
8369c48f1c629ecfa114850c03f875c6691003214deDon Zickus	if (ret == NMI_DONE)
83781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto out;
83881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
83981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (sev_global >= GHES_SEV_PANIC) {
84081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		oops_begin();
84146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying		ghes_print_queued_estatus();
8425ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying		__ghes_print_estatus(KERN_EMERG, ghes_global->generic,
84367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				     ghes_global->estatus);
84481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* reboot to log the error! */
84581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (panic_timeout == 0)
84681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			panic_timeout = ghes_panic_timeout;
84781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		panic("Fatal hardware error!");
84881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
84981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
85081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
85167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
85267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		u32 len, node_len;
85367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		struct ghes_estatus_node *estatus_node;
85467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		struct acpi_hest_generic_status *estatus;
85567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
85681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (!(ghes->flags & GHES_TO_CLEAR))
85781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
85867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
859152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (ghes_estatus_cached(ghes->estatus))
860152cef40a808d3034e383465b3f7d6783613e458Huang Ying			goto next;
86167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		/* Save estatus for further processing in IRQ context */
86267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = apei_estatus_len(ghes->estatus);
86367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
86467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
86567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying						      node_len);
86667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (estatus_node) {
86767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			estatus_node->generic = ghes->generic;
86867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
86967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			memcpy(estatus, ghes->estatus, len);
87067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			llist_add(&estatus_node->llnode, &ghes_estatus_llist);
87167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		}
872152cef40a808d3034e383465b3f7d6783613e458Huang Yingnext:
87367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
87481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_clear_estatus(ghes);
87581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
87667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
87767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	irq_work_queue(&ghes_proc_irq_work);
87867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
87981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
88081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout:
88181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_unlock(&ghes_nmi_lock);
88281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return ret;
88381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
88481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
885d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = {
886d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	.notifier_call = ghes_notify_sci,
887d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
888d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
88967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_esource_prealloc_size(
89067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	const struct acpi_hest_generic *generic)
89167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
89267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long block_length, prealloc_records, prealloc_size;
89367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
89467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	block_length = min_t(unsigned long, generic->error_block_length,
89567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			     GHES_ESTATUS_MAX_SIZE);
89667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	prealloc_records = max_t(unsigned long,
89767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 generic->records_to_preallocate, 1);
89867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	prealloc_size = min_t(unsigned long, block_length * prealloc_records,
89967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			      GHES_ESOURCE_PREALLOC_MAX_SIZE);
90067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
90167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return prealloc_size;
90267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
90367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
9047ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev)
905d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
906d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
907d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes = NULL;
90867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long len;
9097ad6e9435596f692ff65f399da12816c94960185Huang Ying	int rc = -EINVAL;
910d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9111dd6b20e368765223c31569d364219785b24700bJin Dongming	generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
912d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!generic->enabled)
9137ad6e9435596f692ff65f399da12816c94960185Huang Ying		return -ENODEV;
914d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
91581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
91681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
91781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
91881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
91981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
92081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
92181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_LOCAL:
92281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
923d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
924d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
92581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
92681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
92781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->notify.type, generic->header.source_id);
92881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
929d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
93081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
93181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = -EIO;
93281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (generic->error_block_length <
93381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	    sizeof(struct acpi_hest_generic_status)) {
93481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n",
93581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->error_block_length,
936d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
937d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
938d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
939d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = ghes_new(generic);
940d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (IS_ERR(ghes)) {
941d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = PTR_ERR(ghes);
942d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes = NULL;
943d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
944d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
94581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
94681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
94781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.function = ghes_poll_func;
94881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.data = (unsigned long)ghes;
94981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		init_timer_deferrable(&ghes->timer);
95081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
95181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
95281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
95381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* External interrupt vector is GSI */
95481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) {
95581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
95681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
95781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			goto err;
95881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
95981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (request_irq(ghes->irq, ghes_irq_func,
96081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				0, "GHES IRQ", ghes)) {
96181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
96281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
96381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			goto err;
96481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
96581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
96681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
9677ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
968d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (list_empty(&ghes_sci))
969d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			register_acpi_hed_notifier(&ghes_notifier_sci);
970d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_add_rcu(&ghes->list, &ghes_sci);
9717ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
97281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
97381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
97467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = ghes_esource_prealloc_size(generic);
97567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ghes_estatus_pool_expand(len);
97681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_lock(&ghes_list_mutex);
97781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (list_empty(&ghes_nmi))
9789c48f1c629ecfa114850c03f875c6691003214deDon Zickus			register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0,
9799c48f1c629ecfa114850c03f875c6691003214deDon Zickus						"ghes");
98081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		list_add_rcu(&ghes->list, &ghes_nmi);
98181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_unlock(&ghes_list_mutex);
98281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
98381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
98481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		BUG();
985d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
9867ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, ghes);
987d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
988d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
989d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr:
9907ad6e9435596f692ff65f399da12816c94960185Huang Ying	if (ghes) {
991d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
9927ad6e9435596f692ff65f399da12816c94960185Huang Ying		kfree(ghes);
9937ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
994d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
995d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
996d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9977ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev)
998d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
9997ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct ghes *ghes;
10007ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct acpi_hest_generic *generic;
100167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long len;
1002d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10037ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes = platform_get_drvdata(ghes_dev);
10047ad6e9435596f692ff65f399da12816c94960185Huang Ying	generic = ghes->generic;
10057ad6e9435596f692ff65f399da12816c94960185Huang Ying
100681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->flags |= GHES_EXITING;
10077ad6e9435596f692ff65f399da12816c94960185Huang Ying	switch (generic->notify.type) {
100881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
100981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		del_timer_sync(&ghes->timer);
101081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
101181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
101281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		free_irq(ghes->irq, ghes);
101381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
10147ad6e9435596f692ff65f399da12816c94960185Huang Ying	case ACPI_HEST_NOTIFY_SCI:
10157ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
10167ad6e9435596f692ff65f399da12816c94960185Huang Ying		list_del_rcu(&ghes->list);
10177ad6e9435596f692ff65f399da12816c94960185Huang Ying		if (list_empty(&ghes_sci))
10187ad6e9435596f692ff65f399da12816c94960185Huang Ying			unregister_acpi_hed_notifier(&ghes_notifier_sci);
10197ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
10207ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
102181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
102281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_lock(&ghes_list_mutex);
102381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		list_del_rcu(&ghes->list);
102481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (list_empty(&ghes_nmi))
10259c48f1c629ecfa114850c03f875c6691003214deDon Zickus			unregister_nmi_handler(NMI_LOCAL, "ghes");
102681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_unlock(&ghes_list_mutex);
102781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/*
102881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 * To synchronize with NMI handler, ghes can only be
102981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 * freed after NMI handler finishes.
103081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 */
103181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		synchronize_rcu();
103267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = ghes_esource_prealloc_size(generic);
103367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ghes_estatus_pool_shrink(len);
103481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
10357ad6e9435596f692ff65f399da12816c94960185Huang Ying	default:
10367ad6e9435596f692ff65f399da12816c94960185Huang Ying		BUG();
10377ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
10387ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
1039d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10407ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes_fini(ghes);
10417ad6e9435596f692ff65f399da12816c94960185Huang Ying	kfree(ghes);
1042d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10437ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, NULL);
10447ad6e9435596f692ff65f399da12816c94960185Huang Ying
10457ad6e9435596f692ff65f399da12816c94960185Huang Ying	return 0;
1046d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1047d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10487ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = {
10497ad6e9435596f692ff65f399da12816c94960185Huang Ying	.driver		= {
10507ad6e9435596f692ff65f399da12816c94960185Huang Ying		.name	= "GHES",
10517ad6e9435596f692ff65f399da12816c94960185Huang Ying		.owner	= THIS_MODULE,
10527ad6e9435596f692ff65f399da12816c94960185Huang Ying	},
10537ad6e9435596f692ff65f399da12816c94960185Huang Ying	.probe		= ghes_probe,
10547ad6e9435596f692ff65f399da12816c94960185Huang Ying	.remove		= ghes_remove,
10557ad6e9435596f692ff65f399da12816c94960185Huang Ying};
10567ad6e9435596f692ff65f399da12816c94960185Huang Ying
1057d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void)
1058d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
105981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
106081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
1061d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (acpi_disabled)
1062d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENODEV;
1063d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1064d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_disable) {
1065d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX "HEST is not enabled!\n");
1066d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EINVAL;
1067d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
1068d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1069b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	if (ghes_disable) {
1070b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		pr_info(GHES_PFX "GHES is not enabled!\n");
1071b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		return -EINVAL;
1072b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	}
1073b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
107467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
107567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
107681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_ioremap_init();
107781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
107881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
107981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
108067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	rc = ghes_estatus_pool_init();
108181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
108281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err_ioremap_exit;
108381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
1084152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
1085152cef40a808d3034e383465b3f7d6783613e458Huang Ying				      GHES_ESTATUS_CACHE_ALLOCED_MAX);
1086152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (rc)
1087152cef40a808d3034e383465b3f7d6783613e458Huang Ying		goto err_pool_exit;
1088152cef40a808d3034e383465b3f7d6783613e458Huang Ying
108967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	rc = platform_driver_register(&ghes_platform_driver);
109067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (rc)
109167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		goto err_pool_exit;
109267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
10939fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	rc = apei_osc_setup();
10949fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	if (rc == 0 && osc_sb_apei_support_acked)
10959fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
10969fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc == 0 && !osc_sb_apei_support_acked)
10979fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
10989fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc && osc_sb_apei_support_acked)
10999fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
11009fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else
11019fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
11029fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying
110381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
110467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingerr_pool_exit:
110567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_exit();
110681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit:
110781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
110881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr:
110981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return rc;
1110d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1111d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1112d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void)
1113d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
11147ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_driver_unregister(&ghes_platform_driver);
111567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_exit();
111681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
1117d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1118d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1119d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init);
1120d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit);
1121d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1122d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying");
1123d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
1124d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL");
11257ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES");
1126