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> 48a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/pci.h> 49a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#include <linux/aer.h> 5044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#include <linux/nmi.h> 5140e064153814ce534a28714b25cb98259f107d96Mauro Carvalho Chehab 5240e064153814ce534a28714b25cb98259f107d96Mauro Carvalho Chehab#include <acpi/ghes.h> 539dae3d0d9e64c3cb8bb172f041d4e66d4b92088aTomasz Nowicki#include <acpi/apei.h> 5481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h> 55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h" 57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 58d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX "GHES: " 59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 60d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE 65536 6167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 6267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 6367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 6467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 65152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* This is just an estimation for memory pool allocation */ 66152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_AVG_SIZE 512 67152cef40a808d3034e383465b3f7d6783613e458Huang Ying 68152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHES_SIZE 4 69152cef40a808d3034e383465b3f7d6783613e458Huang Ying 7070cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL 71152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* Prevent too many caches are allocated because of RCU */ 72152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) 73152cef40a808d3034e383465b3f7d6783613e458Huang Ying 74152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ 75152cef40a808d3034e383465b3f7d6783613e458Huang Ying (sizeof(struct ghes_estatus_cache) + (estatus_len)) 76152cef40a808d3034e383465b3f7d6783613e458Huang Ying#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ 770a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng ((struct acpi_hest_generic_status *) \ 78152cef40a808d3034e383465b3f7d6783613e458Huang Ying ((struct ghes_estatus_cache *)(estatus_cache) + 1)) 79152cef40a808d3034e383465b3f7d6783613e458Huang Ying 8067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#define GHES_ESTATUS_NODE_LEN(estatus_len) \ 8167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying (sizeof(struct ghes_estatus_node) + (estatus_len)) 8288f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong#define GHES_ESTATUS_FROM_NODE(estatus_node) \ 830a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng ((struct acpi_hest_generic_status *) \ 8467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ((struct ghes_estatus_node *)(estatus_node) + 1)) 85d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 8690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool ghes_disable; 87b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0); 88b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 9081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function, 9181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one. This is applied 9281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too. 93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 9481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for 9581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing. 96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 97d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci); 987ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex); 99d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 10181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information 10281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer 10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so 10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that. 10581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 10681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 10781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 108594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki * Two virtual pages are used, one for IRQ/PROCESS context, the other for 109594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki * NMI context (optionally). 11081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 111594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#ifdef CONFIG_HAVE_ACPI_APEI_NMI 112594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_PAGES 2 113594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#else 114594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_PAGES 1 115594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#endif 116594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_IRQ_PAGE(base) (base) 117594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki#define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE) 11881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 11981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */ 12081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area; 12181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 12281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory 12381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously. 12481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 12581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); 12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq); 12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 12867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic struct gen_pool *ghes_estatus_pool; 12967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_estatus_pool_size_request; 13067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 131152cef40a808d3034e383465b3f7d6783613e458Huang Yingstruct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; 132152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic atomic_t ghes_estatus_cache_alloced; 133152cef40a808d3034e383465b3f7d6783613e458Huang Ying 13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void) 13581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 13681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, 13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying VM_IOREMAP, VMALLOC_START, VMALLOC_END); 13881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!ghes_ioremap_area) { 13981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); 14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return -ENOMEM; 14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void) 14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_vm_area(ghes_ioremap_area); 14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) 15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); 15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn) 16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); 16781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 16881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 16981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 17081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 17181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 17281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 17381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr) 17481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 17581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 17681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 17781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 17881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); 17981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 180594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki arch_apei_flush_tlb_one(vaddr); 18181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 18281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 18381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr) 18481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 18581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 18681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 18781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 18881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); 18981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 190594c7255dce7a13cac50cf2470cc56e2c3b0494eTomasz Nowicki arch_apei_flush_tlb_one(vaddr); 19181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 19281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 19367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_init(void) 19467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 19567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1); 19667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (!ghes_estatus_pool) 19767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return -ENOMEM; 19867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 19967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 20067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 20167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool, 20267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct gen_pool_chunk *chunk, 20367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying void *data) 20467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 20567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying free_page(chunk->start_addr); 20667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 20767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 20867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_estatus_pool_exit(void) 20967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 21067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_for_each_chunk(ghes_estatus_pool, 21167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_free_chunk_page, NULL); 21267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_destroy(ghes_estatus_pool); 21367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 21467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 21567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic int ghes_estatus_pool_expand(unsigned long len) 21667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 21767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long i, pages, size, addr; 21867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying int ret; 21967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 22067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_size_request += PAGE_ALIGN(len); 22167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying size = gen_pool_size(ghes_estatus_pool); 22267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (size >= ghes_estatus_pool_size_request) 22367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 22467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE; 22567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying for (i = 0; i < pages; i++) { 22667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying addr = __get_free_page(GFP_KERNEL); 22767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (!addr) 22867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return -ENOMEM; 22967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1); 23067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ret) 23167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return ret; 23267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 23367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 23467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return 0; 23567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 23667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 237d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic) 238d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 239d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 240d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned int error_block_length; 241d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 242d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 243d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = kzalloc(sizeof(*ghes), GFP_KERNEL); 244d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes) 245d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(-ENOMEM); 246d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->generic = generic; 24734ddeb035d704eafdcdb3cbc781894300136c3c4Huang Ying rc = apei_map_generic_address(&generic->error_status_address); 248d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 249d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_free; 250d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = generic->error_block_length; 251d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (error_block_length > GHES_ESTATUS_MAX_SIZE) { 252d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 253d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Error status block length is too long: %u for " 254d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "generic hardware error source: %d.\n", 255d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length, generic->header.source_id); 256d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = GHES_ESTATUS_MAX_SIZE; 257d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 258d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus = kmalloc(error_block_length, GFP_KERNEL); 259d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus) { 260d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -ENOMEM; 261d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_unmap; 262d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 263d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 264d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ghes; 265d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 266d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap: 26734ddeb035d704eafdcdb3cbc781894300136c3c4Huang Ying apei_unmap_generic_address(&generic->error_status_address); 268d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free: 269d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes); 270d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(rc); 271d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 272d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 273d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes) 274d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 275d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes->estatus); 27634ddeb035d704eafdcdb3cbc781894300136c3c4Huang Ying apei_unmap_generic_address(&ghes->generic->error_status_address); 277d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 278d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 279d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity) 280d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 281d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying switch (severity) { 282ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_INFORMATIONAL: 283ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_NO; 284ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_CORRECTED: 285ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_CORRECTED; 286ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_RECOVERABLE: 287ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_RECOVERABLE; 288ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_FATAL: 289ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 290d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying default: 29125985edcedea6396277003854657b5f3cb31a628Lucas De Marchi /* Unknown, go panic */ 292ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 29681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, 29781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int from_phys) 298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 29981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void __iomem *vaddr; 30081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long flags = 0; 30181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int in_nmi = in_nmi(); 30281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u64 offset; 30381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u32 trunk; 30481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 30581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying while (len > 0) { 30681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying offset = paddr - (paddr & PAGE_MASK); 30781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 30881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_ioremap_lock_nmi); 30981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); 31081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 31181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); 31281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); 31381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 31481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = PAGE_SIZE - offset; 31581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = min(trunk, len); 31681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (from_phys) 31781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_fromio(buffer, vaddr + offset, trunk); 31881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying else 31981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_toio(vaddr + offset, buffer, trunk); 32081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len -= trunk; 32181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying paddr += trunk; 32281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buffer += trunk; 32381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 32481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_nmi(vaddr); 32581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_ioremap_lock_nmi); 32681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 32781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_irq(vaddr); 32881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); 32981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 33081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 331d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent) 334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *g = ghes->generic; 336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buf_paddr; 337d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u32 len; 338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 340700130b41f4ee54520ac2ef2f7f1d072789711a4Myron Stowe rc = apei_read(&buf_paddr, &g->error_status_address); 341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) { 342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!silent && printk_ratelimit()) 343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n", 345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying g->header.source_id); 346d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EIO; 347d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 348d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!buf_paddr) 349d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 350d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 35181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, 35281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(*ghes->estatus), 1); 353d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus->block_status) 354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->buffer_paddr = buf_paddr; 357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags |= GHES_TO_CLEAR; 358d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -EIO; 36088f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(ghes->estatus); 361d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len < sizeof(*ghes->estatus)) 362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 363d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len > ghes->generic->error_block_length) 364d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 36588f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong if (cper_estatus_check_header(ghes->estatus)) 366d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 36781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus + 1, 36881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buf_paddr + sizeof(*ghes->estatus), 36981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len - sizeof(*ghes->estatus), 1); 37088f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong if (cper_estatus_check(ghes->estatus)) 371d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 372d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = 0; 373d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 374d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block: 37581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc && !silent && printk_ratelimit()) 376d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 377d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Failed to read error status block!\n"); 378d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 379d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 380d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 381d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes) 382d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 383d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus->block_status = 0; 384d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 385d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return; 386d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr, 387d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying sizeof(ghes->estatus->block_status), 0); 388d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags &= ~GHES_TO_CLEAR; 389d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 390d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 3910a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zhengstatic void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) 392cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao{ 393cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE 394cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao unsigned long pfn; 395ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong int flags = -1; 396cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao int sec_sev = ghes_severity(gdata->error_severity); 397cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao struct cper_sec_mem_err *mem_err; 398cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao mem_err = (struct cper_sec_mem_err *)(gdata + 1); 399cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao 400ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) 401ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong return; 402ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong 403ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong pfn = mem_err->physical_addr >> PAGE_SHIFT; 404ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong if (!pfn_valid(pfn)) { 405ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong pr_warn_ratelimited(FW_WARN GHES_PFX 406ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong "Invalid address in generic error data: %#llx\n", 407ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong mem_err->physical_addr); 408ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong return; 409cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao } 410ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong 411ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong /* iff following two events can be handled properly by now */ 412ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong if (sec_sev == GHES_SEV_CORRECTED && 413ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) 414ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong flags = MF_SOFT_OFFLINE; 415ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) 416ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong flags = 0; 417ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong 418ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong if (flags != -1) 419ca104edc17841da87850b20ab77e57fe0a99ead6Chen, Gong memory_failure_queue(pfn, 0, flags); 420cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao#endif 421cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao} 422cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao 42321480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehabstatic void ghes_do_proc(struct ghes *ghes, 4240a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng const struct acpi_hest_generic_status *estatus) 425d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 426ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying int sev, sec_sev; 4270a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_data *gdata; 428d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 42967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying sev = ghes_severity(estatus->error_severity); 43067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying apei_estatus_for_each_section(estatus, gdata) { 431ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying sec_sev = ghes_severity(gdata->error_severity); 432d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 433d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying CPER_SEC_PLATFORM_MEM)) { 434ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying struct cper_sec_mem_err *mem_err; 435ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying mem_err = (struct cper_sec_mem_err *)(gdata+1); 43621480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab ghes_edac_report_mem_error(ghes, sev, mem_err); 43721480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab 4389dae3d0d9e64c3cb8bb172f041d4e66d4b92088aTomasz Nowicki arch_apei_report_mem_error(sev, mem_err); 439cf870c70a194443f8fc654ddc9d6cfd02c58003bNaveen N. Rao ghes_handle_memory_failure(gdata, sev); 440ba61ca4aab47441f1c6cec28a9a6aa0489fd1df3Huang Ying } 441a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#ifdef CONFIG_ACPI_APEI_PCIEAER 442a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 443a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying CPER_SEC_PCIE)) { 444a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying struct cper_sec_pcie *pcie_err; 445a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err = (struct cper_sec_pcie *)(gdata+1); 446a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying if (sev == GHES_SEV_RECOVERABLE && 447a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying sec_sev == GHES_SEV_RECOVERABLE && 448a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && 449a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { 450a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying unsigned int devfn; 451a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying int aer_severity; 4520ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall 453a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying devfn = PCI_DEVFN(pcie_err->device_id.device, 454a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->device_id.function); 455a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying aer_severity = cper_severity_to_aer(sev); 4560ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall 4570ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall /* 4580ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall * If firmware reset the component to contain 4590ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall * the error, we must reinitialize it before 4600ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall * use, so treat it as a fatal AER error. 4610ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall */ 4620ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall if (gdata->flags & CPER_SEC_RESET) 4630ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall aer_severity = AER_FATAL; 4640ba98ec9196746dd6abfa7bb9856ef4f29ffb9beBetty Dall 465a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying aer_recover_queue(pcie_err->device_id.segment, 466a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying pcie_err->device_id.bus, 46737448adfc7ce0d6d5892b87aa8d57edde4126f49Lance Ortiz devfn, aer_severity, 46837448adfc7ce0d6d5892b87aa8d57edde4126f49Lance Ortiz (struct aer_capability_regs *) 46937448adfc7ce0d6d5892b87aa8d57edde4126f49Lance Ortiz pcie_err->aer_info); 470a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying } 471a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying 472a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying } 473a654e5ee4f2213844d23361eda4955fe9efaf35fHuang Ying#endif 474d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 47532c361f574f85fa47600d84900598e2efc99082eHuang Ying} 476d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 47767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void __ghes_print_estatus(const char *pfx, 47867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic *generic, 4790a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng const struct acpi_hest_generic_status *estatus) 48032c361f574f85fa47600d84900598e2efc99082eHuang Ying{ 4815ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying static atomic_t seqno; 4825ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying unsigned int curr_seqno; 4835ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying char pfx_seq[64]; 4845ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying 48532c361f574f85fa47600d84900598e2efc99082eHuang Ying if (pfx == NULL) { 48667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ghes_severity(estatus->error_severity) <= 48732c361f574f85fa47600d84900598e2efc99082eHuang Ying GHES_SEV_CORRECTED) 4885ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying pfx = KERN_WARNING; 48932c361f574f85fa47600d84900598e2efc99082eHuang Ying else 4905ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying pfx = KERN_ERR; 49132c361f574f85fa47600d84900598e2efc99082eHuang Ying } 4925ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying curr_seqno = atomic_inc_return(&seqno); 4935ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); 4945588340d46a484da53bbce8136184d9c7fbc259cHuang Ying printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", 4955ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying pfx_seq, generic->header.source_id); 49688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong cper_estatus_print(pfx_seq, estatus); 4975588340d46a484da53bbce8136184d9c7fbc259cHuang Ying} 4985588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 499152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic int ghes_print_estatus(const char *pfx, 500152cef40a808d3034e383465b3f7d6783613e458Huang Ying const struct acpi_hest_generic *generic, 5010a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng const struct acpi_hest_generic_status *estatus) 5025588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{ 5035588340d46a484da53bbce8136184d9c7fbc259cHuang Ying /* Not more than 2 messages every 5 seconds */ 50467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); 50567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); 50667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ratelimit_state *ratelimit; 5075588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 50867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED) 50967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ratelimit = &ratelimit_corrected; 51067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying else 51167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ratelimit = &ratelimit_uncorrected; 512152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (__ratelimit(ratelimit)) { 51367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying __ghes_print_estatus(pfx, generic, estatus); 514152cef40a808d3034e383465b3f7d6783613e458Huang Ying return 1; 515152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 516152cef40a808d3034e383465b3f7d6783613e458Huang Ying return 0; 517152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 518152cef40a808d3034e383465b3f7d6783613e458Huang Ying 519152cef40a808d3034e383465b3f7d6783613e458Huang Ying/* 520152cef40a808d3034e383465b3f7d6783613e458Huang Ying * GHES error status reporting throttle, to report more kinds of 521152cef40a808d3034e383465b3f7d6783613e458Huang Ying * errors, instead of just most frequently occurred errors. 522152cef40a808d3034e383465b3f7d6783613e458Huang Ying */ 5230a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zhengstatic int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) 524152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 525152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len; 526152cef40a808d3034e383465b3f7d6783613e458Huang Ying int i, cached = 0; 527152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long now; 528152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 5290a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *cache_estatus; 530152cef40a808d3034e383465b3f7d6783613e458Huang Ying 53188f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(estatus); 532152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_lock(); 533152cef40a808d3034e383465b3f7d6783613e458Huang Ying for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { 534152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = rcu_dereference(ghes_estatus_caches[i]); 535152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (cache == NULL) 536152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 537152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (len != cache->estatus_len) 538152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 539152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); 540152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (memcmp(estatus, cache_estatus, len)) 541152cef40a808d3034e383465b3f7d6783613e458Huang Ying continue; 542152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_inc(&cache->count); 543152cef40a808d3034e383465b3f7d6783613e458Huang Ying now = sched_clock(); 544152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) 545152cef40a808d3034e383465b3f7d6783613e458Huang Ying cached = 1; 546152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 547152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 548152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_unlock(); 549152cef40a808d3034e383465b3f7d6783613e458Huang Ying return cached; 550152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 551152cef40a808d3034e383465b3f7d6783613e458Huang Ying 552152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic struct ghes_estatus_cache *ghes_estatus_cache_alloc( 553152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic, 5540a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *estatus) 555152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 556152cef40a808d3034e383465b3f7d6783613e458Huang Ying int alloced; 557152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len, cache_len; 558152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 5590a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *cache_estatus; 560152cef40a808d3034e383465b3f7d6783613e458Huang Ying 561152cef40a808d3034e383465b3f7d6783613e458Huang Ying alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); 562152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { 563152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 564152cef40a808d3034e383465b3f7d6783613e458Huang Ying return NULL; 565152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 56688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(estatus); 567152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_len = GHES_ESTATUS_CACHE_LEN(len); 568152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); 569152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!cache) { 570152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 571152cef40a808d3034e383465b3f7d6783613e458Huang Ying return NULL; 572152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 573152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); 574152cef40a808d3034e383465b3f7d6783613e458Huang Ying memcpy(cache_estatus, estatus, len); 575152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->estatus_len = len; 576152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_set(&cache->count, 0); 577152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->generic = generic; 578152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache->time_in = sched_clock(); 579152cef40a808d3034e383465b3f7d6783613e458Huang Ying return cache; 580152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 581152cef40a808d3034e383465b3f7d6783613e458Huang Ying 582152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_free(struct ghes_estatus_cache *cache) 583152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 584152cef40a808d3034e383465b3f7d6783613e458Huang Ying u32 len; 585152cef40a808d3034e383465b3f7d6783613e458Huang Ying 58688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); 587152cef40a808d3034e383465b3f7d6783613e458Huang Ying len = GHES_ESTATUS_CACHE_LEN(len); 588152cef40a808d3034e383465b3f7d6783613e458Huang Ying gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); 589152cef40a808d3034e383465b3f7d6783613e458Huang Ying atomic_dec(&ghes_estatus_cache_alloced); 590152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 591152cef40a808d3034e383465b3f7d6783613e458Huang Ying 592152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_rcu_free(struct rcu_head *head) 593152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 594152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache; 595152cef40a808d3034e383465b3f7d6783613e458Huang Ying 596152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = container_of(head, struct ghes_estatus_cache, rcu); 597152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_free(cache); 598152cef40a808d3034e383465b3f7d6783613e458Huang Ying} 599152cef40a808d3034e383465b3f7d6783613e458Huang Ying 600152cef40a808d3034e383465b3f7d6783613e458Huang Yingstatic void ghes_estatus_cache_add( 601152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic, 6020a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *estatus) 603152cef40a808d3034e383465b3f7d6783613e458Huang Ying{ 604152cef40a808d3034e383465b3f7d6783613e458Huang Ying int i, slot = -1, count; 605152cef40a808d3034e383465b3f7d6783613e458Huang Ying unsigned long long now, duration, period, max_period = 0; 606152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache; 607152cef40a808d3034e383465b3f7d6783613e458Huang Ying 608152cef40a808d3034e383465b3f7d6783613e458Huang Ying new_cache = ghes_estatus_cache_alloc(generic, estatus); 609152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (new_cache == NULL) 610152cef40a808d3034e383465b3f7d6783613e458Huang Ying return; 611152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_lock(); 612152cef40a808d3034e383465b3f7d6783613e458Huang Ying now = sched_clock(); 613152cef40a808d3034e383465b3f7d6783613e458Huang Ying for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { 614152cef40a808d3034e383465b3f7d6783613e458Huang Ying cache = rcu_dereference(ghes_estatus_caches[i]); 615152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (cache == NULL) { 616152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 617152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = NULL; 618152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 619152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 620152cef40a808d3034e383465b3f7d6783613e458Huang Ying duration = now - cache->time_in; 621152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) { 622152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 623152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = cache; 624152cef40a808d3034e383465b3f7d6783613e458Huang Ying break; 625152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 626152cef40a808d3034e383465b3f7d6783613e458Huang Ying count = atomic_read(&cache->count); 62770cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown period = duration; 62870cb6e1da00db6c9212e6fd69bd96fd41c797077Len Brown do_div(period, (count + 1)); 629152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (period > max_period) { 630152cef40a808d3034e383465b3f7d6783613e458Huang Ying max_period = period; 631152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot = i; 632152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache = cache; 633152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 634152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 635152cef40a808d3034e383465b3f7d6783613e458Huang Ying /* new_cache must be put into array after its contents are written */ 636152cef40a808d3034e383465b3f7d6783613e458Huang Ying smp_wmb(); 637152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (slot != -1 && cmpxchg(ghes_estatus_caches + slot, 638152cef40a808d3034e383465b3f7d6783613e458Huang Ying slot_cache, new_cache) == slot_cache) { 639152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (slot_cache) 640152cef40a808d3034e383465b3f7d6783613e458Huang Ying call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free); 641152cef40a808d3034e383465b3f7d6783613e458Huang Ying } else 642152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_free(new_cache); 643152cef40a808d3034e383465b3f7d6783613e458Huang Ying rcu_read_unlock(); 644d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 645d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 646d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes) 647d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 648d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 649d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 650d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = ghes_read_estatus(ghes, 0); 651d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 652d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto out; 653152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!ghes_estatus_cached(ghes->estatus)) { 654152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus)) 655152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_add(ghes->generic, ghes->estatus); 656152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 65721480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab ghes_do_proc(ghes, ghes->estatus); 658d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout: 659d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_clear_estatus(ghes); 660d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 661d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 662d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 66381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes) 66481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 66581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct acpi_hest_generic *g = ghes->generic; 66681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long expire; 66781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 66881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!g->notify.poll_interval) { 66981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n", 67081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying g->header.source_id); 67181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return; 67281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 67381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying expire = jiffies + msecs_to_jiffies(g->notify.poll_interval); 67481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.expires = round_jiffies_relative(expire); 67581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying add_timer(&ghes->timer); 67681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 67781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 67881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data) 67981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 68081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = (void *)data; 68181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 68281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_proc(ghes); 68381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_EXITING)) 68481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 68581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 68681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 68781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data) 68881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 68981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = data; 69081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 69181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 69281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_proc(ghes); 69381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 69481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_NONE; 69581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 69681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_HANDLED; 69781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 69881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 699d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this, 700d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long event, void *data) 701d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 702d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 703d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int ret = NOTIFY_DONE; 704d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 705d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_lock(); 706d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_for_each_entry_rcu(ghes, &ghes_sci, list) { 707d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes_proc(ghes)) 708d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ret = NOTIFY_OK; 709d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 710d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_unlock(); 711d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 712d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ret; 713d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 714d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 71544a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic struct notifier_block ghes_notifier_sci = { 71644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki .notifier_call = ghes_notify_sci, 71744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki}; 71844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 71944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#ifdef CONFIG_HAVE_ACPI_APEI_NMI 72044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki/* 72144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * printk is not safe in NMI context. So in NMI handler, we allocate 72244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * required memory from lock-less memory allocator 72344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * (ghes_estatus_pool), save estatus into it, put them into lock-less 72444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * list (ghes_estatus_llist), then delay printk into IRQ context via 72544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record 72644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * required pool size by all NMI error source. 72744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki */ 72844a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic struct llist_head ghes_estatus_llist; 72944a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic struct irq_work ghes_proc_irq_work; 73044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 73144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki/* 73244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * NMI may be triggered on any CPU, so ghes_nmi_lock is used for 73344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * mutual exclusion. 73444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki */ 73544a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock); 73644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 73744a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic LIST_HEAD(ghes_nmi); 73844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 73944a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic int ghes_panic_timeout __read_mostly = 30; 74044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 74146d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic struct llist_node *llist_nodes_reverse(struct llist_node *llnode) 74246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{ 74346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct llist_node *next, *tail = NULL; 74446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 74546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying while (llnode) { 74646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying next = llnode->next; 74746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode->next = tail; 74846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying tail = llnode; 74946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = next; 75046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying } 75146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 75246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying return tail; 75346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying} 75446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 75567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic void ghes_proc_in_irq(struct irq_work *irq_work) 75667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 75746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct llist_node *llnode, *next; 75867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ghes_estatus_node *estatus_node; 759152cef40a808d3034e383465b3f7d6783613e458Huang Ying struct acpi_hest_generic *generic; 7600a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *estatus; 76167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying u32 len, node_len; 76267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 76346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_del_all(&ghes_estatus_llist); 76467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying /* 76567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * Because the time order of estatus in list is reversed, 76667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying * revert it back to proper order. 76767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying */ 76846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_nodes_reverse(llnode); 76967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying while (llnode) { 77067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying next = llnode->next; 77167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node = llist_entry(llnode, struct ghes_estatus_node, 77267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode); 77367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 77488f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(estatus); 77567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 77621480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab ghes_do_proc(estatus_node->ghes, estatus); 777152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (!ghes_estatus_cached(estatus)) { 778152cef40a808d3034e383465b3f7d6783613e458Huang Ying generic = estatus_node->generic; 779152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_print_estatus(NULL, generic, estatus)) 780152cef40a808d3034e383465b3f7d6783613e458Huang Ying ghes_estatus_cache_add(generic, estatus); 781152cef40a808d3034e383465b3f7d6783613e458Huang Ying } 78267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, 78367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len); 78467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llnode = next; 78567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 78667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 78767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 78846d12f0bcb17b2de89a059114349d472b7eb1783Huang Yingstatic void ghes_print_queued_estatus(void) 78946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying{ 79046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct llist_node *llnode; 79146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct ghes_estatus_node *estatus_node; 79246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying struct acpi_hest_generic *generic; 7930a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *estatus; 79446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying u32 len, node_len; 79546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 79646d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_del_all(&ghes_estatus_llist); 79746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying /* 79846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying * Because the time order of estatus in list is reversed, 79946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying * revert it back to proper order. 80046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying */ 80146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llist_nodes_reverse(llnode); 80246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying while (llnode) { 80346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying estatus_node = llist_entry(llnode, struct ghes_estatus_node, 80446d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode); 80546d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 80688f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(estatus); 80746d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 80846d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying generic = estatus_node->generic; 80946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying ghes_print_estatus(NULL, generic, estatus); 81046d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying llnode = llnode->next; 81146d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying } 81246d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying} 81346d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying 8149c48f1c629ecfa114850c03f875c6691003214deDon Zickusstatic int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) 81581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 81681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes, *ghes_global = NULL; 81781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int sev, sev_global = -1; 8189c48f1c629ecfa114850c03f875c6691003214deDon Zickus int ret = NMI_DONE; 81981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 82081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_nmi_lock); 82181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 82281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (ghes_read_estatus(ghes, 1)) { 82381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 82481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 82581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 82681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev = ghes_severity(ghes->estatus->error_severity); 82781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev > sev_global) { 82881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev_global = sev; 82981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_global = ghes; 83081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 8319c48f1c629ecfa114850c03f875c6691003214deDon Zickus ret = NMI_HANDLED; 83281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 83381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 8349c48f1c629ecfa114850c03f875c6691003214deDon Zickus if (ret == NMI_DONE) 83581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto out; 83681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 83781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev_global >= GHES_SEV_PANIC) { 83881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying oops_begin(); 83946d12f0bcb17b2de89a059114349d472b7eb1783Huang Ying ghes_print_queued_estatus(); 8405ba82ab534a325d310fe02af1c149f1072792c7bHuang Ying __ghes_print_estatus(KERN_EMERG, ghes_global->generic, 84167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_global->estatus); 84281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* reboot to log the error! */ 84381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (panic_timeout == 0) 84481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic_timeout = ghes_panic_timeout; 84581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic("Fatal hardware error!"); 84681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 84781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 84881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 84967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 85067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying u32 len, node_len; 85167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying struct ghes_estatus_node *estatus_node; 8520a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng struct acpi_hest_generic_status *estatus; 85367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 85481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 85581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 85667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 857152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (ghes_estatus_cached(ghes->estatus)) 858152cef40a808d3034e383465b3f7d6783613e458Huang Ying goto next; 85967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying /* Save estatus for further processing in IRQ context */ 86088f074f4871a8c212b212b725e4dcdcdb09613c1Chen, Gong len = cper_estatus_len(ghes->estatus); 86167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len = GHES_ESTATUS_NODE_LEN(len); 86267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, 86367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying node_len); 86467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (estatus_node) { 86521480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab estatus_node->ghes = ghes; 86667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus_node->generic = ghes->generic; 86767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 86867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying memcpy(estatus, ghes->estatus, len); 86967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying llist_add(&estatus_node->llnode, &ghes_estatus_llist); 87067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying } 871152cef40a808d3034e383465b3f7d6783613e458Huang Yingnext: 87267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 87381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 87481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 87567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 87667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying irq_work_queue(&ghes_proc_irq_work); 87767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying#endif 87881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 87981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout: 88081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_nmi_lock); 88181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return ret; 88281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 88381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 88467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingstatic unsigned long ghes_esource_prealloc_size( 88567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying const struct acpi_hest_generic *generic) 88667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying{ 88767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying unsigned long block_length, prealloc_records, prealloc_size; 88867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 88967eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying block_length = min_t(unsigned long, generic->error_block_length, 89067eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying GHES_ESTATUS_MAX_SIZE); 89167eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying prealloc_records = max_t(unsigned long, 89267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying generic->records_to_preallocate, 1); 89367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying prealloc_size = min_t(unsigned long, block_length * prealloc_records, 89467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying GHES_ESOURCE_PREALLOC_MAX_SIZE); 89567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 89667eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying return prealloc_size; 89767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying} 89867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 89944a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_estatus_pool_shrink(unsigned long len) 90044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 90144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes_estatus_pool_size_request -= PAGE_ALIGN(len); 90244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 90344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 90444a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_nmi_add(struct ghes *ghes) 90544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 90644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki unsigned long len; 90744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 90844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki len = ghes_esource_prealloc_size(ghes->generic); 90944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes_estatus_pool_expand(len); 91044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki mutex_lock(&ghes_list_mutex); 91144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki if (list_empty(&ghes_nmi)) 91244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); 91344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki list_add_rcu(&ghes->list, &ghes_nmi); 91444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki mutex_unlock(&ghes_list_mutex); 91544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 91644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 91744a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_nmi_remove(struct ghes *ghes) 91844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 91944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki unsigned long len; 92044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 92144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki mutex_lock(&ghes_list_mutex); 92244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki list_del_rcu(&ghes->list); 92344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki if (list_empty(&ghes_nmi)) 92444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki unregister_nmi_handler(NMI_LOCAL, "ghes"); 92544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki mutex_unlock(&ghes_list_mutex); 92644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki /* 92744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * To synchronize with NMI handler, ghes can only be 92844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki * freed after NMI handler finishes. 92944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki */ 93044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki synchronize_rcu(); 93144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki len = ghes_esource_prealloc_size(ghes->generic); 93244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes_estatus_pool_shrink(len); 93344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 93444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 93544a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic void ghes_nmi_init_cxt(void) 93644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 93744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); 93844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 93944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#else /* CONFIG_HAVE_ACPI_APEI_NMI */ 94044a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic inline void ghes_nmi_add(struct ghes *ghes) 94144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 94244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", 94344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes->generic->header.source_id); 94444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki BUG(); 94544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 94644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 94744a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic inline void ghes_nmi_remove(struct ghes *ghes) 94844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 94944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", 95044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes->generic->header.source_id); 95144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki BUG(); 95244a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 95344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 95444a69f6195628f6f940566d133a72987559e102dTomasz Nowickistatic inline void ghes_nmi_init_cxt(void) 95544a69f6195628f6f940566d133a72987559e102dTomasz Nowicki{ 95644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki} 95744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki#endif /* CONFIG_HAVE_ACPI_APEI_NMI */ 95844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 959da095fd3d5063f2dd03468d71f7df39a0430d86fBill Pembertonstatic int ghes_probe(struct platform_device *ghes_dev) 960d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 961d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 962d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes = NULL; 96344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki 9647ad6e9435596f692ff65f399da12816c94960185Huang Ying int rc = -EINVAL; 965d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9661dd6b20e368765223c31569d364219785b24700bJin Dongming generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; 967d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!generic->enabled) 9687ad6e9435596f692ff65f399da12816c94960185Huang Ying return -ENODEV; 969d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 97081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 97181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 97281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 97381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 97444a69f6195628f6f940566d133a72987559e102dTomasz Nowicki break; 97581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 97644a69f6195628f6f940566d133a72987559e102dTomasz Nowicki if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) { 97744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n", 97844a69f6195628f6f940566d133a72987559e102dTomasz Nowicki generic->header.source_id); 97944a69f6195628f6f940566d133a72987559e102dTomasz Nowicki goto err; 98044a69f6195628f6f940566d133a72987559e102dTomasz Nowicki } 98181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 98281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_LOCAL: 98381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", 984d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 985d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 98681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 98781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", 98881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->notify.type, generic->header.source_id); 98981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 990d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 99181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 99281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = -EIO; 99381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (generic->error_block_length < 9940a00fd5e20fd5dc89e976e163588d7c54edaf745Lv Zheng sizeof(struct acpi_hest_generic_status)) { 99581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", 99681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->error_block_length, 997d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 998d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 999d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 1000d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = ghes_new(generic); 1001d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (IS_ERR(ghes)) { 1002d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = PTR_ERR(ghes); 1003d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = NULL; 1004d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 1005d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 100621480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab 100721480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab rc = ghes_edac_register(ghes, &ghes_dev->dev); 100821480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab if (rc < 0) 100921480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab goto err; 101021480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab 101181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 101281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 101381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.function = ghes_poll_func; 101481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.data = (unsigned long)ghes; 101581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying init_timer_deferrable(&ghes->timer); 101681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 101781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 101881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 101981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* External interrupt vector is GSI */ 1020a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun rc = acpi_gsi_to_irq(generic->notify.vector, &ghes->irq); 1021a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun if (rc) { 102281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", 102381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 102421480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab goto err_edac_unreg; 102581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 1026a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun rc = request_irq(ghes->irq, ghes_irq_func, 0, "GHES IRQ", ghes); 1027a98d4f64a20b2b88697e7e08c871144a7e3f0ec4Wei Yongjun if (rc) { 102881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", 102981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 103021480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab goto err_edac_unreg; 103181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 103281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 103381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 10347ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 1035d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (list_empty(&ghes_sci)) 1036d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying register_acpi_hed_notifier(&ghes_notifier_sci); 1037d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_add_rcu(&ghes->list, &ghes_sci); 10387ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 103981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 104081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 104144a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes_nmi_add(ghes); 104281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 104381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 104481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG(); 1045d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 10467ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, ghes); 1047d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1048d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 104921480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehaberr_edac_unreg: 105021480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab ghes_edac_unregister(ghes); 1051d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr: 10527ad6e9435596f692ff65f399da12816c94960185Huang Ying if (ghes) { 1053d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_fini(ghes); 10547ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 10557ad6e9435596f692ff65f399da12816c94960185Huang Ying } 1056d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 1057d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1058d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1059b59bc2fbb4bb67e486c40cdb6a306c06acbaec06Bill Pembertonstatic int ghes_remove(struct platform_device *ghes_dev) 1060d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 10617ad6e9435596f692ff65f399da12816c94960185Huang Ying struct ghes *ghes; 10627ad6e9435596f692ff65f399da12816c94960185Huang Ying struct acpi_hest_generic *generic; 1063d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10647ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes = platform_get_drvdata(ghes_dev); 10657ad6e9435596f692ff65f399da12816c94960185Huang Ying generic = ghes->generic; 10667ad6e9435596f692ff65f399da12816c94960185Huang Ying 106781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->flags |= GHES_EXITING; 10687ad6e9435596f692ff65f399da12816c94960185Huang Ying switch (generic->notify.type) { 106981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 107081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying del_timer_sync(&ghes->timer); 107181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 107281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 107381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_irq(ghes->irq, ghes); 107481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 10757ad6e9435596f692ff65f399da12816c94960185Huang Ying case ACPI_HEST_NOTIFY_SCI: 10767ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 10777ad6e9435596f692ff65f399da12816c94960185Huang Ying list_del_rcu(&ghes->list); 10787ad6e9435596f692ff65f399da12816c94960185Huang Ying if (list_empty(&ghes_sci)) 10797ad6e9435596f692ff65f399da12816c94960185Huang Ying unregister_acpi_hed_notifier(&ghes_notifier_sci); 10807ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 10817ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 108281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 108344a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes_nmi_remove(ghes); 108481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 10857ad6e9435596f692ff65f399da12816c94960185Huang Ying default: 10867ad6e9435596f692ff65f399da12816c94960185Huang Ying BUG(); 10877ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 10887ad6e9435596f692ff65f399da12816c94960185Huang Ying } 1089d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10907ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes_fini(ghes); 109121480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab 109221480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab ghes_edac_unregister(ghes); 109321480547c8b85be6c08c4d77ed514673b73eda8aMauro Carvalho Chehab 10947ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 1095d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 10967ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, NULL); 10977ad6e9435596f692ff65f399da12816c94960185Huang Ying 10987ad6e9435596f692ff65f399da12816c94960185Huang Ying return 0; 1099d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1100d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 11017ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = { 11027ad6e9435596f692ff65f399da12816c94960185Huang Ying .driver = { 11037ad6e9435596f692ff65f399da12816c94960185Huang Ying .name = "GHES", 11047ad6e9435596f692ff65f399da12816c94960185Huang Ying .owner = THIS_MODULE, 11057ad6e9435596f692ff65f399da12816c94960185Huang Ying }, 11067ad6e9435596f692ff65f399da12816c94960185Huang Ying .probe = ghes_probe, 11077ad6e9435596f692ff65f399da12816c94960185Huang Ying .remove = ghes_remove, 11087ad6e9435596f692ff65f399da12816c94960185Huang Ying}; 11097ad6e9435596f692ff65f399da12816c94960185Huang Ying 1110d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void) 1111d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 111281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 111381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 1114d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (acpi_disabled) 1115d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENODEV; 1116d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1117d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (hest_disable) { 1118d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_info(GHES_PFX "HEST is not enabled!\n"); 1119d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EINVAL; 1120d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 1121d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1122b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying if (ghes_disable) { 1123b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying pr_info(GHES_PFX "GHES is not enabled!\n"); 1124b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying return -EINVAL; 1125b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying } 1126b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 112744a69f6195628f6f940566d133a72987559e102dTomasz Nowicki ghes_nmi_init_cxt(); 112867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 112981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_ioremap_init(); 113081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 113181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 113281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 113367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying rc = ghes_estatus_pool_init(); 113481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 113581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err_ioremap_exit; 113681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 1137152cef40a808d3034e383465b3f7d6783613e458Huang Ying rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * 1138152cef40a808d3034e383465b3f7d6783613e458Huang Ying GHES_ESTATUS_CACHE_ALLOCED_MAX); 1139152cef40a808d3034e383465b3f7d6783613e458Huang Ying if (rc) 1140152cef40a808d3034e383465b3f7d6783613e458Huang Ying goto err_pool_exit; 1141152cef40a808d3034e383465b3f7d6783613e458Huang Ying 114267eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying rc = platform_driver_register(&ghes_platform_driver); 114367eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying if (rc) 114467eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying goto err_pool_exit; 114567eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying 11469fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying rc = apei_osc_setup(); 11479fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying if (rc == 0 && osc_sb_apei_support_acked) 11489fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n"); 11499fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc == 0 && !osc_sb_apei_support_acked) 11509fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n"); 11519fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc && osc_sb_apei_support_acked) 11529fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); 11539fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else 11549fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); 11559fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying 115681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 115767eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Yingerr_pool_exit: 115867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_exit(); 115981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit: 116081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 116181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr: 116281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return rc; 1163d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1164d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1165d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void) 1166d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 11677ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_driver_unregister(&ghes_platform_driver); 116867eb2e99076708cc790019a6a08ca3e0ae130a3aHuang Ying ghes_estatus_pool_exit(); 116981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 1170d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 1171d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1172d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init); 1173d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit); 1174d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 1175d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying"); 1176d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); 1177d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL"); 11787ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES"); 1179