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