ghes.c revision 152cef40a808d3034e383465b3f7d6783613e458
1d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
2d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * APEI Generic Hardware Error Source support
3d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
4d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Generic Hardware Error Source provides a way to report platform
5d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * hardware errors (such as that from chipset). It works in so called
6d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * "Firmware First" mode, that is, hardware errors are reported to
7d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * firmware firstly, then reported to Linux by firmware. This way,
8d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * some non-standard hardware error registers or non-standard hardware
9d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * link can be checked by firmware to produce more hardware error
10d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * information for Linux.
11d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
12d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * For more information about Generic Hardware Error Source, please
13d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * refer to ACPI Specification version 4.0, section 17.3.2.6
14d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
1567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * Copyright 2010,2011 Intel Corp.
16d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *   Author: Huang Ying <ying.huang@intel.com>
17d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
18d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is free software; you can redistribute it and/or
19d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * modify it under the terms of the GNU General Public License version
20d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 2 as published by the Free Software Foundation;
21d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
22d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is distributed in the hope that it will be useful,
23d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * but WITHOUT ANY WARRANTY; without even the implied warranty of
24d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * GNU General Public License for more details.
26d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
27d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * You should have received a copy of the GNU General Public License
28d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * along with this program; if not, write to the Free Software
29d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
31d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
32d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kernel.h>
33d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/module.h>
34d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/init.h>
35d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/acpi.h>
36d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/io.h>
37d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/interrupt.h>
3881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/timer.h>
39d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/cper.h>
40d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kdebug.h>
417ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/platform_device.h>
427ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/mutex.h>
4332c361f574f85fa47600d84900598e2efc99082eHuang Ying#include <linux/ratelimit.h>
4481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/vmalloc.h>
4567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/irq_work.h>
4667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/llist.h>
4767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/genalloc.h>
48d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h>
49d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/atomicio.h>
50d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h>
51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h>
5281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h>
53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
54d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h"
55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX	"GHES: "
57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE		65536
5967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESOURCE_PREALLOC_MAX_SIZE	65536
6067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
6167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
6267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
63152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* This is just an estimation for memory pool allocation */
64152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_AVG_SIZE	512
65152cef40a808d3034e383465b3f7d6783613e458Huang Ying
66152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHES_SIZE	4
67152cef40a808d3034e383465b3f7d6783613e458Huang Ying
68152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_IN_CACHE_MAX_NSEC	(10 * NSEC_PER_SEC)
69152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* Prevent too many caches are allocated because of RCU */
70152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_ALLOCED_MAX	(GHES_ESTATUS_CACHES_SIZE * 3 / 2)
71152cef40a808d3034e383465b3f7d6783613e458Huang Ying
72152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_LEN(estatus_len)			\
73152cef40a808d3034e383465b3f7d6783613e458Huang Ying	(sizeof(struct ghes_estatus_cache) + (estatus_len))
74152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_FROM_CACHE(estatus_cache)			\
75152cef40a808d3034e383465b3f7d6783613e458Huang Ying	((struct acpi_hest_generic_status *)			\
76152cef40a808d3034e383465b3f7d6783613e458Huang Ying	 ((struct ghes_estatus_cache *)(estatus_cache) + 1))
77152cef40a808d3034e383465b3f7d6783613e458Huang Ying
7867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_NODE_LEN(estatus_len)			\
7967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	(sizeof(struct ghes_estatus_node) + (estatus_len))
8067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_FROM_NODE(estatus_node)				\
8167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	((struct acpi_hest_generic_status *)				\
8267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 ((struct ghes_estatus_node *)(estatus_node) + 1))
83d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
84d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
8581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * One struct ghes is created for each generic hardware error source.
86d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
8781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler.
88d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during
90d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing.
91d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR		0x0001
9381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_EXITING		0x0002
94d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
95d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes {
96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_status *estatus;
98d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buffer_paddr;
99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned long flags;
10081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	union {
10181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		struct list_head list;
10281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		struct timer_list timer;
10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		unsigned int irq;
10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	};
105d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
106d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
10767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstruct ghes_estatus_node {
10867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct llist_node llnode;
10967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct acpi_hest_generic *generic;
11067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying};
11167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
112152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache {
113152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 estatus_len;
114152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_t count;
115152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic;
116152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long time_in;
117152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct rcu_head rcu;
118152cef40a808d3034e383465b3f7d6783613e458Huang Ying};
119152cef40a808d3034e383465b3f7d6783613e458Huang Ying
120b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingint ghes_disable;
121b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0);
122b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
12381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_panic_timeout	__read_mostly = 30;
12481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
125d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function,
12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one.  This is applied
12881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too.
129d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
13081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for
13181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing.
132d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
133d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci);
13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic LIST_HEAD(ghes_nmi);
1357ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex);
136d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
13881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * NMI may be triggered on any CPU, so ghes_nmi_lock is used for
13981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * mutual exclusion.
14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock);
14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information
14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer
14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so
14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that.
14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Two virtual pages are used, one for NMI context, the other for
15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * IRQ/PROCESS context
15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_PAGES		2
15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_NMI_PAGE(base)	(base)
15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_IRQ_PAGE(base)	((base) + PAGE_SIZE)
15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */
15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area;
16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/*
16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory
16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously.
16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */
16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
16767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying/*
16867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * printk is not safe in NMI context.  So in NMI handler, we allocate
16967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required memory from lock-less memory allocator
17067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * (ghes_estatus_pool), save estatus into it, put them into lock-less
17167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * list (ghes_estatus_llist), then delay printk into IRQ context via
17267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * irq_work (ghes_proc_irq_work).  ghes_estatus_size_request record
17367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required pool size by all NMI error source.
17467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */
17567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct gen_pool *ghes_estatus_pool;
17667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_estatus_pool_size_request;
17767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct llist_head ghes_estatus_llist;
17867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct irq_work ghes_proc_irq_work;
17967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
180152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
181152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic atomic_t ghes_estatus_cache_alloced;
182152cef40a808d3034e383465b3f7d6783613e458Huang Ying
18381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void)
18481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
18581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
18681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		VM_IOREMAP, VMALLOC_START, VMALLOC_END);
18781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!ghes_ioremap_area) {
18881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n");
18981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return -ENOMEM;
19081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
19181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
19281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
19381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
19481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
19581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void)
19681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
19781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	free_vm_area(ghes_ioremap_area);
19881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
19981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
20081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
20181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
20281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
20381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
20481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr);
20581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
20681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
20781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
20881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
20981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
21081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
21181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn)
21281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
21381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr;
21481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
21581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr);
21681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
21781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   pfn << PAGE_SHIFT, PAGE_KERNEL);
21881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
21981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return (void __iomem *)vaddr;
22081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
22181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
22281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr)
22381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
22481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
22581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
22681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
22781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base));
22881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
22981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	__flush_tlb_one(vaddr);
23081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
23181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
23281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr)
23381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
23481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long vaddr = (unsigned long __force)vaddr_ptr;
23581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void *base = ghes_ioremap_area->addr;
23681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
23781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base));
23881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unmap_kernel_range_noflush(vaddr, PAGE_SIZE);
23981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	__flush_tlb_one(vaddr);
24081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
24181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
24267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_init(void)
24367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
24467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
24567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (!ghes_estatus_pool)
24667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		return -ENOMEM;
24767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return 0;
24867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
24967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
25067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
25167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					      struct gen_pool_chunk *chunk,
25267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					      void *data)
25367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
25467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	free_page(chunk->start_addr);
25567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
25667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
25767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_exit(void)
25867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
25967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	gen_pool_for_each_chunk(ghes_estatus_pool,
26067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				ghes_estatus_pool_free_chunk_page, NULL);
26167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	gen_pool_destroy(ghes_estatus_pool);
26267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
26367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
26467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_expand(unsigned long len)
26567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
26667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long i, pages, size, addr;
26767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	int ret;
26867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
26967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_size_request += PAGE_ALIGN(len);
27067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	size = gen_pool_size(ghes_estatus_pool);
27167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (size >= ghes_estatus_pool_size_request)
27267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		return 0;
27367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
27467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	for (i = 0; i < pages; i++) {
27567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		addr = __get_free_page(GFP_KERNEL);
27667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (!addr)
27767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			return -ENOMEM;
27867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
27967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (ret)
28067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			return ret;
28167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
28267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
28367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return 0;
28467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
28567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
28667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_shrink(unsigned long len)
28767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
28867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
28967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
29067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
291d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic)
292d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned int error_block_length;
295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes)
299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return ERR_PTR(-ENOMEM);
300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->generic = generic;
301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_pre_map_gar(&generic->error_status_address);
302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_free;
304d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	error_block_length = generic->error_block_length;
305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
307d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Error status block length is too long: %u for "
308d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "generic hardware error source: %d.\n",
309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   error_block_length, generic->header.source_id);
310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		error_block_length = GHES_ESTATUS_MAX_SIZE;
311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus) {
314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENOMEM;
315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_unmap;
316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ghes;
319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap:
321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&generic->error_status_address);
322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free:
323d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes);
324d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ERR_PTR(rc);
325d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes)
328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes->estatus);
330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&ghes->generic->error_status_address);
331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum {
334ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_NO = 0x0,
335ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_CORRECTED = 0x1,
336ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_RECOVERABLE = 0x2,
337ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_PANIC = 0x3,
338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
340d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity)
341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (severity) {
343ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_INFORMATIONAL:
344ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_NO;
345ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_CORRECTED:
346ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_CORRECTED;
347ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_RECOVERABLE:
348ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_RECOVERABLE;
349ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_FATAL:
350ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
351d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
35225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi		/* Unknown, go panic */
353ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
35781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
35881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				  int from_phys)
359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
36081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	void __iomem *vaddr;
36181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long flags = 0;
36281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int in_nmi = in_nmi();
36381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u64 offset;
36481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	u32 trunk;
36581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
36681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	while (len > 0) {
36781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		offset = paddr - (paddr & PAGE_MASK);
36881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
36981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_lock(&ghes_ioremap_lock_nmi);
37081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT);
37181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
37281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_lock_irqsave(&ghes_ioremap_lock_irq, flags);
37381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT);
37481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
37581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = PAGE_SIZE - offset;
37681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		trunk = min(trunk, len);
37781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (from_phys)
37881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_fromio(buffer, vaddr + offset, trunk);
37981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		else
38081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			memcpy_toio(vaddr + offset, buffer, trunk);
38181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		len -= trunk;
38281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		paddr += trunk;
38381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		buffer += trunk;
38481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (in_nmi) {
38581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_nmi(vaddr);
38681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			raw_spin_unlock(&ghes_ioremap_lock_nmi);
38781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		} else {
38881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_iounmap_irq(vaddr);
38981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags);
39081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
39181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
392d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
393d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
394d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent)
395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
396d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *g = ghes->generic;
397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buf_paddr;
398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u32 len;
399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!silent && printk_ratelimit())
404d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			pr_warning(FW_WARN GHES_PFX
405d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n",
406d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   g->header.source_id);
407d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EIO;
408d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
409d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!buf_paddr)
410d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
411d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
41281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
41381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      sizeof(*ghes->estatus), 1);
414d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus->block_status)
415d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
416d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
417d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->buffer_paddr = buf_paddr;
418d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags |= GHES_TO_CLEAR;
419d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
420d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = -EIO;
421d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	len = apei_estatus_len(ghes->estatus);
422d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len < sizeof(*ghes->estatus))
423d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
424d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len > ghes->generic->error_block_length)
425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
426d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check_header(ghes->estatus))
427d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
42881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_copy_tofrom_phys(ghes->estatus + 1,
42981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      buf_paddr + sizeof(*ghes->estatus),
43081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			      len - sizeof(*ghes->estatus), 1);
431d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check(ghes->estatus))
432d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
433d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = 0;
434d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
435d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block:
43681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc && !silent && printk_ratelimit())
437d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
438d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Failed to read error status block!\n");
439d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
440d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
441d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
442d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes)
443d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
444d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus->block_status = 0;
445d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!(ghes->flags & GHES_TO_CLEAR))
446d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return;
447d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
448d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			      sizeof(ghes->estatus->block_status), 0);
449d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags &= ~GHES_TO_CLEAR;
450d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
451d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
45267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_do_proc(const struct acpi_hest_generic_status *estatus)
453d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
454ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	int sev, processed = 0;
455d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_data *gdata;
456d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
45767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	sev = ghes_severity(estatus->error_severity);
45867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	apei_estatus_for_each_section(estatus, gdata) {
459d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#ifdef CONFIG_X86_MCE
460d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
461d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 CPER_SEC_PLATFORM_MEM)) {
462d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			apei_mce_report_mem_error(
463ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying				sev == GHES_SEV_CORRECTED,
464d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				(struct cper_sec_mem_err *)(gdata+1));
465d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			processed = 1;
466d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		}
467d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif
468d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
46932c361f574f85fa47600d84900598e2efc99082eHuang Ying}
470d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
47167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void __ghes_print_estatus(const char *pfx,
47267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 const struct acpi_hest_generic *generic,
47367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 const struct acpi_hest_generic_status *estatus)
47432c361f574f85fa47600d84900598e2efc99082eHuang Ying{
47532c361f574f85fa47600d84900598e2efc99082eHuang Ying	if (pfx == NULL) {
47667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (ghes_severity(estatus->error_severity) <=
47732c361f574f85fa47600d84900598e2efc99082eHuang Ying		    GHES_SEV_CORRECTED)
47832c361f574f85fa47600d84900598e2efc99082eHuang Ying			pfx = KERN_WARNING HW_ERR;
47932c361f574f85fa47600d84900598e2efc99082eHuang Ying		else
48032c361f574f85fa47600d84900598e2efc99082eHuang Ying			pfx = KERN_ERR HW_ERR;
48132c361f574f85fa47600d84900598e2efc99082eHuang Ying	}
4825588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
48367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	       pfx, generic->header.source_id);
48467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	apei_estatus_print(pfx, estatus);
4855588340d46a484da53bbce8136184d9c7fbc259cHuang Ying}
4865588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
487152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_print_estatus(const char *pfx,
488152cef40a808d3034e383465b3f7d6783613e458Huang Ying			      const struct acpi_hest_generic *generic,
489152cef40a808d3034e383465b3f7d6783613e458Huang Ying			      const struct acpi_hest_generic_status *estatus)
4905588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{
4915588340d46a484da53bbce8136184d9c7fbc259cHuang Ying	/* Not more than 2 messages every 5 seconds */
49267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
49367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
49467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct ratelimit_state *ratelimit;
4955588340d46a484da53bbce8136184d9c7fbc259cHuang Ying
49667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
49767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ratelimit = &ratelimit_corrected;
49867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	else
49967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ratelimit = &ratelimit_uncorrected;
500152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (__ratelimit(ratelimit)) {
50167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		__ghes_print_estatus(pfx, generic, estatus);
502152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return 1;
503152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
504152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return 0;
505152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
506152cef40a808d3034e383465b3f7d6783613e458Huang Ying
507152cef40a808d3034e383465b3f7d6783613e458Huang Ying/*
508152cef40a808d3034e383465b3f7d6783613e458Huang Ying * GHES error status reporting throttle, to report more kinds of
509152cef40a808d3034e383465b3f7d6783613e458Huang Ying * errors, instead of just most frequently occurred errors.
510152cef40a808d3034e383465b3f7d6783613e458Huang Ying */
511152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
512152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
513152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len;
514152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int i, cached = 0;
515152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long now;
516152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
517152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *cache_estatus;
518152cef40a808d3034e383465b3f7d6783613e458Huang Ying
519152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = apei_estatus_len(estatus);
520152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_lock();
521152cef40a808d3034e383465b3f7d6783613e458Huang Ying	for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
522152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache = rcu_dereference(ghes_estatus_caches[i]);
523152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (cache == NULL)
524152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
525152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (len != cache->estatus_len)
526152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
527152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
528152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (memcmp(estatus, cache_estatus, len))
529152cef40a808d3034e383465b3f7d6783613e458Huang Ying			continue;
530152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_inc(&cache->count);
531152cef40a808d3034e383465b3f7d6783613e458Huang Ying		now = sched_clock();
532152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
533152cef40a808d3034e383465b3f7d6783613e458Huang Ying			cached = 1;
534152cef40a808d3034e383465b3f7d6783613e458Huang Ying		break;
535152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
536152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_unlock();
537152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return cached;
538152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
539152cef40a808d3034e383465b3f7d6783613e458Huang Ying
540152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic struct ghes_estatus_cache *ghes_estatus_cache_alloc(
541152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic,
542152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *estatus)
543152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
544152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int alloced;
545152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len, cache_len;
546152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
547152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *cache_estatus;
548152cef40a808d3034e383465b3f7d6783613e458Huang Ying
549152cef40a808d3034e383465b3f7d6783613e458Huang Ying	alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
550152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
551152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_dec(&ghes_estatus_cache_alloced);
552152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return NULL;
553152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
554152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = apei_estatus_len(estatus);
555152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache_len = GHES_ESTATUS_CACHE_LEN(len);
556152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
557152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (!cache) {
558152cef40a808d3034e383465b3f7d6783613e458Huang Ying		atomic_dec(&ghes_estatus_cache_alloced);
559152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return NULL;
560152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
561152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
562152cef40a808d3034e383465b3f7d6783613e458Huang Ying	memcpy(cache_estatus, estatus, len);
563152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->estatus_len = len;
564152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_set(&cache->count, 0);
565152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->generic = generic;
566152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache->time_in = sched_clock();
567152cef40a808d3034e383465b3f7d6783613e458Huang Ying	return cache;
568152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
569152cef40a808d3034e383465b3f7d6783613e458Huang Ying
570152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
571152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
572152cef40a808d3034e383465b3f7d6783613e458Huang Ying	u32 len;
573152cef40a808d3034e383465b3f7d6783613e458Huang Ying
574152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
575152cef40a808d3034e383465b3f7d6783613e458Huang Ying	len = GHES_ESTATUS_CACHE_LEN(len);
576152cef40a808d3034e383465b3f7d6783613e458Huang Ying	gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
577152cef40a808d3034e383465b3f7d6783613e458Huang Ying	atomic_dec(&ghes_estatus_cache_alloced);
578152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
579152cef40a808d3034e383465b3f7d6783613e458Huang Ying
580152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_rcu_free(struct rcu_head *head)
581152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
582152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache;
583152cef40a808d3034e383465b3f7d6783613e458Huang Ying
584152cef40a808d3034e383465b3f7d6783613e458Huang Ying	cache = container_of(head, struct ghes_estatus_cache, rcu);
585152cef40a808d3034e383465b3f7d6783613e458Huang Ying	ghes_estatus_cache_free(cache);
586152cef40a808d3034e383465b3f7d6783613e458Huang Ying}
587152cef40a808d3034e383465b3f7d6783613e458Huang Ying
588152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_add(
589152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic,
590152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic_status *estatus)
591152cef40a808d3034e383465b3f7d6783613e458Huang Ying{
592152cef40a808d3034e383465b3f7d6783613e458Huang Ying	int i, slot = -1, count;
593152cef40a808d3034e383465b3f7d6783613e458Huang Ying	unsigned long long now, duration, period, max_period = 0;
594152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
595152cef40a808d3034e383465b3f7d6783613e458Huang Ying
596152cef40a808d3034e383465b3f7d6783613e458Huang Ying	new_cache = ghes_estatus_cache_alloc(generic, estatus);
597152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (new_cache == NULL)
598152cef40a808d3034e383465b3f7d6783613e458Huang Ying		return;
599152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_lock();
600152cef40a808d3034e383465b3f7d6783613e458Huang Ying	now = sched_clock();
601152cef40a808d3034e383465b3f7d6783613e458Huang Ying	for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
602152cef40a808d3034e383465b3f7d6783613e458Huang Ying		cache = rcu_dereference(ghes_estatus_caches[i]);
603152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (cache == NULL) {
604152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
605152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = NULL;
606152cef40a808d3034e383465b3f7d6783613e458Huang Ying			break;
607152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
608152cef40a808d3034e383465b3f7d6783613e458Huang Ying		duration = now - cache->time_in;
609152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
610152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
611152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = cache;
612152cef40a808d3034e383465b3f7d6783613e458Huang Ying			break;
613152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
614152cef40a808d3034e383465b3f7d6783613e458Huang Ying		count = atomic_read(&cache->count);
615152cef40a808d3034e383465b3f7d6783613e458Huang Ying		period = duration / (count + 1);
616152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (period > max_period) {
617152cef40a808d3034e383465b3f7d6783613e458Huang Ying			max_period = period;
618152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot = i;
619152cef40a808d3034e383465b3f7d6783613e458Huang Ying			slot_cache = cache;
620152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
621152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
622152cef40a808d3034e383465b3f7d6783613e458Huang Ying	/* new_cache must be put into array after its contents are written */
623152cef40a808d3034e383465b3f7d6783613e458Huang Ying	smp_wmb();
624152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
625152cef40a808d3034e383465b3f7d6783613e458Huang Ying				  slot_cache, new_cache) == slot_cache) {
626152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (slot_cache)
627152cef40a808d3034e383465b3f7d6783613e458Huang Ying			call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
628152cef40a808d3034e383465b3f7d6783613e458Huang Ying	} else
629152cef40a808d3034e383465b3f7d6783613e458Huang Ying		ghes_estatus_cache_free(new_cache);
630152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rcu_read_unlock();
631d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
632d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
633d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes)
634d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
635d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
636d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
637d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_read_estatus(ghes, 0);
638d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
639d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto out;
640152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (!ghes_estatus_cached(ghes->estatus)) {
641152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
642152cef40a808d3034e383465b3f7d6783613e458Huang Ying			ghes_estatus_cache_add(ghes->generic, ghes->estatus);
643152cef40a808d3034e383465b3f7d6783613e458Huang Ying	}
64467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_do_proc(ghes->estatus);
645d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout:
646d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_clear_estatus(ghes);
647d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
648d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
649d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
65081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes)
65181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
65281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct acpi_hest_generic *g = ghes->generic;
65381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	unsigned long expire;
65481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
65581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!g->notify.poll_interval) {
65681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n",
65781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   g->header.source_id);
65881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return;
65981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
66081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	expire = jiffies + msecs_to_jiffies(g->notify.poll_interval);
66181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->timer.expires = round_jiffies_relative(expire);
66281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	add_timer(&ghes->timer);
66381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
66481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
66581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data)
66681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
66781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = (void *)data;
66881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
66981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_proc(ghes);
67081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (!(ghes->flags & GHES_EXITING))
67181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
67281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
67381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
67481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data)
67581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
67681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes = data;
67781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
67881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
67981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_proc(ghes);
68081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
68181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return IRQ_NONE;
68281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
68381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return IRQ_HANDLED;
68481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
68581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
686d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this,
687d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				  unsigned long event, void *data)
688d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
689d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
690d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ret = NOTIFY_DONE;
691d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
692d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_lock();
693d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_rcu(ghes, &ghes_sci, list) {
694d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!ghes_proc(ghes))
695d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			ret = NOTIFY_OK;
696d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
697d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_unlock();
698d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
699d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ret;
700d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
701d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
70267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_proc_in_irq(struct irq_work *irq_work)
70367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
70467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct llist_node *llnode, *next, *tail = NULL;
70567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct ghes_estatus_node *estatus_node;
706152cef40a808d3034e383465b3f7d6783613e458Huang Ying	struct acpi_hest_generic *generic;
70767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	struct acpi_hest_generic_status *estatus;
70867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	u32 len, node_len;
70967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
71067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	/*
71167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 * Because the time order of estatus in list is reversed,
71267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 * revert it back to proper order.
71367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	 */
71467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	llnode = llist_del_all(&ghes_estatus_llist);
71567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	while (llnode) {
71667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		next = llnode->next;
71767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		llnode->next = tail;
71867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		tail = llnode;
71967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		llnode = next;
72067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
72167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	llnode = tail;
72267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	while (llnode) {
72367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		next = llnode->next;
72467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus_node = llist_entry(llnode, struct ghes_estatus_node,
72567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying					   llnode);
72667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
72767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = apei_estatus_len(estatus);
72867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
72967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ghes_do_proc(estatus);
730152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (!ghes_estatus_cached(estatus)) {
731152cef40a808d3034e383465b3f7d6783613e458Huang Ying			generic = estatus_node->generic;
732152cef40a808d3034e383465b3f7d6783613e458Huang Ying			if (ghes_print_estatus(NULL, generic, estatus))
733152cef40a808d3034e383465b3f7d6783613e458Huang Ying				ghes_estatus_cache_add(generic, estatus);
734152cef40a808d3034e383465b3f7d6783613e458Huang Ying		}
73567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
73667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			      node_len);
73767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		llnode = next;
73867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	}
73967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
74067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
74181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_notify_nmi(struct notifier_block *this,
74281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				  unsigned long cmd, void *data)
74381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{
74481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	struct ghes *ghes, *ghes_global = NULL;
74581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int sev, sev_global = -1;
74681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int ret = NOTIFY_DONE;
74781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
74881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (cmd != DIE_NMI)
74981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		return ret;
75081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
75181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_lock(&ghes_nmi_lock);
75281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
75381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (ghes_read_estatus(ghes, 1)) {
75481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_clear_estatus(ghes);
75581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
75681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
75781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		sev = ghes_severity(ghes->estatus->error_severity);
75881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (sev > sev_global) {
75981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			sev_global = sev;
76081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			ghes_global = ghes;
76181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
76281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ret = NOTIFY_STOP;
76381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
76481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
76581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (ret == NOTIFY_DONE)
76681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto out;
76781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
76881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (sev_global >= GHES_SEV_PANIC) {
76981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		oops_begin();
77067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		__ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic,
77167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				     ghes_global->estatus);
77281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* reboot to log the error! */
77381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (panic_timeout == 0)
77481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			panic_timeout = ghes_panic_timeout;
77581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		panic("Fatal hardware error!");
77681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
77781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
77881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
77967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
78067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		u32 len, node_len;
78167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		struct ghes_estatus_node *estatus_node;
78267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		struct acpi_hest_generic_status *estatus;
78367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
78481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (!(ghes->flags & GHES_TO_CLEAR))
78581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			continue;
78667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
787152cef40a808d3034e383465b3f7d6783613e458Huang Ying		if (ghes_estatus_cached(ghes->estatus))
788152cef40a808d3034e383465b3f7d6783613e458Huang Ying			goto next;
78967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		/* Save estatus for further processing in IRQ context */
79067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = apei_estatus_len(ghes->estatus);
79167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		node_len = GHES_ESTATUS_NODE_LEN(len);
79267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
79367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying						      node_len);
79467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		if (estatus_node) {
79567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			estatus_node->generic = ghes->generic;
79667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
79767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			memcpy(estatus, ghes->estatus, len);
79867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			llist_add(&estatus_node->llnode, &ghes_estatus_llist);
79967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		}
800152cef40a808d3034e383465b3f7d6783613e458Huang Yingnext:
80167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
80281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_clear_estatus(ghes);
80381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	}
80467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
80567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	irq_work_queue(&ghes_proc_irq_work);
80667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif
80781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
80881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout:
80981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	raw_spin_unlock(&ghes_nmi_lock);
81081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return ret;
81181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}
81281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
813d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = {
814d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	.notifier_call = ghes_notify_sci,
815d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
816d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
81781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct notifier_block ghes_notifier_nmi = {
81881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	.notifier_call = ghes_notify_nmi,
81981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying};
82081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
82167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_esource_prealloc_size(
82267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	const struct acpi_hest_generic *generic)
82367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{
82467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long block_length, prealloc_records, prealloc_size;
82567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
82667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	block_length = min_t(unsigned long, generic->error_block_length,
82767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			     GHES_ESTATUS_MAX_SIZE);
82867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	prealloc_records = max_t(unsigned long,
82967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying				 generic->records_to_preallocate, 1);
83067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	prealloc_size = min_t(unsigned long, block_length * prealloc_records,
83167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying			      GHES_ESOURCE_PREALLOC_MAX_SIZE);
83267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
83367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	return prealloc_size;
83467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}
83567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
8367ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev)
837d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
838d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
839d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes = NULL;
84067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long len;
8417ad6e9435596f692ff65f399da12816c94960185Huang Ying	int rc = -EINVAL;
842d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
8431dd6b20e368765223c31569d364219785b24700bJin Dongming	generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
844d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!generic->enabled)
8457ad6e9435596f692ff65f399da12816c94960185Huang Ying		return -ENODEV;
846d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
84781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
84881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
84981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
85081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
85181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
85281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
85381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_LOCAL:
85481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
855d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
856d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
85781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
85881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
85981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->notify.type, generic->header.source_id);
86081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
861d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
86281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
86381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = -EIO;
86481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (generic->error_block_length <
86581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	    sizeof(struct acpi_hest_generic_status)) {
86681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n",
86781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			   generic->error_block_length,
868d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
869d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
870d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
871d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = ghes_new(generic);
872d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (IS_ERR(ghes)) {
873d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = PTR_ERR(ghes);
874d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes = NULL;
875d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
876d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
87781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	switch (generic->notify.type) {
87881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
87981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.function = ghes_poll_func;
88081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes->timer.data = (unsigned long)ghes;
88181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		init_timer_deferrable(&ghes->timer);
88281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		ghes_add_timer(ghes);
88381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
88481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
88581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/* External interrupt vector is GSI */
88681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) {
88781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
88881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
88981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			goto err;
89081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
89181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (request_irq(ghes->irq, ghes_irq_func,
89281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying				0, "GHES IRQ", ghes)) {
89381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
89481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			       generic->header.source_id);
89581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			goto err;
89681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		}
89781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
89881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_SCI:
8997ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
900d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (list_empty(&ghes_sci))
901d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			register_acpi_hed_notifier(&ghes_notifier_sci);
902d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_add_rcu(&ghes->list, &ghes_sci);
9037ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
90481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
90581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
90667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = ghes_esource_prealloc_size(generic);
90767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ghes_estatus_pool_expand(len);
90881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_lock(&ghes_list_mutex);
90981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (list_empty(&ghes_nmi))
91081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			register_die_notifier(&ghes_notifier_nmi);
91181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		list_add_rcu(&ghes->list, &ghes_nmi);
91281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_unlock(&ghes_list_mutex);
91381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
91481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	default:
91581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		BUG();
916d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
9177ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, ghes);
918d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
919d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
920d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr:
9217ad6e9435596f692ff65f399da12816c94960185Huang Ying	if (ghes) {
922d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
9237ad6e9435596f692ff65f399da12816c94960185Huang Ying		kfree(ghes);
9247ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
925d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
926d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
927d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9287ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev)
929d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
9307ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct ghes *ghes;
9317ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct acpi_hest_generic *generic;
93267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	unsigned long len;
933d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9347ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes = platform_get_drvdata(ghes_dev);
9357ad6e9435596f692ff65f399da12816c94960185Huang Ying	generic = ghes->generic;
9367ad6e9435596f692ff65f399da12816c94960185Huang Ying
93781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes->flags |= GHES_EXITING;
9387ad6e9435596f692ff65f399da12816c94960185Huang Ying	switch (generic->notify.type) {
93981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
94081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		del_timer_sync(&ghes->timer);
94181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
94281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
94381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		free_irq(ghes->irq, ghes);
94481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
9457ad6e9435596f692ff65f399da12816c94960185Huang Ying	case ACPI_HEST_NOTIFY_SCI:
9467ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
9477ad6e9435596f692ff65f399da12816c94960185Huang Ying		list_del_rcu(&ghes->list);
9487ad6e9435596f692ff65f399da12816c94960185Huang Ying		if (list_empty(&ghes_sci))
9497ad6e9435596f692ff65f399da12816c94960185Huang Ying			unregister_acpi_hed_notifier(&ghes_notifier_sci);
9507ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
9517ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
95281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	case ACPI_HEST_NOTIFY_NMI:
95381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_lock(&ghes_list_mutex);
95481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		list_del_rcu(&ghes->list);
95581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		if (list_empty(&ghes_nmi))
95681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying			unregister_die_notifier(&ghes_notifier_nmi);
95781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		mutex_unlock(&ghes_list_mutex);
95881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		/*
95981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 * To synchronize with NMI handler, ghes can only be
96081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 * freed after NMI handler finishes.
96181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		 */
96281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		synchronize_rcu();
96367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		len = ghes_esource_prealloc_size(generic);
96467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		ghes_estatus_pool_shrink(len);
96581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		break;
9667ad6e9435596f692ff65f399da12816c94960185Huang Ying	default:
9677ad6e9435596f692ff65f399da12816c94960185Huang Ying		BUG();
9687ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
9697ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
970d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9717ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes_fini(ghes);
9727ad6e9435596f692ff65f399da12816c94960185Huang Ying	kfree(ghes);
973d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9747ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, NULL);
9757ad6e9435596f692ff65f399da12816c94960185Huang Ying
9767ad6e9435596f692ff65f399da12816c94960185Huang Ying	return 0;
977d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
978d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
9797ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = {
9807ad6e9435596f692ff65f399da12816c94960185Huang Ying	.driver		= {
9817ad6e9435596f692ff65f399da12816c94960185Huang Ying		.name	= "GHES",
9827ad6e9435596f692ff65f399da12816c94960185Huang Ying		.owner	= THIS_MODULE,
9837ad6e9435596f692ff65f399da12816c94960185Huang Ying	},
9847ad6e9435596f692ff65f399da12816c94960185Huang Ying	.probe		= ghes_probe,
9857ad6e9435596f692ff65f399da12816c94960185Huang Ying	.remove		= ghes_remove,
9867ad6e9435596f692ff65f399da12816c94960185Huang Ying};
9877ad6e9435596f692ff65f399da12816c94960185Huang Ying
988d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void)
989d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
99081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	int rc;
99181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
992d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (acpi_disabled)
993d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENODEV;
994d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
995d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_disable) {
996d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX "HEST is not enabled!\n");
997d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EINVAL;
998d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
999d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1000b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	if (ghes_disable) {
1001b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		pr_info(GHES_PFX "GHES is not enabled!\n");
1002b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying		return -EINVAL;
1003b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying	}
1004b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying
100567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
100667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
100781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	rc = ghes_ioremap_init();
100881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
100981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err;
101081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
101167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	rc = ghes_estatus_pool_init();
101281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	if (rc)
101381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying		goto err_ioremap_exit;
101481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying
1015152cef40a808d3034e383465b3f7d6783613e458Huang Ying	rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
1016152cef40a808d3034e383465b3f7d6783613e458Huang Ying				      GHES_ESTATUS_CACHE_ALLOCED_MAX);
1017152cef40a808d3034e383465b3f7d6783613e458Huang Ying	if (rc)
1018152cef40a808d3034e383465b3f7d6783613e458Huang Ying		goto err_pool_exit;
1019152cef40a808d3034e383465b3f7d6783613e458Huang Ying
102067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	rc = platform_driver_register(&ghes_platform_driver);
102167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	if (rc)
102267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying		goto err_pool_exit;
102367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying
10249fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	rc = apei_osc_setup();
10259fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	if (rc == 0 && osc_sb_apei_support_acked)
10269fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
10279fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc == 0 && !osc_sb_apei_support_acked)
10289fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
10299fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else if (rc && osc_sb_apei_support_acked)
10309fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
10319fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying	else
10329fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying		pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
10339fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying
103481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return 0;
103567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingerr_pool_exit:
103667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_exit();
103781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit:
103881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
103981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr:
104081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	return rc;
1041d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1042d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1043d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void)
1044d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
10457ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_driver_unregister(&ghes_platform_driver);
104667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying	ghes_estatus_pool_exit();
104781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying	ghes_ioremap_exit();
1048d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
1049d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1050d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init);
1051d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit);
1052d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
1053d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying");
1054d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
1055d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL");
10567ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES");
1057