ghes.c revision d334a49113a4a33109fd24e46073280ecd1bea0d
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>
44d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h>
45d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/atomicio.h>
46d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h>
47d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h>
48d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
49d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h"
50d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX	"GHES: "
52d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE		65536
54d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * One struct ghes is created for each generic hardware error
57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * source.
58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
60d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * handler. Handler for one generic hardware error source is only
61d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * triggered after the previous one is done. So handler can uses
62d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * struct ghes without locking.
63d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
64d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during
65d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing.
66d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
67d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR		0x0001
68d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
69d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes {
70d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
71d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_status *estatus;
72d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct list_head list;
73d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buffer_paddr;
74d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned long flags;
75d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
76d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
77d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/*
78d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Error source lists, one list for each notification method. The
79d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * members in lists are struct ghes.
80d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
81d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * The list members are only added in HEST parsing and deleted during
82d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * module_exit, that is, single-threaded. So no lock is needed for
83d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * that.
84d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying *
85d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * But the mutual exclusion is needed between members adding/deleting
86d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * and timer/IRQ/SCI/NMI handler, which may traverse the list. RCU is
87d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * used for that.
88d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */
89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci);
90d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
91d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic)
92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
94d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	unsigned int error_block_length;
95d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
98d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes)
99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return ERR_PTR(-ENOMEM);
100d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->generic = generic;
101d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	INIT_LIST_HEAD(&ghes->list);
102d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_pre_map_gar(&generic->error_status_address);
103d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
104d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_free;
105d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	error_block_length = generic->error_block_length;
106d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
107d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
108d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Error status block length is too long: %u for "
109d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "generic hardware error source: %d.\n",
110d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   error_block_length, generic->header.source_id);
111d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		error_block_length = GHES_ESTATUS_MAX_SIZE;
112d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
113d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
114d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus) {
115d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENOMEM;
116d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_unmap;
117d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
118d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
119d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ghes;
120d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
121d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap:
122d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&generic->error_status_address);
123d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free:
124d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes);
125d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ERR_PTR(rc);
126d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
127d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
128d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes)
129d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
130d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	kfree(ghes->estatus);
131d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	acpi_post_unmap_gar(&ghes->generic->error_status_address);
132d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
133d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
134d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum {
135d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	GHES_SER_NO = 0x0,
136d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	GHES_SER_CORRECTED = 0x1,
137d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	GHES_SER_RECOVERABLE = 0x2,
138d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	GHES_SER_PANIC = 0x3,
139d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
140d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
141d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity)
142d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
143d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (severity) {
144d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case CPER_SER_INFORMATIONAL:
145d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return GHES_SER_NO;
146d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case CPER_SER_CORRECTED:
147d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return GHES_SER_CORRECTED;
148d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case CPER_SER_RECOVERABLE:
149d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return GHES_SER_RECOVERABLE;
150d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case CPER_SER_FATAL:
151d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return GHES_SER_PANIC;
152d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
153d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		/* Unkown, go panic */
154d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return GHES_SER_PANIC;
155d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
156d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
157d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
158d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* SCI handler run in work queue, so ioremap can be used here */
159d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
160d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 int from_phys)
161d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
162d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	void *vaddr;
163d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
164d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	vaddr = ioremap_cache(paddr, len);
165d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!vaddr)
166d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOMEM;
167d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (from_phys)
168d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		memcpy(buffer, vaddr, len);
169d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	else
170d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		memcpy(vaddr, buffer, len);
171d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	iounmap(vaddr);
172d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
173d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
174d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
175d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
176d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent)
177d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
178d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *g = ghes->generic;
179d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u64 buf_paddr;
180d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	u32 len;
181d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
182d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
183d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
184d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
185d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!silent && printk_ratelimit())
186d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			pr_warning(FW_WARN GHES_PFX
187d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n",
188d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   g->header.source_id);
189d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EIO;
190d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
191d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!buf_paddr)
192d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
193d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
194d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
195d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   sizeof(*ghes->estatus), 1);
196d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
197d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return rc;
198d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!ghes->estatus->block_status)
199d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENOENT;
200d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
201d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->buffer_paddr = buf_paddr;
202d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags |= GHES_TO_CLEAR;
203d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
204d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = -EIO;
205d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	len = apei_estatus_len(ghes->estatus);
206d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len < sizeof(*ghes->estatus))
207d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
208d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (len > ghes->generic->error_block_length)
209d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
210d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check_header(ghes->estatus))
211d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
212d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_copy_tofrom_phys(ghes->estatus + 1,
213d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   buf_paddr + sizeof(*ghes->estatus),
214d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				   len - sizeof(*ghes->estatus), 1);
215d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
216d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return rc;
217d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (apei_estatus_check(ghes->estatus))
218d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_read_block;
219d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = 0;
220d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
221d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block:
222d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc && !silent)
223d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
224d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   "Failed to read error status block!\n");
225d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
226d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
227d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
228d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes)
229d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
230d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->estatus->block_status = 0;
231d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!(ghes->flags & GHES_TO_CLEAR))
232d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return;
233d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
234d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			      sizeof(ghes->estatus->block_status), 0);
235d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes->flags &= ~GHES_TO_CLEAR;
236d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
237d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
238d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_do_proc(struct ghes *ghes)
239d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
240d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ser, processed = 0;
241d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic_data *gdata;
242d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
243d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ser = ghes_severity(ghes->estatus->error_severity);
244d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	apei_estatus_for_each_section(ghes->estatus, gdata) {
245d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#ifdef CONFIG_X86_MCE
246d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
247d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				 CPER_SEC_PLATFORM_MEM)) {
248d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			apei_mce_report_mem_error(
249d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				ser == GHES_SER_CORRECTED,
250d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				(struct cper_sec_mem_err *)(gdata+1));
251d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			processed = 1;
252d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		}
253d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif
254d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
255d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
256d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!processed && printk_ratelimit())
257d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(GHES_PFX
258d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		"Unknown error record from generic hardware error source: %d\n",
259d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   ghes->generic->header.source_id);
260d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
261d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
262d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes)
263d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
264d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
265d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
266d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = ghes_read_estatus(ghes, 0);
267d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc)
268d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto out;
269d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_do_proc(ghes);
270d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
271d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout:
272d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_clear_estatus(ghes);
273d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
274d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
275d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
276d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this,
277d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying				  unsigned long event, void *data)
278d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
279d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes;
280d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int ret = NOTIFY_DONE;
281d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
282d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_lock();
283d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_rcu(ghes, &ghes_sci, list) {
284d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (!ghes_proc(ghes))
285d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			ret = NOTIFY_OK;
286d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
287d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rcu_read_unlock();
288d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
289d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return ret;
290d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
291d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
292d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = {
293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	.notifier_call = ghes_notify_sci,
294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying};
295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct acpi_hest_generic *generic;
299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes = NULL;
300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc = 0;
301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return 0;
304d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	generic = (struct acpi_hest_generic *)hest_hdr;
306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!generic->enabled)
307d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return 0;
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	}
330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	switch (generic->notify.type) {
331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case ACPI_HEST_NOTIFY_POLLED:
332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(GHES_PFX
333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Generic hardware error source: %d notified via POLL is not supported!\n",
334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		break;
336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case ACPI_HEST_NOTIFY_EXTERNAL:
337d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case ACPI_HEST_NOTIFY_LOCAL:
338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(GHES_PFX
339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Generic hardware error source: %d notified via IRQ is not supported!\n",
340d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		break;
342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case ACPI_HEST_NOTIFY_SCI:
343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		if (list_empty(&ghes_sci))
344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			register_acpi_hed_notifier(&ghes_notifier_sci);
345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_add_rcu(&ghes->list, &ghes_sci);
346d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		break;
347d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	case ACPI_HEST_NOTIFY_NMI:
348d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(GHES_PFX
349d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Generic hardware error source: %d notified via NMI is not supported!\n",
350d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->header.source_id);
351d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		break;
352d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	default:
353d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_warning(FW_WARN GHES_PFX
354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	"Unknown notification type: %u for generic hardware error source: %d\n",
355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			   generic->notify.type, generic->header.source_id);
356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		break;
357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
358d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
360d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr:
361d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (ghes)
362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
363d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
364d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
365d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
366d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_cleanup(void)
367d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
368d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	struct ghes *ghes, *nghes;
369d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
370d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (!list_empty(&ghes_sci))
371d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		unregister_acpi_hed_notifier(&ghes_notifier_sci);
372d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
373d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	synchronize_rcu();
374d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
375d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	list_for_each_entry_safe(ghes, nghes, &ghes_sci, list) {
376d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		list_del(&ghes->list);
377d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		ghes_fini(ghes);
378d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		kfree(ghes);
379d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
380d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
381d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
382d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void)
383d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
384d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	int rc;
385d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
386d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (acpi_disabled)
387d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -ENODEV;
388d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
389d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (hest_disable) {
390d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX "HEST is not enabled!\n");
391d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		return -EINVAL;
392d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
393d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
394d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	rc = apei_hest_parse(hest_ghes_parse, NULL);
395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (rc) {
396d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_err(GHES_PFX
397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		"Error during parsing HEST generic hardware error sources.\n");
398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_cleanup;
399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	if (list_empty(&ghes_sci)) {
402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		pr_info(GHES_PFX
403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying			"No functional generic hardware error sources.\n");
404d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		rc = -ENODEV;
405d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		goto err_cleanup;
406d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	}
407d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
408d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	pr_info(GHES_PFX
409d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying		"Generic Hardware Error Source support is initialized.\n");
410d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
411d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return 0;
412d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_cleanup:
413d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_cleanup();
414d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	return rc;
415d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
416d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
417d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void)
418d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{
419d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying	ghes_cleanup();
420d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}
421d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
422d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init);
423d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit);
424d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying
425d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying");
426d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
427d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL");
428