ghes.c revision 9fb0bfe1408d5506b7b83d13d1eed573fd71d67d
1d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 2d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * APEI Generic Hardware Error Source support 3d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 4d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Generic Hardware Error Source provides a way to report platform 5d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * hardware errors (such as that from chipset). It works in so called 6d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * "Firmware First" mode, that is, hardware errors are reported to 7d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * firmware firstly, then reported to Linux by firmware. This way, 8d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * some non-standard hardware error registers or non-standard hardware 9d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * link can be checked by firmware to produce more hardware error 10d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * information for Linux. 11d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 12d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * For more information about Generic Hardware Error Source, please 13d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * refer to ACPI Specification version 4.0, section 17.3.2.6 14d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 15d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * Copyright 2010 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> 45d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/apei.h> 46d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/atomicio.h> 47d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <acpi/hed.h> 48d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include <asm/mce.h> 4981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#include <asm/tlbflush.h> 50d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 51d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#include "apei-internal.h" 52d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 53d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_PFX "GHES: " 54d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 55d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_ESTATUS_MAX_SIZE 65536 56d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 57d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 5881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * One struct ghes is created for each generic hardware error source. 59d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * It provides the context for APEI hardware error timer/IRQ/SCI/NMI 6081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler. 61d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 62d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * estatus: memory buffer for error status block, allocated during 63d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * HEST parsing. 64d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 65d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#define GHES_TO_CLEAR 0x0001 6681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_EXITING 0x0002 67d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 68d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstruct ghes { 69d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 70d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic_status *estatus; 71d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buffer_paddr; 72d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long flags; 7381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying union { 7481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct list_head list; 7581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct timer_list timer; 7681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned int irq; 7781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying }; 78d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 79d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 80b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingint ghes_disable; 81b6a9501658530d8b8374e37f1edb549039a8a260Huang Yingmodule_param_named(disable, ghes_disable, bool, 0); 82b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 8381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_panic_timeout __read_mostly = 30; 8481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 85d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying/* 8681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * All error sources notified with SCI shares one notifier function, 8781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * so they need to be linked and checked one by one. This is applied 8881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * to NMI too. 89d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying * 9081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * RCU is used for these lists, so ghes_list_mutex is only used for 9181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * list changing, not for traversing. 92d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying */ 93d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic LIST_HEAD(ghes_sci); 9481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic LIST_HEAD(ghes_nmi); 957ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic DEFINE_MUTEX(ghes_list_mutex); 96d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 9781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 9881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * NMI may be triggered on any CPU, so ghes_nmi_lock is used for 9981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * mutual exclusion. 10081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 10181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_nmi_lock); 10281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 10381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 10481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Because the memory area used to transfer hardware error information 10581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * from BIOS to Linux can be determined only in NMI, IRQ or timer 10681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * handler, but general ioremap can not be used in atomic context, so 10781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * a special version of atomic ioremap is implemented for that. 10881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 10981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 11081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 11181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * Two virtual pages are used, one for NMI context, the other for 11281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * IRQ/PROCESS context 11381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 11481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_PAGES 2 11581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_NMI_PAGE(base) (base) 11681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying#define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) 11781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 11881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* virtual memory area for atomic ioremap */ 11981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct vm_struct *ghes_ioremap_area; 12081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying/* 12181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * These 2 spinlock is used to prevent atomic ioremap virtual memory 12281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * area from being mapped simultaneously. 12381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 12481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); 12581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic DEFINE_SPINLOCK(ghes_ioremap_lock_irq); 12681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 12781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_ioremap_init(void) 12881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 12981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, 13081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying VM_IOREMAP, VMALLOC_START, VMALLOC_END); 13181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!ghes_ioremap_area) { 13281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); 13381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return -ENOMEM; 13481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 13581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 13681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 13781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 13881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 13981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_ioremap_exit(void) 14081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 14181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_vm_area(ghes_ioremap_area); 14281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 14381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 14481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) 14581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 14681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 14781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 14881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); 14981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 15081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 15181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 15381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 15481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void __iomem *ghes_ioremap_pfn_irq(u64 pfn) 15681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 15781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr; 15881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 15981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); 16081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ioremap_page_range(vaddr, vaddr + PAGE_SIZE, 16181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pfn << PAGE_SHIFT, PAGE_KERNEL); 16281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 16381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return (void __iomem *)vaddr; 16481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 16581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 16681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_nmi(void __iomem *vaddr_ptr) 16781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 16881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 16981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 17081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 17181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); 17281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 17381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying __flush_tlb_one(vaddr); 17481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 17581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 17681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_iounmap_irq(void __iomem *vaddr_ptr) 17781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 17881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long vaddr = (unsigned long __force)vaddr_ptr; 17981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void *base = ghes_ioremap_area->addr; 18081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 18181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); 18281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unmap_kernel_range_noflush(vaddr, PAGE_SIZE); 18381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying __flush_tlb_one(vaddr); 18481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 18581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 186d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct ghes *ghes_new(struct acpi_hest_generic *generic) 187d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 188d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 189d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned int error_block_length; 190d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 191d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 192d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = kzalloc(sizeof(*ghes), GFP_KERNEL); 193d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes) 194d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(-ENOMEM); 195d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->generic = generic; 196d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = acpi_pre_map_gar(&generic->error_status_address); 197d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 198d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_free; 199d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = generic->error_block_length; 200d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (error_block_length > GHES_ESTATUS_MAX_SIZE) { 201d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 202d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Error status block length is too long: %u for " 203d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "generic hardware error source: %d.\n", 204d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length, generic->header.source_id); 205d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying error_block_length = GHES_ESTATUS_MAX_SIZE; 206d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 207d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus = kmalloc(error_block_length, GFP_KERNEL); 208d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus) { 209d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -ENOMEM; 210d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_unmap; 211d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 212d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 213d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ghes; 214d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 215d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_unmap: 216d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying acpi_post_unmap_gar(&generic->error_status_address); 217d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_free: 218d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes); 219d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ERR_PTR(rc); 220d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 221d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 222d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_fini(struct ghes *ghes) 223d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 224d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying kfree(ghes->estatus); 225d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying acpi_post_unmap_gar(&ghes->generic->error_status_address); 226d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 227d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 228d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingenum { 229ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_NO = 0x0, 230ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_CORRECTED = 0x1, 231ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_RECOVERABLE = 0x2, 232ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying GHES_SEV_PANIC = 0x3, 233d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 234d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 235d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic inline int ghes_severity(int severity) 236d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 237d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying switch (severity) { 238ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_INFORMATIONAL: 239ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_NO; 240ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_CORRECTED: 241ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_CORRECTED; 242ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_RECOVERABLE: 243ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_RECOVERABLE; 244ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying case CPER_SEV_FATAL: 245ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 246d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying default: 24725985edcedea6396277003854657b5f3cb31a628Lucas De Marchi /* Unknown, go panic */ 248ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying return GHES_SEV_PANIC; 249d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 250d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 251d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 25281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, 25381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int from_phys) 254d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 25581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying void __iomem *vaddr; 25681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long flags = 0; 25781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int in_nmi = in_nmi(); 25881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u64 offset; 25981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying u32 trunk; 26081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 26181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying while (len > 0) { 26281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying offset = paddr - (paddr & PAGE_MASK); 26381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 26481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_ioremap_lock_nmi); 26581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); 26681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 26781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); 26881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); 26981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 27081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = PAGE_SIZE - offset; 27181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying trunk = min(trunk, len); 27281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (from_phys) 27381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_fromio(buffer, vaddr + offset, trunk); 27481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying else 27581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying memcpy_toio(vaddr + offset, buffer, trunk); 27681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len -= trunk; 27781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying paddr += trunk; 27881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buffer += trunk; 27981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (in_nmi) { 28081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_nmi(vaddr); 28181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_ioremap_lock_nmi); 28281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } else { 28381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_iounmap_irq(vaddr); 28481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); 28581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 28681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 287d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 288d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 289d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_read_estatus(struct ghes *ghes, int silent) 290d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 291d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *g = ghes->generic; 292d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u64 buf_paddr; 293d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying u32 len; 294d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 295d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 296d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); 297d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) { 298d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!silent && printk_ratelimit()) 299d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 300d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying"Failed to read error status block address for hardware error source: %d.\n", 301d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying g->header.source_id); 302d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EIO; 303d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 304d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!buf_paddr) 305d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 306d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 30781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, 30881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(*ghes->estatus), 1); 309d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes->estatus->block_status) 310d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENOENT; 311d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 312d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->buffer_paddr = buf_paddr; 313d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags |= GHES_TO_CLEAR; 314d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 315d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = -EIO; 316d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying len = apei_estatus_len(ghes->estatus); 317d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len < sizeof(*ghes->estatus)) 318d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 319d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (len > ghes->generic->error_block_length) 320d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 321d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (apei_estatus_check_header(ghes->estatus)) 322d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 32381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_copy_tofrom_phys(ghes->estatus + 1, 32481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying buf_paddr + sizeof(*ghes->estatus), 32581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying len - sizeof(*ghes->estatus), 1); 326d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (apei_estatus_check(ghes->estatus)) 327d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err_read_block; 328d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = 0; 329d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 330d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr_read_block: 33181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc && !silent && printk_ratelimit()) 332d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_warning(FW_WARN GHES_PFX 333d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying "Failed to read error status block!\n"); 334d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 335d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 336d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 337d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_clear_estatus(struct ghes *ghes) 338d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 339d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->estatus->block_status = 0; 340d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 341d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return; 342d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr, 343d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying sizeof(ghes->estatus->block_status), 0); 344d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes->flags &= ~GHES_TO_CLEAR; 345d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 346d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 347d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void ghes_do_proc(struct ghes *ghes) 348d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 349ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying int sev, processed = 0; 350d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic_data *gdata; 351d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 352ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying sev = ghes_severity(ghes->estatus->error_severity); 353d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying apei_estatus_for_each_section(ghes->estatus, gdata) { 354d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#ifdef CONFIG_X86_MCE 355d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 356d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying CPER_SEC_PLATFORM_MEM)) { 357d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying apei_mce_report_mem_error( 358ad4ecef2f13c790f95b55320f2925c205d8f971fHuang Ying sev == GHES_SEV_CORRECTED, 359d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying (struct cper_sec_mem_err *)(gdata+1)); 360d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying processed = 1; 361d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 362d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying#endif 363d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 36432c361f574f85fa47600d84900598e2efc99082eHuang Ying} 365d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 3665588340d46a484da53bbce8136184d9c7fbc259cHuang Yingstatic void __ghes_print_estatus(const char *pfx, struct ghes *ghes) 36732c361f574f85fa47600d84900598e2efc99082eHuang Ying{ 36832c361f574f85fa47600d84900598e2efc99082eHuang Ying if (pfx == NULL) { 36932c361f574f85fa47600d84900598e2efc99082eHuang Ying if (ghes_severity(ghes->estatus->error_severity) <= 37032c361f574f85fa47600d84900598e2efc99082eHuang Ying GHES_SEV_CORRECTED) 37132c361f574f85fa47600d84900598e2efc99082eHuang Ying pfx = KERN_WARNING HW_ERR; 37232c361f574f85fa47600d84900598e2efc99082eHuang Ying else 37332c361f574f85fa47600d84900598e2efc99082eHuang Ying pfx = KERN_ERR HW_ERR; 37432c361f574f85fa47600d84900598e2efc99082eHuang Ying } 3755588340d46a484da53bbce8136184d9c7fbc259cHuang Ying printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", 3765588340d46a484da53bbce8136184d9c7fbc259cHuang Ying pfx, ghes->generic->header.source_id); 3775588340d46a484da53bbce8136184d9c7fbc259cHuang Ying apei_estatus_print(pfx, ghes->estatus); 3785588340d46a484da53bbce8136184d9c7fbc259cHuang Ying} 3795588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 3805588340d46a484da53bbce8136184d9c7fbc259cHuang Yingstatic void ghes_print_estatus(const char *pfx, struct ghes *ghes) 3815588340d46a484da53bbce8136184d9c7fbc259cHuang Ying{ 3825588340d46a484da53bbce8136184d9c7fbc259cHuang Ying /* Not more than 2 messages every 5 seconds */ 3835588340d46a484da53bbce8136184d9c7fbc259cHuang Ying static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2); 3845588340d46a484da53bbce8136184d9c7fbc259cHuang Ying 3855588340d46a484da53bbce8136184d9c7fbc259cHuang Ying if (__ratelimit(&ratelimit)) 3865588340d46a484da53bbce8136184d9c7fbc259cHuang Ying __ghes_print_estatus(pfx, ghes); 387d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 388d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 389d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_proc(struct ghes *ghes) 390d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 391d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int rc; 392d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 393d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = ghes_read_estatus(ghes, 0); 394d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (rc) 395d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto out; 39632c361f574f85fa47600d84900598e2efc99082eHuang Ying ghes_print_estatus(NULL, ghes); 397d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_do_proc(ghes); 398d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 399d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingout: 400d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_clear_estatus(ghes); 401d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 402d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 403d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 40481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_add_timer(struct ghes *ghes) 40581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 40681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct acpi_hest_generic *g = ghes->generic; 40781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long expire; 40881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 40981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!g->notify.poll_interval) { 41081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n", 41181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying g->header.source_id); 41281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return; 41381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 41481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying expire = jiffies + msecs_to_jiffies(g->notify.poll_interval); 41581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.expires = round_jiffies_relative(expire); 41681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying add_timer(&ghes->timer); 41781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 41881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 41981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic void ghes_poll_func(unsigned long data) 42081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 42181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = (void *)data; 42281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 42381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_proc(ghes); 42481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_EXITING)) 42581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 42681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 42781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 42881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic irqreturn_t ghes_irq_func(int irq, void *data) 42981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 43081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes = data; 43181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 43281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 43381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_proc(ghes); 43481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 43581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_NONE; 43681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 43781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return IRQ_HANDLED; 43881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 43981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 440d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int ghes_notify_sci(struct notifier_block *this, 441d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying unsigned long event, void *data) 442d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 443d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes; 444d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying int ret = NOTIFY_DONE; 445d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 446d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_lock(); 447d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_for_each_entry_rcu(ghes, &ghes_sci, list) { 448d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!ghes_proc(ghes)) 449d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ret = NOTIFY_OK; 450d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 451d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rcu_read_unlock(); 452d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 453d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return ret; 454d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 455d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 45681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic int ghes_notify_nmi(struct notifier_block *this, 45781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unsigned long cmd, void *data) 45881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying{ 45981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying struct ghes *ghes, *ghes_global = NULL; 46081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int sev, sev_global = -1; 46181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int ret = NOTIFY_DONE; 46281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 46381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (cmd != DIE_NMI) 46481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return ret; 46581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 46681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_lock(&ghes_nmi_lock); 46781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 46881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (ghes_read_estatus(ghes, 1)) { 46981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 47081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 47181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 47281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev = ghes_severity(ghes->estatus->error_severity); 47381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev > sev_global) { 47481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sev_global = sev; 47581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_global = ghes; 47681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 47781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ret = NOTIFY_STOP; 47881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 47981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 48081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (ret == NOTIFY_DONE) 48181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto out; 48281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 48381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (sev_global >= GHES_SEV_PANIC) { 48481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying oops_begin(); 4855588340d46a484da53bbce8136184d9c7fbc259cHuang Ying __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global); 48681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* reboot to log the error! */ 48781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (panic_timeout == 0) 48881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic_timeout = ghes_panic_timeout; 48981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying panic("Fatal hardware error!"); 49081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 49181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 49281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_for_each_entry_rcu(ghes, &ghes_nmi, list) { 49381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (!(ghes->flags & GHES_TO_CLEAR)) 49481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying continue; 49581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* Do not print estatus because printk is not NMI safe */ 49681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_do_proc(ghes); 49781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_clear_estatus(ghes); 49881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 49981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 50081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingout: 50181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying raw_spin_unlock(&ghes_nmi_lock); 50281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return ret; 50381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying} 50481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 505d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic struct notifier_block ghes_notifier_sci = { 506d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying .notifier_call = ghes_notify_sci, 507d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying}; 508d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 50981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingstatic struct notifier_block ghes_notifier_nmi = { 51081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying .notifier_call = ghes_notify_nmi, 51181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying}; 51281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 5137ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devinit ghes_probe(struct platform_device *ghes_dev) 514d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 515d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct acpi_hest_generic *generic; 516d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying struct ghes *ghes = NULL; 5177ad6e9435596f692ff65f399da12816c94960185Huang Ying int rc = -EINVAL; 518d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 5191dd6b20e368765223c31569d364219785b24700bJin Dongming generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; 520d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (!generic->enabled) 5217ad6e9435596f692ff65f399da12816c94960185Huang Ying return -ENODEV; 522d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 52381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 52481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 52581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 52681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 52781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 52881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 52981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_LOCAL: 53081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", 531d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 532d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 53381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 53481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", 53581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->notify.type, generic->header.source_id); 53681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 537d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 53881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 53981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = -EIO; 54081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (generic->error_block_length < 54181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying sizeof(struct acpi_hest_generic_status)) { 54281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", 54381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->error_block_length, 544d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying generic->header.source_id); 545d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 546d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 547d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = ghes_new(generic); 548d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (IS_ERR(ghes)) { 549d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying rc = PTR_ERR(ghes); 550d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes = NULL; 551d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying goto err; 552d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 55381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying switch (generic->notify.type) { 55481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 55581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.function = ghes_poll_func; 55681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->timer.data = (unsigned long)ghes; 55781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying init_timer_deferrable(&ghes->timer); 55881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_add_timer(ghes); 55981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 56081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 56181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* External interrupt vector is GSI */ 56281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) { 56381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", 56481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 56581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 56681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 56781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (request_irq(ghes->irq, ghes_irq_func, 56881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 0, "GHES IRQ", ghes)) { 56981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", 57081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying generic->header.source_id); 57181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 57281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying } 57381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 57481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_SCI: 5757ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 576d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (list_empty(&ghes_sci)) 577d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying register_acpi_hed_notifier(&ghes_notifier_sci); 578d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying list_add_rcu(&ghes->list, &ghes_sci); 5797ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 58081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 58181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 58281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_lock(&ghes_list_mutex); 58381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (list_empty(&ghes_nmi)) 58481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying register_die_notifier(&ghes_notifier_nmi); 58581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_add_rcu(&ghes->list, &ghes_nmi); 58681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_unlock(&ghes_list_mutex); 58781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 58881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying default: 58981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying BUG(); 590d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 5917ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, ghes); 592d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 593d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return 0; 594d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingerr: 5957ad6e9435596f692ff65f399da12816c94960185Huang Ying if (ghes) { 596d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying ghes_fini(ghes); 5977ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 5987ad6e9435596f692ff65f399da12816c94960185Huang Ying } 599d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return rc; 600d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 601d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 6027ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic int __devexit ghes_remove(struct platform_device *ghes_dev) 603d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 6047ad6e9435596f692ff65f399da12816c94960185Huang Ying struct ghes *ghes; 6057ad6e9435596f692ff65f399da12816c94960185Huang Ying struct acpi_hest_generic *generic; 606d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 6077ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes = platform_get_drvdata(ghes_dev); 6087ad6e9435596f692ff65f399da12816c94960185Huang Ying generic = ghes->generic; 6097ad6e9435596f692ff65f399da12816c94960185Huang Ying 61081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes->flags |= GHES_EXITING; 6117ad6e9435596f692ff65f399da12816c94960185Huang Ying switch (generic->notify.type) { 61281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_POLLED: 61381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying del_timer_sync(&ghes->timer); 61481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 61581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_EXTERNAL: 61681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying free_irq(ghes->irq, ghes); 61781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 6187ad6e9435596f692ff65f399da12816c94960185Huang Ying case ACPI_HEST_NOTIFY_SCI: 6197ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_lock(&ghes_list_mutex); 6207ad6e9435596f692ff65f399da12816c94960185Huang Ying list_del_rcu(&ghes->list); 6217ad6e9435596f692ff65f399da12816c94960185Huang Ying if (list_empty(&ghes_sci)) 6227ad6e9435596f692ff65f399da12816c94960185Huang Ying unregister_acpi_hed_notifier(&ghes_notifier_sci); 6237ad6e9435596f692ff65f399da12816c94960185Huang Ying mutex_unlock(&ghes_list_mutex); 6247ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 62581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying case ACPI_HEST_NOTIFY_NMI: 62681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_lock(&ghes_list_mutex); 62781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying list_del_rcu(&ghes->list); 62881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (list_empty(&ghes_nmi)) 62981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying unregister_die_notifier(&ghes_notifier_nmi); 63081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying mutex_unlock(&ghes_list_mutex); 63181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying /* 63281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * To synchronize with NMI handler, ghes can only be 63381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying * freed after NMI handler finishes. 63481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying */ 63581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying synchronize_rcu(); 63681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying break; 6377ad6e9435596f692ff65f399da12816c94960185Huang Ying default: 6387ad6e9435596f692ff65f399da12816c94960185Huang Ying BUG(); 6397ad6e9435596f692ff65f399da12816c94960185Huang Ying break; 6407ad6e9435596f692ff65f399da12816c94960185Huang Ying } 641d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 6427ad6e9435596f692ff65f399da12816c94960185Huang Ying ghes_fini(ghes); 6437ad6e9435596f692ff65f399da12816c94960185Huang Ying kfree(ghes); 644d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 6457ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_set_drvdata(ghes_dev, NULL); 6467ad6e9435596f692ff65f399da12816c94960185Huang Ying 6477ad6e9435596f692ff65f399da12816c94960185Huang Ying return 0; 648d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 649d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 6507ad6e9435596f692ff65f399da12816c94960185Huang Yingstatic struct platform_driver ghes_platform_driver = { 6517ad6e9435596f692ff65f399da12816c94960185Huang Ying .driver = { 6527ad6e9435596f692ff65f399da12816c94960185Huang Ying .name = "GHES", 6537ad6e9435596f692ff65f399da12816c94960185Huang Ying .owner = THIS_MODULE, 6547ad6e9435596f692ff65f399da12816c94960185Huang Ying }, 6557ad6e9435596f692ff65f399da12816c94960185Huang Ying .probe = ghes_probe, 6567ad6e9435596f692ff65f399da12816c94960185Huang Ying .remove = ghes_remove, 6577ad6e9435596f692ff65f399da12816c94960185Huang Ying}; 6587ad6e9435596f692ff65f399da12816c94960185Huang Ying 659d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic int __init ghes_init(void) 660d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 66181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying int rc; 66281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 663d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (acpi_disabled) 664d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -ENODEV; 665d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 666d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying if (hest_disable) { 667d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying pr_info(GHES_PFX "HEST is not enabled!\n"); 668d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying return -EINVAL; 669d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying } 670d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 671b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying if (ghes_disable) { 672b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying pr_info(GHES_PFX "GHES is not enabled!\n"); 673b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying return -EINVAL; 674b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying } 675b6a9501658530d8b8374e37f1edb549039a8a260Huang Ying 67681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = ghes_ioremap_init(); 67781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 67881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err; 67981e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 68081e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying rc = platform_driver_register(&ghes_platform_driver); 68181e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying if (rc) 68281e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying goto err_ioremap_exit; 68381e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying 6849fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying rc = apei_osc_setup(); 6859fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying if (rc == 0 && osc_sb_apei_support_acked) 6869fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n"); 6879fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc == 0 && !osc_sb_apei_support_acked) 6889fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n"); 6899fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else if (rc && osc_sb_apei_support_acked) 6909fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); 6919fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying else 6929fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); 6939fb0bfe1408d5506b7b83d13d1eed573fd71d67dHuang Ying 69481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return 0; 69581e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr_ioremap_exit: 69681e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 69781e88fdc432a1552401d6e91a984dcccce72b8dcHuang Yingerr: 69881e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying return rc; 699d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 700d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 701d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingstatic void __exit ghes_exit(void) 702d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying{ 7037ad6e9435596f692ff65f399da12816c94960185Huang Ying platform_driver_unregister(&ghes_platform_driver); 70481e88fdc432a1552401d6e91a984dcccce72b8dcHuang Ying ghes_ioremap_exit(); 705d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying} 706d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 707d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_init(ghes_init); 708d334a49113a4a33109fd24e46073280ecd1bea0dHuang Yingmodule_exit(ghes_exit); 709d334a49113a4a33109fd24e46073280ecd1bea0dHuang Ying 710d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_AUTHOR("Huang Ying"); 711d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); 712d334a49113a4a33109fd24e46073280ecd1bea0dHuang YingMODULE_LICENSE("GPL"); 7137ad6e9435596f692ff65f399da12816c94960185Huang YingMODULE_ALIAS("platform:GHES"); 714