ghes.c revision 7ad6e9435596f692ff65f399da12816c94960185
1d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
2d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * APEI Generic Hardware Error Source support
3d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
4d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Generic Hardware Error Source provides a way to report platform
5d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * hardware errors (such as that from chipset). It works in so called
6d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * "Firmware First" mode, that is, hardware errors are reported to
7d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * firmware firstly, then reported to Linux by firmware. This way,
8d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * some non-standard hardware error registers or non-standard hardware
9d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * link can be checked by firmware to produce more hardware error
10d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * information for Linux.
11d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
12d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * For more information about Generic Hardware Error Source, please
13d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * refer to ACPI Specification version 4.0, section 17.3.2.6
14d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
15d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Now, only SCI notification type and memory errors are
16d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * supported. More notification type and hardware error type will be
17d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * added later.
18d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
19d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Copyright 2010 Intel Corp.
20d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *   Author: Huang Ying <ying.huang@intel.com>
21d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
22d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is free software; you can redistribute it and/or
23d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * modify it under the terms of the GNU General Public License version
24d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 2 as published by the Free Software Foundation;
25d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
26d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is distributed in the hope that it will be useful,
27d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * but WITHOUT ANY WARRANTY; without even the implied warranty of
28d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * GNU General Public License for more details.
30d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
31d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * You should have received a copy of the GNU General Public License
32d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * along with this program; if not, write to the Free Software
33d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
34d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
35d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
36d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kernel.h>
37d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/module.h>
38d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/init.h>
39d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/acpi.h>
40d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/io.h>
41d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/interrupt.h>
42d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/cper.h>
43d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kdebug.h>
447ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/platform_device.h>
457ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/mutex.h>
46d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h>
47d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/atomicio.h>
48d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h>
49d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h>
50d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h"
52d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX	"GHES: "
54d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE		65536
56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * One struct ghes is created for each generic hardware error
59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * source.
60d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
61d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
62d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * handler. Handler for one generic hardware error source is only
63d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * triggered after the previous one is done. So handler can uses
64d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * struct ghes without locking.
65d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
66d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during
67d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing.
68d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
69d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR		0x0001
70d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
71d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes {
72d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
73d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_status *estatus;
74d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct list_head list;
75d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buffer_paddr;
76d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned long flags;
77d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
78d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
79d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
80d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Error source lists, one list for each notification method. The
81d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * members in lists are struct ghes.
82d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
83d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * The list members are only added in HEST parsing and deleted during
84d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * module_exit, that is, single-threaded. So no lock is needed for
85d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * that.
86d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
87d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * But the mutual exclusion is needed between members adding/deleting
88d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * and timer/IRQ/SCI/NMI handler, which may traverse the list. RCU is
89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * used for that.
90d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
91d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci);
927ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex);
93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
94d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic)
95d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned int error_block_length;
98d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
100d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
101d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes)
102d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return ERR_PTR(-ENOMEM);
103d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->generic = generic;
104d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	INIT_LIST_HEAD(&ghes->list);
105d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_pre_map_gar(&generic->error_status_address);
106d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
107d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_free;
108d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	error_block_length = generic->error_block_length;
109d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
110d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
111d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Error status block length is too long: %u for "
112d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "generic hardware error source: %d.\n",
113d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   error_block_length, generic->header.source_id);
114d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		error_block_length = GHES_ESTATUS_MAX_SIZE;
115d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
116d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
117d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus) {
118d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENOMEM;
119d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_unmap;
120d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
121d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
122d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ghes;
123d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
124d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap:
125d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&generic->error_status_address);
126d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free:
127d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes);
128d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ERR_PTR(rc);
129d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
130d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
131d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes)
132d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
133d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes->estatus);
134d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&ghes->generic->error_status_address);
135d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
136d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
137d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum {
138ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_NO = 0x0,
139ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_CORRECTED = 0x1,
140ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_RECOVERABLE = 0x2,
141ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	GHES_SEV_PANIC = 0x3,
142d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
143d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
144d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity)
145d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
146d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (severity) {
147ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_INFORMATIONAL:
148ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_NO;
149ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_CORRECTED:
150ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_CORRECTED;
151ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_RECOVERABLE:
152ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_RECOVERABLE;
153ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	case CPER_SEV_FATAL:
154ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
155d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
156d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		/* Unkown, go panic */
157ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying		return GHES_SEV_PANIC;
158d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
159d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
160d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
161d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* SCI handler run in work queue, so ioremap can be used here */
162d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
163d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 int from_phys)
164d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
165d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	void *vaddr;
166d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
167d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	vaddr = ioremap_cache(paddr, len);
168d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!vaddr)
169d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOMEM;
170d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (from_phys)
171d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		memcpy(buffer, vaddr, len);
172d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	else
173d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		memcpy(vaddr, buffer, len);
174d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	iounmap(vaddr);
175d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
176d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
177d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
178d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
179d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent)
180d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
181d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *g = ghes->generic;
182d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buf_paddr;
183d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u32 len;
184d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
185d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
186d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
187d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
188d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!silent && printk_ratelimit())
189d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			pr_warning(FW_WARN GHES_PFX
190d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n",
191d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   g->header.source_id);
192d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EIO;
193d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
194d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!buf_paddr)
195d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
196d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
197d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
198d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   sizeof(*ghes->estatus), 1);
199d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
200d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return rc;
201d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus->block_status)
202d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
203d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
204d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->buffer_paddr = buf_paddr;
205d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags |= GHES_TO_CLEAR;
206d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
207d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = -EIO;
208d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	len = apei_estatus_len(ghes->estatus);
209d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len < sizeof(*ghes->estatus))
210d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
211d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len > ghes->generic->error_block_length)
212d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
213d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check_header(ghes->estatus))
214d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
215d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_copy_tofrom_phys(ghes->estatus + 1,
216d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   buf_paddr + sizeof(*ghes->estatus),
217d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   len - sizeof(*ghes->estatus), 1);
218d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
219d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return rc;
220d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check(ghes->estatus))
221d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
222d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = 0;
223d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
224d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block:
225d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc && !silent)
226d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
227d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Failed to read error status block!\n");
228d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
229d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
230d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
231d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes)
232d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
233d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus->block_status = 0;
234d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!(ghes->flags & GHES_TO_CLEAR))
235d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return;
236d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
237d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			      sizeof(ghes->estatus->block_status), 0);
238d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags &= ~GHES_TO_CLEAR;
239d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
240d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
241d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_do_proc(struct ghes *ghes)
242d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
243ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	int sev, processed = 0;
244d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_data *gdata;
245d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
246ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying	sev = ghes_severity(ghes->estatus->error_severity);
247d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	apei_estatus_for_each_section(ghes->estatus, gdata) {
248d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#ifdef CONFIG_X86_MCE
249d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
250d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 CPER_SEC_PLATFORM_MEM)) {
251d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			apei_mce_report_mem_error(
252ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying				sev == GHES_SEV_CORRECTED,
253d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				(struct cper_sec_mem_err *)(gdata+1));
254d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			processed = 1;
255d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		}
256d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif
257d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
258d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
259d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!processed && printk_ratelimit())
260d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(GHES_PFX
261d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		"Unknown error record from generic hardware error source: %d\n",
262d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   ghes->generic->header.source_id);
263d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
264d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
265d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes)
266d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
267d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
268d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
269d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_read_estatus(ghes, 0);
270d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
271d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto out;
272d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_do_proc(ghes);
273d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
274d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout:
275d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_clear_estatus(ghes);
276d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
277d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
278d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
279d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this,
280d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				  unsigned long event, void *data)
281d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
282d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
283d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ret = NOTIFY_DONE;
284d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
285d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_lock();
286d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_rcu(ghes, &ghes_sci, list) {
287d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!ghes_proc(ghes))
288d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			ret = NOTIFY_OK;
289d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
290d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_unlock();
291d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
292d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ret;
293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = {
296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	.notifier_call = ghes_notify_sci,
297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
2997ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev)
300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes = NULL;
3037ad6e9435596f692ff65f399da12816c94960185Huang Ying	int rc = -EINVAL;
304d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
3057ad6e9435596f692ff65f399da12816c94960185Huang Ying	generic = ghes_dev->dev.platform_data;
306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!generic->enabled)
3077ad6e9435596f692ff65f399da12816c94960185Huang Ying		return -ENODEV;
308d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (generic->error_block_length <
310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	    sizeof(struct acpi_hest_generic_status)) {
311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_BUG GHES_PFX
312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Invalid error block length: %u for generic hardware error source: %d\n",
313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->error_block_length,
314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (generic->records_to_preallocate == 0) {
318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_BUG GHES_PFX
319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Invalid records to preallocate: %u for generic hardware error source: %d\n",
320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->records_to_preallocate,
321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
323d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
324d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = ghes_new(generic);
325d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (IS_ERR(ghes)) {
326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = PTR_ERR(ghes);
327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes = NULL;
328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err;
329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
3307ad6e9435596f692ff65f399da12816c94960185Huang Ying	if (generic->notify.type == ACPI_HEST_NOTIFY_SCI) {
3317ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (list_empty(&ghes_sci))
333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			register_acpi_hed_notifier(&ghes_notifier_sci);
334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_add_rcu(&ghes->list, &ghes_sci);
3357ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
3367ad6e9435596f692ff65f399da12816c94960185Huang Ying	} else {
3377ad6e9435596f692ff65f399da12816c94960185Huang Ying		unsigned char *notify = NULL;
3387ad6e9435596f692ff65f399da12816c94960185Huang Ying
3397ad6e9435596f692ff65f399da12816c94960185Huang Ying		switch (generic->notify.type) {
3407ad6e9435596f692ff65f399da12816c94960185Huang Ying		case ACPI_HEST_NOTIFY_POLLED:
3417ad6e9435596f692ff65f399da12816c94960185Huang Ying			notify = "POLL";
3427ad6e9435596f692ff65f399da12816c94960185Huang Ying			break;
3437ad6e9435596f692ff65f399da12816c94960185Huang Ying		case ACPI_HEST_NOTIFY_EXTERNAL:
3447ad6e9435596f692ff65f399da12816c94960185Huang Ying		case ACPI_HEST_NOTIFY_LOCAL:
3457ad6e9435596f692ff65f399da12816c94960185Huang Ying			notify = "IRQ";
3467ad6e9435596f692ff65f399da12816c94960185Huang Ying			break;
3477ad6e9435596f692ff65f399da12816c94960185Huang Ying		case ACPI_HEST_NOTIFY_NMI:
3487ad6e9435596f692ff65f399da12816c94960185Huang Ying			notify = "NMI";
3497ad6e9435596f692ff65f399da12816c94960185Huang Ying			break;
3507ad6e9435596f692ff65f399da12816c94960185Huang Ying		}
3517ad6e9435596f692ff65f399da12816c94960185Huang Ying		if (notify) {
3527ad6e9435596f692ff65f399da12816c94960185Huang Ying			pr_warning(GHES_PFX
3537ad6e9435596f692ff65f399da12816c94960185Huang Ying"Generic hardware error source: %d notified via %s is not supported!\n",
3547ad6e9435596f692ff65f399da12816c94960185Huang Ying				   generic->header.source_id, notify);
3557ad6e9435596f692ff65f399da12816c94960185Huang Ying		} else {
3567ad6e9435596f692ff65f399da12816c94960185Huang Ying			pr_warning(FW_WARN GHES_PFX
3577ad6e9435596f692ff65f399da12816c94960185Huang Ying"Unknown notification type: %u for generic hardware error source: %d\n",
3587ad6e9435596f692ff65f399da12816c94960185Huang Ying			generic->notify.type, generic->header.source_id);
3597ad6e9435596f692ff65f399da12816c94960185Huang Ying		}
3607ad6e9435596f692ff65f399da12816c94960185Huang Ying		rc = -ENODEV;
3617ad6e9435596f692ff65f399da12816c94960185Huang Ying		goto err;
362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
3637ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, ghes);
364d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
365d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
366d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr:
3677ad6e9435596f692ff65f399da12816c94960185Huang Ying	if (ghes) {
368d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
3697ad6e9435596f692ff65f399da12816c94960185Huang Ying		kfree(ghes);
3707ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
371d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
372d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
373d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
3747ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev)
375d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
3767ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct ghes *ghes;
3777ad6e9435596f692ff65f399da12816c94960185Huang Ying	struct acpi_hest_generic *generic;
378d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
3797ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes = platform_get_drvdata(ghes_dev);
3807ad6e9435596f692ff65f399da12816c94960185Huang Ying	generic = ghes->generic;
3817ad6e9435596f692ff65f399da12816c94960185Huang Ying
3827ad6e9435596f692ff65f399da12816c94960185Huang Ying	switch (generic->notify.type) {
3837ad6e9435596f692ff65f399da12816c94960185Huang Ying	case ACPI_HEST_NOTIFY_SCI:
3847ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_lock(&ghes_list_mutex);
3857ad6e9435596f692ff65f399da12816c94960185Huang Ying		list_del_rcu(&ghes->list);
3867ad6e9435596f692ff65f399da12816c94960185Huang Ying		if (list_empty(&ghes_sci))
3877ad6e9435596f692ff65f399da12816c94960185Huang Ying			unregister_acpi_hed_notifier(&ghes_notifier_sci);
3887ad6e9435596f692ff65f399da12816c94960185Huang Ying		mutex_unlock(&ghes_list_mutex);
3897ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
3907ad6e9435596f692ff65f399da12816c94960185Huang Ying	default:
3917ad6e9435596f692ff65f399da12816c94960185Huang Ying		BUG();
3927ad6e9435596f692ff65f399da12816c94960185Huang Ying		break;
3937ad6e9435596f692ff65f399da12816c94960185Huang Ying	}
394d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	synchronize_rcu();
3967ad6e9435596f692ff65f399da12816c94960185Huang Ying	ghes_fini(ghes);
3977ad6e9435596f692ff65f399da12816c94960185Huang Ying	kfree(ghes);
398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
3997ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_set_drvdata(ghes_dev, NULL);
4007ad6e9435596f692ff65f399da12816c94960185Huang Ying
4017ad6e9435596f692ff65f399da12816c94960185Huang Ying	return 0;
402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
4047ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = {
4057ad6e9435596f692ff65f399da12816c94960185Huang Ying	.driver		= {
4067ad6e9435596f692ff65f399da12816c94960185Huang Ying		.name	= "GHES",
4077ad6e9435596f692ff65f399da12816c94960185Huang Ying		.owner	= THIS_MODULE,
4087ad6e9435596f692ff65f399da12816c94960185Huang Ying	},
4097ad6e9435596f692ff65f399da12816c94960185Huang Ying	.probe		= ghes_probe,
4107ad6e9435596f692ff65f399da12816c94960185Huang Ying	.remove		= ghes_remove,
4117ad6e9435596f692ff65f399da12816c94960185Huang Ying};
4127ad6e9435596f692ff65f399da12816c94960185Huang Ying
413d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void)
414d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
415d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (acpi_disabled)
416d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENODEV;
417d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
418d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_disable) {
419d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX "HEST is not enabled!\n");
420d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EINVAL;
421d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
422d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
4237ad6e9435596f692ff65f399da12816c94960185Huang Ying	return platform_driver_register(&ghes_platform_driver);
424d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
426d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void)
427d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
4287ad6e9435596f692ff65f399da12816c94960185Huang Ying	platform_driver_unregister(&ghes_platform_driver);
429d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
430d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
431d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init);
432d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit);
433d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
434d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying");
435d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
436d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL");
4377ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES");
438