ghes.c revision 152cef40a808d3034e383465b3f7d6783613e458
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> 36d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/io.h> 37d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/interrupt.h> 3881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/timer.h> 39d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/cper.h> 40d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <linux/kdebug.h> 417ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/platform_device.h> 427ad6e9435596f692ff65f399da12816c94960185Huang Ying#include <linux/mutex.h> 4332c361f574f85fa47600d84900598e2efc99082eHuang Ying#include <linux/ratelimit.h> 4481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <linux/vmalloc.h> 4567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/irq_work.h> 4667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/llist.h> 4767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#include <linux/genalloc.h> 48d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h> 49d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/atomicio.h> 50d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h> 51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h> 5281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h> 53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 54d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h" 55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX "GHES: " 57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE 65536 5967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 6067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 6167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 6267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 63152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* This is just an estimation for memory pool allocation */ 64152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_AVG_SIZE 512 65152cef40a808d3034e383465b3f7d6783613e458Huang Ying 66152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHES_SIZE 4 67152cef40a808d3034e383465b3f7d6783613e458Huang Ying 68152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_IN_CACHE_MAX_NSEC (10 * NSEC_PER_SEC) 69152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* Prevent too many caches are allocated because of RCU */ 70152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) 71152cef40a808d3034e383465b3f7d6783613e458Huang Ying 72152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ 73152cef40a808d3034e383465b3f7d6783613e458Huang Ying (sizeof(struct ghes_estatus_cache) + (estatus_len)) 74152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ 75152cef40a808d3034e383465b3f7d6783613e458Huang Ying ((struct acpi_hest_generic_status *) \ 76152cef40a808d3034e383465b3f7d6783613e458Huang Ying ((struct ghes_estatus_cache *)(estatus_cache) + 1)) 77152cef40a808d3034e383465b3f7d6783613e458Huang Ying 7867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_NODE_LEN(estatus_len) \ 7967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying (sizeof(struct ghes_estatus_node) + (estatus_len)) 8067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_FROM_NODE(estatus_node) \ 8167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ((struct acpi_hest_generic_status *) \ 8267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ((struct ghes_estatus_node *)(estatus_node) + 1)) 83d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 84d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 8581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * One struct ghes is created for each generic hardware error source. 86d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI 8781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler. 88d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during 90d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing. 91d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR 0x0001 9381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_EXITING 0x0002 94d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 95d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes { 96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic_status *estatus; 98d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buffer_paddr; 99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long flags; 10081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying union { 10181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct list_head list; 10281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct timer_list timer; 10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned int irq; 10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying }; 105d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 106d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstruct ghes_estatus_node { 10867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct llist_node llnode; 10967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct acpi_hest_generic *generic; 11067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying}; 11167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 112152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache { 113152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 estatus_len; 114152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_t count; 115152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic; 116152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long time_in; 117152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct rcu_head rcu; 118152cef40a808d3034e383465b3f7d6783613e458Huang Ying}; 119152cef40a808d3034e383465b3f7d6783613e458Huang Ying 120b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingint ghes_disable; 121b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0); 122b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 12381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_panic_timeout __read_mostly = 30; 12481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 125d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function, 12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one. This is applied 12881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too. 129d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 13081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for 13181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing. 132d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 133d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci); 13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic LIST_HEAD(ghes_nmi); 1357ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex); 136d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 13881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * NMI may be triggered on any CPU, so ghes_nmi_lock is used for 13981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * mutual exclusion. 14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock); 14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information 14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer 14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so 14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that. 14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Two virtual pages are used, one for NMI context, the other for 15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * IRQ/PROCESS context 15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_PAGES 2 15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_NMI_PAGE(base) (base) 15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) 15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */ 15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area; 16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory 16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously. 16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); 16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq); 16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 16767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying/* 16867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * printk is not safe in NMI context. So in NMI handler, we allocate 16967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required memory from lock-less memory allocator 17067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * (ghes_estatus_pool), save estatus into it, put them into lock-less 17167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * list (ghes_estatus_llist), then delay printk into IRQ context via 17267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record 17367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * required pool size by all NMI error source. 17467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */ 17567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct gen_pool *ghes_estatus_pool; 17667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_estatus_pool_size_request; 17767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct llist_head ghes_estatus_llist; 17867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct irq_work ghes_proc_irq_work; 17967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 180152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; 181152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic atomic_t ghes_estatus_cache_alloced; 182152cef40a808d3034e383465b3f7d6783613e458Huang Ying 18381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void) 18481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 18581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, 18681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying VM_IOREMAP, VMALLOC_START, VMALLOC_END); 18781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!ghes_ioremap_area) { 18881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); 18981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return -ENOMEM; 19081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 19181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 19281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 19381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 19481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 19581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void) 19681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 19781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_vm_area(ghes_ioremap_area); 19881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 19981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 20081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) 20181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 20281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 20381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 20481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); 20581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 20681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 20781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 20881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 20981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 21081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 21181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn) 21281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 21381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 21481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 21581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); 21681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 21781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 21881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 21981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 22081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 22181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 22281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr) 22381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 22481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 22581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 22681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 22781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); 22881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 22981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying __flush_tlb_one(vaddr); 23081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 23181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 23281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr) 23381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 23481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 23581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 23681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 23781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); 23881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 23981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying __flush_tlb_one(vaddr); 24081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 24181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 24267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_init(void) 24367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 24467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1); 24567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (!ghes_estatus_pool) 24667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return -ENOMEM; 24767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 24867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 24967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 25067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool, 25167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct gen_pool_chunk *chunk, 25267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying void *data) 25367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 25467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying free_page(chunk->start_addr); 25567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 25667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 25767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_exit(void) 25867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 25967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_for_each_chunk(ghes_estatus_pool, 26067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_free_chunk_page, NULL); 26167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_destroy(ghes_estatus_pool); 26267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 26367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 26467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_expand(unsigned long len) 26567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 26667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long i, pages, size, addr; 26767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying int ret; 26867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 26967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_size_request += PAGE_ALIGN(len); 27067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying size = gen_pool_size(ghes_estatus_pool); 27167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (size >= ghes_estatus_pool_size_request) 27267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 27367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE; 27467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying for (i = 0; i < pages; i++) { 27567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying addr = __get_free_page(GFP_KERNEL); 27667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (!addr) 27767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return -ENOMEM; 27867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1); 27967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ret) 28067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return ret; 28167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 28267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 28367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 28467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 28567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 28667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_shrink(unsigned long len) 28767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 28867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_size_request -= PAGE_ALIGN(len); 28967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 29067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 291d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic) 292d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned int error_block_length; 295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = kzalloc(sizeof(*ghes), GFP_KERNEL); 298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes) 299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(-ENOMEM); 300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->generic = generic; 301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = acpi_pre_map_gar(&generic->error_status_address); 302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_free; 304d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = generic->error_block_length; 305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (error_block_length > GHES_ESTATUS_MAX_SIZE) { 306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 307d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Error status block length is too long: %u for " 308d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "generic hardware error source: %d.\n", 309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length, generic->header.source_id); 310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = GHES_ESTATUS_MAX_SIZE; 311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus = kmalloc(error_block_length, GFP_KERNEL); 313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus) { 314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -ENOMEM; 315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_unmap; 316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ghes; 319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap: 321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying acpi_post_unmap_gar(&generic->error_status_address); 322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free: 323d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes); 324d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(rc); 325d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes) 328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes->estatus); 330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying acpi_post_unmap_gar(&ghes->generic->error_status_address); 331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum { 334ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_NO = 0x0, 335ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_CORRECTED = 0x1, 336ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_RECOVERABLE = 0x2, 337ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_PANIC = 0x3, 338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 340d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity) 341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying switch (severity) { 343ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_INFORMATIONAL: 344ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_NO; 345ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_CORRECTED: 346ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_CORRECTED; 347ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_RECOVERABLE: 348ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_RECOVERABLE; 349ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_FATAL: 350ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 351d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying default: 35225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi /* Unknown, go panic */ 353ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 35781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, 35881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int from_phys) 359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 36081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void __iomem *vaddr; 36181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long flags = 0; 36281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int in_nmi = in_nmi(); 36381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u64 offset; 36481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u32 trunk; 36581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 36681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying while (len > 0) { 36781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying offset = paddr - (paddr & PAGE_MASK); 36881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 36981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_ioremap_lock_nmi); 37081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); 37181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 37281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); 37381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); 37481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 37581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = PAGE_SIZE - offset; 37681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = min(trunk, len); 37781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (from_phys) 37881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_fromio(buffer, vaddr + offset, trunk); 37981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying else 38081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_toio(vaddr + offset, buffer, trunk); 38181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len -= trunk; 38281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying paddr += trunk; 38381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buffer += trunk; 38481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 38581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_nmi(vaddr); 38681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_ioremap_lock_nmi); 38781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 38881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_irq(vaddr); 38981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); 39081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 39181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 392d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 393d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 394d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent) 395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 396d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *g = ghes->generic; 397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buf_paddr; 398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u32 len; 399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); 402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) { 403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!silent && printk_ratelimit()) 404d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 405d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n", 406d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying g->header.source_id); 407d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EIO; 408d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 409d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!buf_paddr) 410d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 411d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 41281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, 41381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(*ghes->estatus), 1); 414d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus->block_status) 415d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 416d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 417d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->buffer_paddr = buf_paddr; 418d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags |= GHES_TO_CLEAR; 419d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 420d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -EIO; 421d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying len = apei_estatus_len(ghes->estatus); 422d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len < sizeof(*ghes->estatus)) 423d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 424d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len > ghes->generic->error_block_length) 425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 426d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (apei_estatus_check_header(ghes->estatus)) 427d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 42881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus + 1, 42981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buf_paddr + sizeof(*ghes->estatus), 43081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len - sizeof(*ghes->estatus), 1); 431d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (apei_estatus_check(ghes->estatus)) 432d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 433d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = 0; 434d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 435d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block: 43681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc && !silent && printk_ratelimit()) 437d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 438d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Failed to read error status block!\n"); 439d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 440d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 441d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 442d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes) 443d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 444d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus->block_status = 0; 445d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 446d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return; 447d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr, 448d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying sizeof(ghes->estatus->block_status), 0); 449d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags &= ~GHES_TO_CLEAR; 450d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 451d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 45267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_do_proc(const struct acpi_hest_generic_status *estatus) 453d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 454ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying int sev, processed = 0; 455d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic_data *gdata; 456d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 45767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying sev = ghes_severity(estatus->error_severity); 45867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying apei_estatus_for_each_section(estatus, gdata) { 459d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#ifdef CONFIG_X86_MCE 460d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 461d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying CPER_SEC_PLATFORM_MEM)) { 462d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying apei_mce_report_mem_error( 463ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying sev == GHES_SEV_CORRECTED, 464d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying (struct cper_sec_mem_err *)(gdata+1)); 465d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying processed = 1; 466d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 467d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif 468d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 46932c361f574f85fa47600d84900598e2efc99082eHuang Ying} 470d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 47167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void __ghes_print_estatus(const char *pfx, 47267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic *generic, 47367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic_status *estatus) 47432c361f574f85fa47600d84900598e2efc99082eHuang Ying{ 47532c361f574f85fa47600d84900598e2efc99082eHuang Ying if (pfx == NULL) { 47667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ghes_severity(estatus->error_severity) <= 47732c361f574f85fa47600d84900598e2efc99082eHuang Ying GHES_SEV_CORRECTED) 47832c361f574f85fa47600d84900598e2efc99082eHuang Ying pfx = KERN_WARNING HW_ERR; 47932c361f574f85fa47600d84900598e2efc99082eHuang Ying else 48032c361f574f85fa47600d84900598e2efc99082eHuang Ying pfx = KERN_ERR HW_ERR; 48132c361f574f85fa47600d84900598e2efc99082eHuang Ying } 4825588340d46a484da53bbce8136184d9c7fbc259cHuang Ying printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", 48367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying pfx, generic->header.source_id); 48467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying apei_estatus_print(pfx, estatus); 4855588340d46a484da53bbce8136184d9c7fbc259cHuang Ying} 4865588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 487152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_print_estatus(const char *pfx, 488152cef40a808d3034e383465b3f7d6783613e458Huang Ying const struct acpi_hest_generic *generic, 489152cef40a808d3034e383465b3f7d6783613e458Huang Ying const struct acpi_hest_generic_status *estatus) 4905588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{ 4915588340d46a484da53bbce8136184d9c7fbc259cHuang Ying /* Not more than 2 messages every 5 seconds */ 49267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); 49367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); 49467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ratelimit_state *ratelimit; 4955588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 49667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED) 49767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ratelimit = &ratelimit_corrected; 49867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying else 49967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ratelimit = &ratelimit_uncorrected; 500152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (__ratelimit(ratelimit)) { 50167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying __ghes_print_estatus(pfx, generic, estatus); 502152cef40a808d3034e383465b3f7d6783613e458Huang Ying return 1; 503152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 504152cef40a808d3034e383465b3f7d6783613e458Huang Ying return 0; 505152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 506152cef40a808d3034e383465b3f7d6783613e458Huang Ying 507152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* 508152cef40a808d3034e383465b3f7d6783613e458Huang Ying * GHES error status reporting throttle, to report more kinds of 509152cef40a808d3034e383465b3f7d6783613e458Huang Ying * errors, instead of just most frequently occurred errors. 510152cef40a808d3034e383465b3f7d6783613e458Huang Ying */ 511152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) 512152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 513152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len; 514152cef40a808d3034e383465b3f7d6783613e458Huang Ying int i, cached = 0; 515152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long now; 516152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 517152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *cache_estatus; 518152cef40a808d3034e383465b3f7d6783613e458Huang Ying 519152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = apei_estatus_len(estatus); 520152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_lock(); 521152cef40a808d3034e383465b3f7d6783613e458Huang Ying for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { 522152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = rcu_dereference(ghes_estatus_caches[i]); 523152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (cache == NULL) 524152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 525152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (len != cache->estatus_len) 526152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 527152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); 528152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (memcmp(estatus, cache_estatus, len)) 529152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 530152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_inc(&cache->count); 531152cef40a808d3034e383465b3f7d6783613e458Huang Ying now = sched_clock(); 532152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) 533152cef40a808d3034e383465b3f7d6783613e458Huang Ying cached = 1; 534152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 535152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 536152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_unlock(); 537152cef40a808d3034e383465b3f7d6783613e458Huang Ying return cached; 538152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 539152cef40a808d3034e383465b3f7d6783613e458Huang Ying 540152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic struct ghes_estatus_cache *ghes_estatus_cache_alloc( 541152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic, 542152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *estatus) 543152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 544152cef40a808d3034e383465b3f7d6783613e458Huang Ying int alloced; 545152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len, cache_len; 546152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 547152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *cache_estatus; 548152cef40a808d3034e383465b3f7d6783613e458Huang Ying 549152cef40a808d3034e383465b3f7d6783613e458Huang Ying alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); 550152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { 551152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 552152cef40a808d3034e383465b3f7d6783613e458Huang Ying return NULL; 553152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 554152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = apei_estatus_len(estatus); 555152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_len = GHES_ESTATUS_CACHE_LEN(len); 556152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); 557152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!cache) { 558152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 559152cef40a808d3034e383465b3f7d6783613e458Huang Ying return NULL; 560152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 561152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); 562152cef40a808d3034e383465b3f7d6783613e458Huang Ying memcpy(cache_estatus, estatus, len); 563152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->estatus_len = len; 564152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_set(&cache->count, 0); 565152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->generic = generic; 566152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->time_in = sched_clock(); 567152cef40a808d3034e383465b3f7d6783613e458Huang Ying return cache; 568152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 569152cef40a808d3034e383465b3f7d6783613e458Huang Ying 570152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_free(struct ghes_estatus_cache *cache) 571152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 572152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len; 573152cef40a808d3034e383465b3f7d6783613e458Huang Ying 574152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); 575152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = GHES_ESTATUS_CACHE_LEN(len); 576152cef40a808d3034e383465b3f7d6783613e458Huang Ying gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); 577152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 578152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 579152cef40a808d3034e383465b3f7d6783613e458Huang Ying 580152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_rcu_free(struct rcu_head *head) 581152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 582152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 583152cef40a808d3034e383465b3f7d6783613e458Huang Ying 584152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = container_of(head, struct ghes_estatus_cache, rcu); 585152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_free(cache); 586152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 587152cef40a808d3034e383465b3f7d6783613e458Huang Ying 588152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_add( 589152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic, 590152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic_status *estatus) 591152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 592152cef40a808d3034e383465b3f7d6783613e458Huang Ying int i, slot = -1, count; 593152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long now, duration, period, max_period = 0; 594152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache; 595152cef40a808d3034e383465b3f7d6783613e458Huang Ying 596152cef40a808d3034e383465b3f7d6783613e458Huang Ying new_cache = ghes_estatus_cache_alloc(generic, estatus); 597152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (new_cache == NULL) 598152cef40a808d3034e383465b3f7d6783613e458Huang Ying return; 599152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_lock(); 600152cef40a808d3034e383465b3f7d6783613e458Huang Ying now = sched_clock(); 601152cef40a808d3034e383465b3f7d6783613e458Huang Ying for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { 602152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = rcu_dereference(ghes_estatus_caches[i]); 603152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (cache == NULL) { 604152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 605152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = NULL; 606152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 607152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 608152cef40a808d3034e383465b3f7d6783613e458Huang Ying duration = now - cache->time_in; 609152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) { 610152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 611152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = cache; 612152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 613152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 614152cef40a808d3034e383465b3f7d6783613e458Huang Ying count = atomic_read(&cache->count); 615152cef40a808d3034e383465b3f7d6783613e458Huang Ying period = duration / (count + 1); 616152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (period > max_period) { 617152cef40a808d3034e383465b3f7d6783613e458Huang Ying max_period = period; 618152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 619152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = cache; 620152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 621152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 622152cef40a808d3034e383465b3f7d6783613e458Huang Ying /* new_cache must be put into array after its contents are written */ 623152cef40a808d3034e383465b3f7d6783613e458Huang Ying smp_wmb(); 624152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (slot != -1 && cmpxchg(ghes_estatus_caches + slot, 625152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache, new_cache) == slot_cache) { 626152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (slot_cache) 627152cef40a808d3034e383465b3f7d6783613e458Huang Ying call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free); 628152cef40a808d3034e383465b3f7d6783613e458Huang Ying } else 629152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_free(new_cache); 630152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_unlock(); 631d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 632d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 633d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes) 634d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 635d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 636d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 637d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = ghes_read_estatus(ghes, 0); 638d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 639d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto out; 640152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!ghes_estatus_cached(ghes->estatus)) { 641152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus)) 642152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_add(ghes->generic, ghes->estatus); 643152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 64467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_do_proc(ghes->estatus); 645d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout: 646d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_clear_estatus(ghes); 647d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 648d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 649d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 65081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes) 65181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 65281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct acpi_hest_generic *g = ghes->generic; 65381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long expire; 65481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 65581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!g->notify.poll_interval) { 65681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n", 65781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying g->header.source_id); 65881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return; 65981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 66081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying expire = jiffies + msecs_to_jiffies(g->notify.poll_interval); 66181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.expires = round_jiffies_relative(expire); 66281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying add_timer(&ghes->timer); 66381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 66481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 66581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data) 66681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 66781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = (void *)data; 66881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 66981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_proc(ghes); 67081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_EXITING)) 67181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 67281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 67381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 67481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data) 67581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 67681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = data; 67781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 67881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 67981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_proc(ghes); 68081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 68181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_NONE; 68281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 68381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_HANDLED; 68481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 68581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 686d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this, 687d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long event, void *data) 688d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 689d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 690d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int ret = NOTIFY_DONE; 691d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 692d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_lock(); 693d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_for_each_entry_rcu(ghes, &ghes_sci, list) { 694d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes_proc(ghes)) 695d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ret = NOTIFY_OK; 696d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 697d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_unlock(); 698d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 699d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ret; 700d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 701d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 70267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_proc_in_irq(struct irq_work *irq_work) 70367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 70467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct llist_node *llnode, *next, *tail = NULL; 70567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ghes_estatus_node *estatus_node; 706152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic; 70767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct acpi_hest_generic_status *estatus; 70867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying u32 len, node_len; 70967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 71067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying /* 71167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * Because the time order of estatus in list is reversed, 71267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * revert it back to proper order. 71367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */ 71467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode = llist_del_all(&ghes_estatus_llist); 71567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying while (llnode) { 71667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying next = llnode->next; 71767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode->next = tail; 71867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying tail = llnode; 71967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode = next; 72067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 72167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode = tail; 72267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying while (llnode) { 72367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying next = llnode->next; 72467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node = llist_entry(llnode, struct ghes_estatus_node, 72567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode); 72667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 72767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = apei_estatus_len(estatus); 72867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 72967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_do_proc(estatus); 730152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!ghes_estatus_cached(estatus)) { 731152cef40a808d3034e383465b3f7d6783613e458Huang Ying generic = estatus_node->generic; 732152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_print_estatus(NULL, generic, estatus)) 733152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_add(generic, estatus); 734152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 73567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, 73667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len); 73767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode = next; 73867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 73967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 74067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 74181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_notify_nmi(struct notifier_block *this, 74281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long cmd, void *data) 74381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 74481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes, *ghes_global = NULL; 74581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int sev, sev_global = -1; 74681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int ret = NOTIFY_DONE; 74781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 74881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (cmd != DIE_NMI) 74981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return ret; 75081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 75181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_nmi_lock); 75281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 75381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (ghes_read_estatus(ghes, 1)) { 75481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 75581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 75681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 75781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev = ghes_severity(ghes->estatus->error_severity); 75881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev > sev_global) { 75981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev_global = sev; 76081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_global = ghes; 76181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 76281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ret = NOTIFY_STOP; 76381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 76481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 76581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (ret == NOTIFY_DONE) 76681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto out; 76781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 76881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev_global >= GHES_SEV_PANIC) { 76981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying oops_begin(); 77067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic, 77167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_global->estatus); 77281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* reboot to log the error! */ 77381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (panic_timeout == 0) 77481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic_timeout = ghes_panic_timeout; 77581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic("Fatal hardware error!"); 77681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 77781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 77881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 77967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 78067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying u32 len, node_len; 78167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ghes_estatus_node *estatus_node; 78267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct acpi_hest_generic_status *estatus; 78367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 78481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 78581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 78667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 787152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_estatus_cached(ghes->estatus)) 788152cef40a808d3034e383465b3f7d6783613e458Huang Ying goto next; 78967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying /* Save estatus for further processing in IRQ context */ 79067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = apei_estatus_len(ghes->estatus); 79167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 79267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, 79367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len); 79467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (estatus_node) { 79567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node->generic = ghes->generic; 79667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 79767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying memcpy(estatus, ghes->estatus, len); 79867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llist_add(&estatus_node->llnode, &ghes_estatus_llist); 79967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 800152cef40a808d3034e383465b3f7d6783613e458Huang Yingnext: 80167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 80281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 80381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 80467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 80567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying irq_work_queue(&ghes_proc_irq_work); 80667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 80781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 80881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout: 80981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_nmi_lock); 81081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return ret; 81181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 81281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 813d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = { 814d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying .notifier_call = ghes_notify_sci, 815d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 816d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 81781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct notifier_block ghes_notifier_nmi = { 81881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying .notifier_call = ghes_notify_nmi, 81981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}; 82081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 82167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_esource_prealloc_size( 82267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic *generic) 82367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 82467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long block_length, prealloc_records, prealloc_size; 82567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 82667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying block_length = min_t(unsigned long, generic->error_block_length, 82767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying GHES_ESTATUS_MAX_SIZE); 82867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying prealloc_records = max_t(unsigned long, 82967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying generic->records_to_preallocate, 1); 83067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying prealloc_size = min_t(unsigned long, block_length * prealloc_records, 83167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying GHES_ESOURCE_PREALLOC_MAX_SIZE); 83267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 83367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return prealloc_size; 83467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 83567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 8367ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev) 837d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 838d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 839d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes = NULL; 84067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long len; 8417ad6e9435596f692ff65f399da12816c94960185Huang Ying int rc = -EINVAL; 842d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 8431dd6b20e368765223c31569d364219785b24700bJin Dongming generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; 844d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!generic->enabled) 8457ad6e9435596f692ff65f399da12816c94960185Huang Ying return -ENODEV; 846d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 84781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 84881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 84981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 85081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 85181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 85281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 85381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_LOCAL: 85481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", 855d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 856d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 85781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 85881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", 85981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->notify.type, generic->header.source_id); 86081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 861d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 86281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 86381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = -EIO; 86481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (generic->error_block_length < 86581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(struct acpi_hest_generic_status)) { 86681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", 86781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->error_block_length, 868d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 869d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 870d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 871d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = ghes_new(generic); 872d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (IS_ERR(ghes)) { 873d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = PTR_ERR(ghes); 874d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = NULL; 875d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 876d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 87781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 87881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 87981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.function = ghes_poll_func; 88081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.data = (unsigned long)ghes; 88181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying init_timer_deferrable(&ghes->timer); 88281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 88381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 88481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 88581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* External interrupt vector is GSI */ 88681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) { 88781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", 88881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 88981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 89081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 89181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (request_irq(ghes->irq, ghes_irq_func, 89281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 0, "GHES IRQ", ghes)) { 89381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", 89481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 89581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 89681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 89781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 89881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 8997ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 900d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (list_empty(&ghes_sci)) 901d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying register_acpi_hed_notifier(&ghes_notifier_sci); 902d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_add_rcu(&ghes->list, &ghes_sci); 9037ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 90481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 90581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 90667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = ghes_esource_prealloc_size(generic); 90767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_expand(len); 90881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_lock(&ghes_list_mutex); 90981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (list_empty(&ghes_nmi)) 91081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying register_die_notifier(&ghes_notifier_nmi); 91181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_add_rcu(&ghes->list, &ghes_nmi); 91281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_unlock(&ghes_list_mutex); 91381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 91481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 91581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG(); 916d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 9177ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, ghes); 918d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 919d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 920d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr: 9217ad6e9435596f692ff65f399da12816c94960185Huang Ying if (ghes) { 922d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_fini(ghes); 9237ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 9247ad6e9435596f692ff65f399da12816c94960185Huang Ying } 925d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 926d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 927d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9287ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev) 929d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 9307ad6e9435596f692ff65f399da12816c94960185Huang Ying struct ghes *ghes; 9317ad6e9435596f692ff65f399da12816c94960185Huang Ying struct acpi_hest_generic *generic; 93267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long len; 933d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9347ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes = platform_get_drvdata(ghes_dev); 9357ad6e9435596f692ff65f399da12816c94960185Huang Ying generic = ghes->generic; 9367ad6e9435596f692ff65f399da12816c94960185Huang Ying 93781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->flags |= GHES_EXITING; 9387ad6e9435596f692ff65f399da12816c94960185Huang Ying switch (generic->notify.type) { 93981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 94081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying del_timer_sync(&ghes->timer); 94181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 94281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 94381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_irq(ghes->irq, ghes); 94481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 9457ad6e9435596f692ff65f399da12816c94960185Huang Ying case ACPI_HEST_NOTIFY_SCI: 9467ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 9477ad6e9435596f692ff65f399da12816c94960185Huang Ying list_del_rcu(&ghes->list); 9487ad6e9435596f692ff65f399da12816c94960185Huang Ying if (list_empty(&ghes_sci)) 9497ad6e9435596f692ff65f399da12816c94960185Huang Ying unregister_acpi_hed_notifier(&ghes_notifier_sci); 9507ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 9517ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 95281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 95381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_lock(&ghes_list_mutex); 95481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_del_rcu(&ghes->list); 95581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (list_empty(&ghes_nmi)) 95681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unregister_die_notifier(&ghes_notifier_nmi); 95781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_unlock(&ghes_list_mutex); 95881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* 95981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * To synchronize with NMI handler, ghes can only be 96081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * freed after NMI handler finishes. 96181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 96281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying synchronize_rcu(); 96367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying len = ghes_esource_prealloc_size(generic); 96467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_shrink(len); 96581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 9667ad6e9435596f692ff65f399da12816c94960185Huang Ying default: 9677ad6e9435596f692ff65f399da12816c94960185Huang Ying BUG(); 9687ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 9697ad6e9435596f692ff65f399da12816c94960185Huang Ying } 970d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9717ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes_fini(ghes); 9727ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 973d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9747ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, NULL); 9757ad6e9435596f692ff65f399da12816c94960185Huang Ying 9767ad6e9435596f692ff65f399da12816c94960185Huang Ying return 0; 977d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 978d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9797ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = { 9807ad6e9435596f692ff65f399da12816c94960185Huang Ying .driver = { 9817ad6e9435596f692ff65f399da12816c94960185Huang Ying .name = "GHES", 9827ad6e9435596f692ff65f399da12816c94960185Huang Ying .owner = THIS_MODULE, 9837ad6e9435596f692ff65f399da12816c94960185Huang Ying }, 9847ad6e9435596f692ff65f399da12816c94960185Huang Ying .probe = ghes_probe, 9857ad6e9435596f692ff65f399da12816c94960185Huang Ying .remove = ghes_remove, 9867ad6e9435596f692ff65f399da12816c94960185Huang Ying}; 9877ad6e9435596f692ff65f399da12816c94960185Huang Ying 988d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void) 989d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 99081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 99181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 992d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (acpi_disabled) 993d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENODEV; 994d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 995d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (hest_disable) { 996d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_info(GHES_PFX "HEST is not enabled!\n"); 997d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EINVAL; 998d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 999d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1000b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying if (ghes_disable) { 1001b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying pr_info(GHES_PFX "GHES is not enabled!\n"); 1002b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying return -EINVAL; 1003b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying } 1004b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 100567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); 100667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 100781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_ioremap_init(); 100881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 100981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 101081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 101167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying rc = ghes_estatus_pool_init(); 101281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 101381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err_ioremap_exit; 101481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 1015152cef40a808d3034e383465b3f7d6783613e458Huang Ying rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * 1016152cef40a808d3034e383465b3f7d6783613e458Huang Ying GHES_ESTATUS_CACHE_ALLOCED_MAX); 1017152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (rc) 1018152cef40a808d3034e383465b3f7d6783613e458Huang Ying goto err_pool_exit; 1019152cef40a808d3034e383465b3f7d6783613e458Huang Ying 102067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying rc = platform_driver_register(&ghes_platform_driver); 102167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (rc) 102267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying goto err_pool_exit; 102367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 10249fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying rc = apei_osc_setup(); 10259fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying if (rc == 0 && osc_sb_apei_support_acked) 10269fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n"); 10279fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc == 0 && !osc_sb_apei_support_acked) 10289fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n"); 10299fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc && osc_sb_apei_support_acked) 10309fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); 10319fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else 10329fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); 10339fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying 103481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 103567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingerr_pool_exit: 103667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_exit(); 103781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit: 103881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 103981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr: 104081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return rc; 1041d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1042d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1043d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void) 1044d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 10457ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_driver_unregister(&ghes_platform_driver); 104667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_exit(); 104781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 1048d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1049d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1050d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init); 1051d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit); 1052d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1053d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying"); 1054d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); 1055d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL"); 10567ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES"); 1057