1d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 2d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * APEI Generic Hardware Error Source support 3d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 4d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Generic Hardware Error Source provides a way to report platform 5d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * hardware errors (such as that from chipset). It works in so called 6d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * "Firmware First" mode, that is, hardware errors are reported to 7d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * firmware firstly, then reported to Linux by firmware. This way, 8d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * some non-standard hardware error registers or non-standard hardware 9d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * link can be checked by firmware to produce more hardware error 10d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * information for Linux. 11d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 12d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * For more information about Generic Hardware Error Source, please 13d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * refer to ACPI Specification version 4.0, section 17.3.2.6 14d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 1567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * Copyright 2010,2011 Intel Corp. 16d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Author: Huang Ying <ying.huang@intel.com> 17d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 18d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is free software; you can redistribute it and/or 19d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * modify it under the terms of the GNU General Public License version 20d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 2 as published by the Free Software Foundation; 21d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 22d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * This program is distributed in the hope that it will be useful, 23d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * but WITHOUT ANY WARRANTY; without even the implied warranty of 24d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * GNU General Public License for more details. 26d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 27d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * You should have received a copy of the GNU General Public License 28d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * along with this program; if not, write to the Free Software 29d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 30d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 31d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 32d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kernel.h> 33d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/module.h> 34d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/init.h> 35d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/acpi.h> 36700130b41f4ee54520ac2ef2f7f1d072789711a4Myron Stowe#include <linux/acpi_io.h> 37d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/io.h> 38d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/interrupt.h> 3981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/timer.h> 40d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/cper.h> 41d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kdebug.h> 427ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/platform_device.h> 437ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/mutex.h> 4432c361f574f85fa47600d84900598e2efc99082eHuang Ying#include <linux/ratelimit.h> 4581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/vmalloc.h> 4667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/irq_work.h> 4767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/llist.h> 4867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/genalloc.h> 49a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/pci.h> 50a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/aer.h> 51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h> 52d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h> 53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h> 5481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h> 559c48f1c629ecfa114850c03f875c6691003214deDon Zickus#include <asm/nmi.h> 56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h" 58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX "GHES: " 60d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 61d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE 65536 6267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 6367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 6467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 6567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 66152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* This is just an estimation for memory pool allocation */ 67152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_AVG_SIZE 512 68152cef40a808d3034e383465b3f7d6783613e458Huang Ying 69152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHES_SIZE 4 70152cef40a808d3034e383465b3f7d6783613e458Huang Ying 7170cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL 72152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* Prevent too many caches are allocated because of RCU */ 73152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) 74152cef40a808d3034e383465b3f7d6783613e458Huang Ying 75152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ 76152cef40a808d3034e383465b3f7d6783613e458Huang Ying (sizeof(struct ghes_estatus_cache) + (estatus_len)) 77152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ 78152cef40a808d3034e383465b3f7d6783613e458Huang Ying ((struct acpi_hest_generic_status *) \ 79152cef40a808d3034e383465b3f7d6783613e458Huang Ying ((struct ghes_estatus_cache *)(estatus_cache) + 1)) 80152cef40a808d3034e383465b3f7d6783613e458Huang Ying 8167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_NODE_LEN(estatus_len) \ 8267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying (sizeof(struct ghes_estatus_node) + (estatus_len)) 8367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_FROM_NODE(estatus_node) \ 8467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ((struct acpi_hest_generic_status *) \ 8567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ((struct ghes_estatus_node *)(estatus_node) + 1)) 86d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 87d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 8881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * One struct ghes is created for each generic hardware error source. 89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI 9081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler. 91d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during 93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing. 94d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 95d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR 0x0001 9681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_EXITING 0x0002 97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 98d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes { 99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 100d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic_status *estatus; 101d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buffer_paddr; 102d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long flags; 10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying union { 10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct list_head list; 10581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct timer_list timer; 10681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned int irq; 10781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying }; 108d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 109d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 11067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstruct ghes_estatus_node { 11167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct llist_node llnode; 11267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct acpi_hest_generic *generic; 11367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}; 11467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 115152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache { 116152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 estatus_len; 117152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_t count; 118152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic; 119152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long time_in; 120152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct rcu_head rcu; 121152cef40a808d3034e383465b3f7d6783613e458Huang Ying}; 122152cef40a808d3034e383465b3f7d6783613e458Huang Ying 12390ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool ghes_disable; 124b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0); 125b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_panic_timeout __read_mostly = 30; 12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 128d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 12981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function, 13081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one. This is applied 13181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too. 132d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 13381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for 13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing. 135d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 136d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci); 13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic LIST_HEAD(ghes_nmi); 1387ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex); 139d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * NMI may be triggered on any CPU, so ghes_nmi_lock is used for 14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * mutual exclusion. 14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock); 14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information 14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer 14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so 15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that. 15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Two virtual pages are used, one for NMI context, the other for 15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * IRQ/PROCESS context 15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_PAGES 2 15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_NMI_PAGE(base) (base) 15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) 16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */ 16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area; 16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory 16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously. 16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 16781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); 16881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq); 16981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 17067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying/* 17167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * printk is not safe in NMI context. So in NMI handler, we allocate 17267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required memory from lock-less memory allocator 17367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * (ghes_estatus_pool), save estatus into it, put them into lock-less 17467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * list (ghes_estatus_llist), then delay printk into IRQ context via 17567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record 17667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required pool size by all NMI error source. 17767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */ 17867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct gen_pool *ghes_estatus_pool; 17967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_estatus_pool_size_request; 18067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct llist_head ghes_estatus_llist; 18167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct irq_work ghes_proc_irq_work; 18267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 183152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; 184152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic atomic_t ghes_estatus_cache_alloced; 185152cef40a808d3034e383465b3f7d6783613e458Huang Ying 18681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void) 18781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 18881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, 18981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying VM_IOREMAP, VMALLOC_START, VMALLOC_END); 19081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!ghes_ioremap_area) { 19181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); 19281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return -ENOMEM; 19381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 19481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 19581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 19681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 19781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 19881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void) 19981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 20081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_vm_area(ghes_ioremap_area); 20181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 20281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 20381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) 20481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 20581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 20681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 20781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); 20881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 20981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 21081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 21181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 21281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 21381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 21481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn) 21581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 21681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 21781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 21881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); 21981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 22081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 22181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 22281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 22381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 22481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 22581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr) 22681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 22781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 22881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 22981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 23081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); 23181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 23281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying __flush_tlb_one(vaddr); 23381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 23481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 23581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr) 23681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 23781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 23881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 23981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 24081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); 24181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 24281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying __flush_tlb_one(vaddr); 24381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 24481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 24567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_init(void) 24667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 24767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1); 24867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (!ghes_estatus_pool) 24967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return -ENOMEM; 25067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 25167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 25267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 25367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool, 25467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct gen_pool_chunk *chunk, 25567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying void *data) 25667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 25767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying free_page(chunk->start_addr); 25867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 25967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 26067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_exit(void) 26167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 26267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_for_each_chunk(ghes_estatus_pool, 26367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_free_chunk_page, NULL); 26467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_destroy(ghes_estatus_pool); 26567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 26667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 26767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_expand(unsigned long len) 26867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 26967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long i, pages, size, addr; 27067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying int ret; 27167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 27267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_size_request += PAGE_ALIGN(len); 27367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying size = gen_pool_size(ghes_estatus_pool); 27467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (size >= ghes_estatus_pool_size_request) 27567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 27667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE; 27767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying for (i = 0; i < pages; i++) { 27867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying addr = __get_free_page(GFP_KERNEL); 27967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (!addr) 28067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return -ENOMEM; 28167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1); 28267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ret) 28367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return ret; 28467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 28567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 28667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 28767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 28867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 28967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_shrink(unsigned long len) 29067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 29167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_size_request -= PAGE_ALIGN(len); 29267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 29367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic) 295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned int error_block_length; 298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = kzalloc(sizeof(*ghes), GFP_KERNEL); 301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes) 302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(-ENOMEM); 303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->generic = generic; 304890b3d0ea620c942718dd29d601a7aaa4362280bHuang Ying rc = apei_map_generic_address(&generic->error_status_address); 305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_free; 307d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = generic->error_block_length; 308d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (error_block_length > GHES_ESTATUS_MAX_SIZE) { 309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Error status block length is too long: %u for " 311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "generic hardware error source: %d.\n", 312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length, generic->header.source_id); 313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = GHES_ESTATUS_MAX_SIZE; 314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus = kmalloc(error_block_length, GFP_KERNEL); 316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus) { 317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -ENOMEM; 318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_unmap; 319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ghes; 322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 323d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap: 324890b3d0ea620c942718dd29d601a7aaa4362280bHuang Ying apei_unmap_generic_address(&generic->error_status_address); 325d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free: 326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes); 327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(rc); 328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes) 331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes->estatus); 333890b3d0ea620c942718dd29d601a7aaa4362280bHuang Ying apei_unmap_generic_address(&ghes->generic->error_status_address); 334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum { 337ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_NO = 0x0, 338ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_CORRECTED = 0x1, 339ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_RECOVERABLE = 0x2, 340ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_PANIC = 0x3, 341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity) 344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying switch (severity) { 346ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_INFORMATIONAL: 347ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_NO; 348ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_CORRECTED: 349ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_CORRECTED; 350ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_RECOVERABLE: 351ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_RECOVERABLE; 352ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_FATAL: 353ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying default: 35525985edcedea6396277003854657b5f3cb31a628Lucas De Marchi /* Unknown, go panic */ 356ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 358d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 36081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, 36181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int from_phys) 362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 36381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void __iomem *vaddr; 36481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long flags = 0; 36581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int in_nmi = in_nmi(); 36681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u64 offset; 36781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u32 trunk; 36881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 36981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying while (len > 0) { 37081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying offset = paddr - (paddr & PAGE_MASK); 37181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 37281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_ioremap_lock_nmi); 37381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); 37481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 37581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); 37681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); 37781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 37881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = PAGE_SIZE - offset; 37981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = min(trunk, len); 38081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (from_phys) 38181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_fromio(buffer, vaddr + offset, trunk); 38281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying else 38381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_toio(vaddr + offset, buffer, trunk); 38481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len -= trunk; 38581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying paddr += trunk; 38681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buffer += trunk; 38781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 38881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_nmi(vaddr); 38981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_ioremap_lock_nmi); 39081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 39181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_irq(vaddr); 39281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); 39381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 39481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 396d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent) 398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *g = ghes->generic; 400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buf_paddr; 401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u32 len; 402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 404700130b41f4ee54520ac2ef2f7f1d072789711a4Myron Stowe rc = apei_read(&buf_paddr, &g->error_status_address); 405d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) { 406d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!silent && printk_ratelimit()) 407d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 408d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n", 409d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying g->header.source_id); 410d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EIO; 411d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 412d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!buf_paddr) 413d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 414d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 41581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, 41681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(*ghes->estatus), 1); 417d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus->block_status) 418d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 419d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 420d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->buffer_paddr = buf_paddr; 421d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags |= GHES_TO_CLEAR; 422d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 423d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -EIO; 424d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying len = apei_estatus_len(ghes->estatus); 425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len < sizeof(*ghes->estatus)) 426d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 427d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len > ghes->generic->error_block_length) 428d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 429d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (apei_estatus_check_header(ghes->estatus)) 430d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 43181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus + 1, 43281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buf_paddr + sizeof(*ghes->estatus), 43381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len - sizeof(*ghes->estatus), 1); 434d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (apei_estatus_check(ghes->estatus)) 435d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 436d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = 0; 437d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 438d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block: 43981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc && !silent && printk_ratelimit()) 440d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 441d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Failed to read error status block!\n"); 442d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 443d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 444d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 445d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes) 446d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 447d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus->block_status = 0; 448d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 449d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return; 450d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr, 451d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying sizeof(ghes->estatus->block_status), 0); 452d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags &= ~GHES_TO_CLEAR; 453d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 454d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 45567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_do_proc(const struct acpi_hest_generic_status *estatus) 456d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 457ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying int sev, sec_sev; 458d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic_data *gdata; 459d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 46067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying sev = ghes_severity(estatus->error_severity); 46167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying apei_estatus_for_each_section(estatus, gdata) { 462ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying sec_sev = ghes_severity(gdata->error_severity); 463d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 464d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying CPER_SEC_PLATFORM_MEM)) { 465ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying struct cper_sec_mem_err *mem_err; 466ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying mem_err = (struct cper_sec_mem_err *)(gdata+1); 467ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying#ifdef CONFIG_X86_MCE 468ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, 469ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying mem_err); 470d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif 471ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE 472ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying if (sev == GHES_SEV_RECOVERABLE && 473ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying sec_sev == GHES_SEV_RECOVERABLE && 474ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { 475ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying unsigned long pfn; 476ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying pfn = mem_err->physical_addr >> PAGE_SHIFT; 477ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying memory_failure_queue(pfn, 0, 0); 478ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying } 479ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying#endif 480ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying } 481a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#ifdef CONFIG_ACPI_APEI_PCIEAER 482a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 483a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying CPER_SEC_PCIE)) { 484a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying struct cper_sec_pcie *pcie_err; 485a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err = (struct cper_sec_pcie *)(gdata+1); 486a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying if (sev == GHES_SEV_RECOVERABLE && 487a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying sec_sev == GHES_SEV_RECOVERABLE && 488a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && 489a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { 490a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying unsigned int devfn; 491a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying int aer_severity; 492a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying devfn = PCI_DEVFN(pcie_err->device_id.device, 493a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->device_id.function); 494a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying aer_severity = cper_severity_to_aer(sev); 495a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying aer_recover_queue(pcie_err->device_id.segment, 496a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->device_id.bus, 497a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying devfn, aer_severity); 498a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying } 499a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying 500a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying } 501a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#endif 502d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 50332c361f574f85fa47600d84900598e2efc99082eHuang Ying} 504d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 50567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void __ghes_print_estatus(const char *pfx, 50667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic *generic, 50767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic_status *estatus) 50832c361f574f85fa47600d84900598e2efc99082eHuang Ying{ 5095ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying static atomic_t seqno; 5105ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying unsigned int curr_seqno; 5115ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying char pfx_seq[64]; 5125ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying 51332c361f574f85fa47600d84900598e2efc99082eHuang Ying if (pfx == NULL) { 51467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ghes_severity(estatus->error_severity) <= 51532c361f574f85fa47600d84900598e2efc99082eHuang Ying GHES_SEV_CORRECTED) 5165ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying pfx = KERN_WARNING; 51732c361f574f85fa47600d84900598e2efc99082eHuang Ying else 5185ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying pfx = KERN_ERR; 51932c361f574f85fa47600d84900598e2efc99082eHuang Ying } 5205ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying curr_seqno = atomic_inc_return(&seqno); 5215ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); 5225588340d46a484da53bbce8136184d9c7fbc259cHuang Ying printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", 5235ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying pfx_seq, generic->header.source_id); 5245ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying apei_estatus_print(pfx_seq, estatus); 5255588340d46a484da53bbce8136184d9c7fbc259cHuang Ying} 5265588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 527152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_print_estatus(const char *pfx, 528152cef40a808d3034e383465b3f7d6783613e458Huang Ying const struct acpi_hest_generic *generic, 529152cef40a808d3034e383465b3f7d6783613e458Huang Ying const struct acpi_hest_generic_status *estatus) 5305588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{ 5315588340d46a484da53bbce8136184d9c7fbc259cHuang Ying /* Not more than 2 messages every 5 seconds */ 53267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); 53367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); 53467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ratelimit_state *ratelimit; 5355588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 53667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED) 53767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ratelimit = &ratelimit_corrected; 53867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying else 53967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ratelimit = &ratelimit_uncorrected; 540152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (__ratelimit(ratelimit)) { 54167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying __ghes_print_estatus(pfx, generic, estatus); 542152cef40a808d3034e383465b3f7d6783613e458Huang Ying return 1; 543152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 544152cef40a808d3034e383465b3f7d6783613e458Huang Ying return 0; 545152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 546152cef40a808d3034e383465b3f7d6783613e458Huang Ying 547152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* 548152cef40a808d3034e383465b3f7d6783613e458Huang Ying * GHES error status reporting throttle, to report more kinds of 549152cef40a808d3034e383465b3f7d6783613e458Huang Ying * errors, instead of just most frequently occurred errors. 550152cef40a808d3034e383465b3f7d6783613e458Huang Ying */ 551152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) 552152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 553152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len; 554152cef40a808d3034e383465b3f7d6783613e458Huang Ying int i, cached = 0; 555152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long now; 556152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 557152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *cache_estatus; 558152cef40a808d3034e383465b3f7d6783613e458Huang Ying 559152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = apei_estatus_len(estatus); 560152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_lock(); 561152cef40a808d3034e383465b3f7d6783613e458Huang Ying for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { 562152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = rcu_dereference(ghes_estatus_caches[i]); 563152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (cache == NULL) 564152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 565152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (len != cache->estatus_len) 566152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 567152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); 568152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (memcmp(estatus, cache_estatus, len)) 569152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 570152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_inc(&cache->count); 571152cef40a808d3034e383465b3f7d6783613e458Huang Ying now = sched_clock(); 572152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) 573152cef40a808d3034e383465b3f7d6783613e458Huang Ying cached = 1; 574152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 575152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 576152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_unlock(); 577152cef40a808d3034e383465b3f7d6783613e458Huang Ying return cached; 578152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 579152cef40a808d3034e383465b3f7d6783613e458Huang Ying 580152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic struct ghes_estatus_cache *ghes_estatus_cache_alloc( 581152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic, 582152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *estatus) 583152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 584152cef40a808d3034e383465b3f7d6783613e458Huang Ying int alloced; 585152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len, cache_len; 586152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 587152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *cache_estatus; 588152cef40a808d3034e383465b3f7d6783613e458Huang Ying 589152cef40a808d3034e383465b3f7d6783613e458Huang Ying alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); 590152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { 591152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 592152cef40a808d3034e383465b3f7d6783613e458Huang Ying return NULL; 593152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 594152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = apei_estatus_len(estatus); 595152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_len = GHES_ESTATUS_CACHE_LEN(len); 596152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); 597152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!cache) { 598152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 599152cef40a808d3034e383465b3f7d6783613e458Huang Ying return NULL; 600152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 601152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); 602152cef40a808d3034e383465b3f7d6783613e458Huang Ying memcpy(cache_estatus, estatus, len); 603152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->estatus_len = len; 604152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_set(&cache->count, 0); 605152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->generic = generic; 606152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->time_in = sched_clock(); 607152cef40a808d3034e383465b3f7d6783613e458Huang Ying return cache; 608152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 609152cef40a808d3034e383465b3f7d6783613e458Huang Ying 610152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_free(struct ghes_estatus_cache *cache) 611152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 612152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len; 613152cef40a808d3034e383465b3f7d6783613e458Huang Ying 614152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); 615152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = GHES_ESTATUS_CACHE_LEN(len); 616152cef40a808d3034e383465b3f7d6783613e458Huang Ying gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); 617152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 618152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 619152cef40a808d3034e383465b3f7d6783613e458Huang Ying 620152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_rcu_free(struct rcu_head *head) 621152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 622152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 623152cef40a808d3034e383465b3f7d6783613e458Huang Ying 624152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = container_of(head, struct ghes_estatus_cache, rcu); 625152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_free(cache); 626152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 627152cef40a808d3034e383465b3f7d6783613e458Huang Ying 628152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_add( 629152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic, 630152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *estatus) 631152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 632152cef40a808d3034e383465b3f7d6783613e458Huang Ying int i, slot = -1, count; 633152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long now, duration, period, max_period = 0; 634152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache; 635152cef40a808d3034e383465b3f7d6783613e458Huang Ying 636152cef40a808d3034e383465b3f7d6783613e458Huang Ying new_cache = ghes_estatus_cache_alloc(generic, estatus); 637152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (new_cache == NULL) 638152cef40a808d3034e383465b3f7d6783613e458Huang Ying return; 639152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_lock(); 640152cef40a808d3034e383465b3f7d6783613e458Huang Ying now = sched_clock(); 641152cef40a808d3034e383465b3f7d6783613e458Huang Ying for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { 642152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = rcu_dereference(ghes_estatus_caches[i]); 643152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (cache == NULL) { 644152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 645152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = NULL; 646152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 647152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 648152cef40a808d3034e383465b3f7d6783613e458Huang Ying duration = now - cache->time_in; 649152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) { 650152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 651152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = cache; 652152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 653152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 654152cef40a808d3034e383465b3f7d6783613e458Huang Ying count = atomic_read(&cache->count); 65570cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown period = duration; 65670cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown do_div(period, (count + 1)); 657152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (period > max_period) { 658152cef40a808d3034e383465b3f7d6783613e458Huang Ying max_period = period; 659152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 660152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = cache; 661152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 662152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 663152cef40a808d3034e383465b3f7d6783613e458Huang Ying /* new_cache must be put into array after its contents are written */ 664152cef40a808d3034e383465b3f7d6783613e458Huang Ying smp_wmb(); 665152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (slot != -1 && cmpxchg(ghes_estatus_caches + slot, 666152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache, new_cache) == slot_cache) { 667152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (slot_cache) 668152cef40a808d3034e383465b3f7d6783613e458Huang Ying call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free); 669152cef40a808d3034e383465b3f7d6783613e458Huang Ying } else 670152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_free(new_cache); 671152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_unlock(); 672d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 673d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 674d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes) 675d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 676d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 677d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 678d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = ghes_read_estatus(ghes, 0); 679d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 680d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto out; 681152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!ghes_estatus_cached(ghes->estatus)) { 682152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus)) 683152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_add(ghes->generic, ghes->estatus); 684152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 68567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_do_proc(ghes->estatus); 686d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout: 687d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_clear_estatus(ghes); 688d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 689d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 690d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 69181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes) 69281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 69381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct acpi_hest_generic *g = ghes->generic; 69481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long expire; 69581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 69681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!g->notify.poll_interval) { 69781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n", 69881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying g->header.source_id); 69981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return; 70081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 70181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying expire = jiffies + msecs_to_jiffies(g->notify.poll_interval); 70281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.expires = round_jiffies_relative(expire); 70381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying add_timer(&ghes->timer); 70481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 70581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 70681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data) 70781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 70881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = (void *)data; 70981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 71081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_proc(ghes); 71181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_EXITING)) 71281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 71381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 71481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 71581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data) 71681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 71781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = data; 71881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 71981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 72081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_proc(ghes); 72181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 72281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_NONE; 72381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 72481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_HANDLED; 72581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 72681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 727d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this, 728d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long event, void *data) 729d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 730d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 731d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int ret = NOTIFY_DONE; 732d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 733d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_lock(); 734d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_for_each_entry_rcu(ghes, &ghes_sci, list) { 735d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes_proc(ghes)) 736d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ret = NOTIFY_OK; 737d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 738d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_unlock(); 739d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 740d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ret; 741d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 742d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 74346d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic struct llist_node *llist_nodes_reverse(struct llist_node *llnode) 74446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{ 74546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct llist_node *next, *tail = NULL; 74646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 74746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying while (llnode) { 74846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying next = llnode->next; 74946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode->next = tail; 75046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying tail = llnode; 75146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = next; 75246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying } 75346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 75446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying return tail; 75546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying} 75646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 75767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_proc_in_irq(struct irq_work *irq_work) 75867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 75946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct llist_node *llnode, *next; 76067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ghes_estatus_node *estatus_node; 761152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic; 76267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct acpi_hest_generic_status *estatus; 76367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying u32 len, node_len; 76467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 76546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_del_all(&ghes_estatus_llist); 76667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying /* 76767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * Because the time order of estatus in list is reversed, 76867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * revert it back to proper order. 76967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */ 77046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_nodes_reverse(llnode); 77167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying while (llnode) { 77267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying next = llnode->next; 77367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node = llist_entry(llnode, struct ghes_estatus_node, 77467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode); 77567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 77667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = apei_estatus_len(estatus); 77767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 77867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_do_proc(estatus); 779152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!ghes_estatus_cached(estatus)) { 780152cef40a808d3034e383465b3f7d6783613e458Huang Ying generic = estatus_node->generic; 781152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_print_estatus(NULL, generic, estatus)) 782152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_add(generic, estatus); 783152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 78467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, 78567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len); 78667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode = next; 78767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 78867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 78967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 79046d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic void ghes_print_queued_estatus(void) 79146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{ 79246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct llist_node *llnode; 79346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct ghes_estatus_node *estatus_node; 79446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct acpi_hest_generic *generic; 79546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct acpi_hest_generic_status *estatus; 79646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying u32 len, node_len; 79746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 79846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_del_all(&ghes_estatus_llist); 79946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying /* 80046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying * Because the time order of estatus in list is reversed, 80146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying * revert it back to proper order. 80246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying */ 80346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_nodes_reverse(llnode); 80446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying while (llnode) { 80546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying estatus_node = llist_entry(llnode, struct ghes_estatus_node, 80646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode); 80746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 80846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying len = apei_estatus_len(estatus); 80946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 81046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying generic = estatus_node->generic; 81146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying ghes_print_estatus(NULL, generic, estatus); 81246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llnode->next; 81346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying } 81446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying} 81546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 8169c48f1c629ecfa114850c03f875c6691003214deDon Zickusstatic int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) 81781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 81881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes, *ghes_global = NULL; 81981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int sev, sev_global = -1; 8209c48f1c629ecfa114850c03f875c6691003214deDon Zickus int ret = NMI_DONE; 82181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 82281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_nmi_lock); 82381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 82481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (ghes_read_estatus(ghes, 1)) { 82581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 82681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 82781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 82881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev = ghes_severity(ghes->estatus->error_severity); 82981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev > sev_global) { 83081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev_global = sev; 83181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_global = ghes; 83281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 8339c48f1c629ecfa114850c03f875c6691003214deDon Zickus ret = NMI_HANDLED; 83481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 83581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 8369c48f1c629ecfa114850c03f875c6691003214deDon Zickus if (ret == NMI_DONE) 83781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto out; 83881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 83981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev_global >= GHES_SEV_PANIC) { 84081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying oops_begin(); 84146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying ghes_print_queued_estatus(); 8425ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying __ghes_print_estatus(KERN_EMERG, ghes_global->generic, 84367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_global->estatus); 84481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* reboot to log the error! */ 84581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (panic_timeout == 0) 84681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic_timeout = ghes_panic_timeout; 84781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic("Fatal hardware error!"); 84881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 84981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 85081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 85167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 85267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying u32 len, node_len; 85367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ghes_estatus_node *estatus_node; 85467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct acpi_hest_generic_status *estatus; 85567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 85681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 85781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 85867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 859152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_estatus_cached(ghes->estatus)) 860152cef40a808d3034e383465b3f7d6783613e458Huang Ying goto next; 86167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying /* Save estatus for further processing in IRQ context */ 86267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = apei_estatus_len(ghes->estatus); 86367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 86467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, 86567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len); 86667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (estatus_node) { 86767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node->generic = ghes->generic; 86867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 86967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying memcpy(estatus, ghes->estatus, len); 87067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llist_add(&estatus_node->llnode, &ghes_estatus_llist); 87167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 872152cef40a808d3034e383465b3f7d6783613e458Huang Yingnext: 87367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 87481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 87581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 87667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 87767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying irq_work_queue(&ghes_proc_irq_work); 87867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 87981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 88081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout: 88181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_nmi_lock); 88281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return ret; 88381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 88481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 885d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = { 886d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying .notifier_call = ghes_notify_sci, 887d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 888d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 88967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_esource_prealloc_size( 89067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic *generic) 89167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 89267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long block_length, prealloc_records, prealloc_size; 89367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 89467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying block_length = min_t(unsigned long, generic->error_block_length, 89567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying GHES_ESTATUS_MAX_SIZE); 89667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying prealloc_records = max_t(unsigned long, 89767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying generic->records_to_preallocate, 1); 89867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying prealloc_size = min_t(unsigned long, block_length * prealloc_records, 89967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying GHES_ESOURCE_PREALLOC_MAX_SIZE); 90067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 90167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return prealloc_size; 90267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 90367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 9047ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev) 905d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 906d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 907d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes = NULL; 90867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long len; 9097ad6e9435596f692ff65f399da12816c94960185Huang Ying int rc = -EINVAL; 910d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9111dd6b20e368765223c31569d364219785b24700bJin Dongming generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; 912d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!generic->enabled) 9137ad6e9435596f692ff65f399da12816c94960185Huang Ying return -ENODEV; 914d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 91581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 91681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 91781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 91881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 91981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 92081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 92181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_LOCAL: 92281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", 923d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 924d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 92581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 92681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", 92781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->notify.type, generic->header.source_id); 92881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 929d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 93081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 93181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = -EIO; 93281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (generic->error_block_length < 93381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(struct acpi_hest_generic_status)) { 93481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", 93581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->error_block_length, 936d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 937d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 938d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 939d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = ghes_new(generic); 940d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (IS_ERR(ghes)) { 941d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = PTR_ERR(ghes); 942d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = NULL; 943d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 944d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 94581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 94681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 94781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.function = ghes_poll_func; 94881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.data = (unsigned long)ghes; 94981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying init_timer_deferrable(&ghes->timer); 95081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 95181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 95281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 95381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* External interrupt vector is GSI */ 95481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) { 95581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", 95681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 95781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 95881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 95981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (request_irq(ghes->irq, ghes_irq_func, 96081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 0, "GHES IRQ", ghes)) { 96181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", 96281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 96381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 96481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 96581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 96681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 9677ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 968d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (list_empty(&ghes_sci)) 969d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying register_acpi_hed_notifier(&ghes_notifier_sci); 970d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_add_rcu(&ghes->list, &ghes_sci); 9717ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 97281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 97381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 97467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = ghes_esource_prealloc_size(generic); 97567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_expand(len); 97681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_lock(&ghes_list_mutex); 97781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (list_empty(&ghes_nmi)) 9789c48f1c629ecfa114850c03f875c6691003214deDon Zickus register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, 9799c48f1c629ecfa114850c03f875c6691003214deDon Zickus "ghes"); 98081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_add_rcu(&ghes->list, &ghes_nmi); 98181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_unlock(&ghes_list_mutex); 98281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 98381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 98481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG(); 985d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 9867ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, ghes); 987d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 988d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 989d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr: 9907ad6e9435596f692ff65f399da12816c94960185Huang Ying if (ghes) { 991d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_fini(ghes); 9927ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 9937ad6e9435596f692ff65f399da12816c94960185Huang Ying } 994d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 995d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 996d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9977ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev) 998d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 9997ad6e9435596f692ff65f399da12816c94960185Huang Ying struct ghes *ghes; 10007ad6e9435596f692ff65f399da12816c94960185Huang Ying struct acpi_hest_generic *generic; 100167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long len; 1002d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10037ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes = platform_get_drvdata(ghes_dev); 10047ad6e9435596f692ff65f399da12816c94960185Huang Ying generic = ghes->generic; 10057ad6e9435596f692ff65f399da12816c94960185Huang Ying 100681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->flags |= GHES_EXITING; 10077ad6e9435596f692ff65f399da12816c94960185Huang Ying switch (generic->notify.type) { 100881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 100981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying del_timer_sync(&ghes->timer); 101081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 101181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 101281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_irq(ghes->irq, ghes); 101381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 10147ad6e9435596f692ff65f399da12816c94960185Huang Ying case ACPI_HEST_NOTIFY_SCI: 10157ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 10167ad6e9435596f692ff65f399da12816c94960185Huang Ying list_del_rcu(&ghes->list); 10177ad6e9435596f692ff65f399da12816c94960185Huang Ying if (list_empty(&ghes_sci)) 10187ad6e9435596f692ff65f399da12816c94960185Huang Ying unregister_acpi_hed_notifier(&ghes_notifier_sci); 10197ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 10207ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 102181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 102281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_lock(&ghes_list_mutex); 102381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_del_rcu(&ghes->list); 102481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (list_empty(&ghes_nmi)) 10259c48f1c629ecfa114850c03f875c6691003214deDon Zickus unregister_nmi_handler(NMI_LOCAL, "ghes"); 102681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_unlock(&ghes_list_mutex); 102781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* 102881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * To synchronize with NMI handler, ghes can only be 102981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * freed after NMI handler finishes. 103081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 103181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying synchronize_rcu(); 103267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = ghes_esource_prealloc_size(generic); 103367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_shrink(len); 103481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 10357ad6e9435596f692ff65f399da12816c94960185Huang Ying default: 10367ad6e9435596f692ff65f399da12816c94960185Huang Ying BUG(); 10377ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 10387ad6e9435596f692ff65f399da12816c94960185Huang Ying } 1039d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10407ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes_fini(ghes); 10417ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 1042d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10437ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, NULL); 10447ad6e9435596f692ff65f399da12816c94960185Huang Ying 10457ad6e9435596f692ff65f399da12816c94960185Huang Ying return 0; 1046d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1047d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10487ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = { 10497ad6e9435596f692ff65f399da12816c94960185Huang Ying .driver = { 10507ad6e9435596f692ff65f399da12816c94960185Huang Ying .name = "GHES", 10517ad6e9435596f692ff65f399da12816c94960185Huang Ying .owner = THIS_MODULE, 10527ad6e9435596f692ff65f399da12816c94960185Huang Ying }, 10537ad6e9435596f692ff65f399da12816c94960185Huang Ying .probe = ghes_probe, 10547ad6e9435596f692ff65f399da12816c94960185Huang Ying .remove = ghes_remove, 10557ad6e9435596f692ff65f399da12816c94960185Huang Ying}; 10567ad6e9435596f692ff65f399da12816c94960185Huang Ying 1057d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void) 1058d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 105981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 106081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 1061d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (acpi_disabled) 1062d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENODEV; 1063d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1064d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (hest_disable) { 1065d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_info(GHES_PFX "HEST is not enabled!\n"); 1066d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EINVAL; 1067d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 1068d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1069b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying if (ghes_disable) { 1070b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying pr_info(GHES_PFX "GHES is not enabled!\n"); 1071b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying return -EINVAL; 1072b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying } 1073b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 107467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); 107567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 107681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_ioremap_init(); 107781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 107881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 107981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 108067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying rc = ghes_estatus_pool_init(); 108181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 108281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err_ioremap_exit; 108381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 1084152cef40a808d3034e383465b3f7d6783613e458Huang Ying rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * 1085152cef40a808d3034e383465b3f7d6783613e458Huang Ying GHES_ESTATUS_CACHE_ALLOCED_MAX); 1086152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (rc) 1087152cef40a808d3034e383465b3f7d6783613e458Huang Ying goto err_pool_exit; 1088152cef40a808d3034e383465b3f7d6783613e458Huang Ying 108967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying rc = platform_driver_register(&ghes_platform_driver); 109067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (rc) 109167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying goto err_pool_exit; 109267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 10939fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying rc = apei_osc_setup(); 10949fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying if (rc == 0 && osc_sb_apei_support_acked) 10959fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n"); 10969fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc == 0 && !osc_sb_apei_support_acked) 10979fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n"); 10989fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc && osc_sb_apei_support_acked) 10999fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); 11009fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else 11019fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); 11029fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying 110381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 110467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingerr_pool_exit: 110567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_exit(); 110681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit: 110781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 110881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr: 110981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return rc; 1110d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1111d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1112d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void) 1113d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 11147ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_driver_unregister(&ghes_platform_driver); 111567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_exit(); 111681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 1117d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1118d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1119d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init); 1120d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit); 1121d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1122d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying"); 1123d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); 1124d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL"); 11257ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES"); 1126