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