18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Extended Error Log driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Intel Corp. 68c2ecf20Sopenharmony_ci * Author: Chen, Gong <gong.chen@intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/cper.h> 128c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 138c2ecf20Sopenharmony_ci#include <linux/edac.h> 148c2ecf20Sopenharmony_ci#include <linux/ras.h> 158c2ecf20Sopenharmony_ci#include <acpi/ghes.h> 168c2ecf20Sopenharmony_ci#include <asm/cpu.h> 178c2ecf20Sopenharmony_ci#include <asm/mce.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "apei/apei-internal.h" 208c2ecf20Sopenharmony_ci#include <ras/ras_event.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define EXT_ELOG_ENTRY_MASK GENMASK_ULL(51, 0) /* elog entry address mask */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define EXTLOG_DSM_REV 0x0 258c2ecf20Sopenharmony_ci#define EXTLOG_FN_ADDR 0x1 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define FLAG_OS_OPTIN BIT(0) 288c2ecf20Sopenharmony_ci#define ELOG_ENTRY_VALID (1ULL<<63) 298c2ecf20Sopenharmony_ci#define ELOG_ENTRY_LEN 0x1000 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define EMCA_BUG \ 328c2ecf20Sopenharmony_ci "Can not request iomem region <0x%016llx-0x%016llx> - eMCA disabled\n" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct extlog_l1_head { 358c2ecf20Sopenharmony_ci u32 ver; /* Header Version */ 368c2ecf20Sopenharmony_ci u32 hdr_len; /* Header Length */ 378c2ecf20Sopenharmony_ci u64 total_len; /* entire L1 Directory length including this header */ 388c2ecf20Sopenharmony_ci u64 elog_base; /* MCA Error Log Directory base address */ 398c2ecf20Sopenharmony_ci u64 elog_len; /* MCA Error Log Directory length */ 408c2ecf20Sopenharmony_ci u32 flags; /* bit 0 - OS/VMM Opt-in */ 418c2ecf20Sopenharmony_ci u8 rev0[12]; 428c2ecf20Sopenharmony_ci u32 entries; /* Valid L1 Directory entries per logical processor */ 438c2ecf20Sopenharmony_ci u8 rev1[12]; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic u8 extlog_dsm_uuid[] __initdata = "663E35AF-CC10-41A4-88EA-5470AF055295"; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* L1 table related physical address */ 498c2ecf20Sopenharmony_cistatic u64 elog_base; 508c2ecf20Sopenharmony_cistatic size_t elog_size; 518c2ecf20Sopenharmony_cistatic u64 l1_dirbase; 528c2ecf20Sopenharmony_cistatic size_t l1_size; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* L1 table related virtual address */ 558c2ecf20Sopenharmony_cistatic void __iomem *extlog_l1_addr; 568c2ecf20Sopenharmony_cistatic void __iomem *elog_addr; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void *elog_buf; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic u64 *l1_entry_base; 618c2ecf20Sopenharmony_cistatic u32 l1_percpu_entry; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define ELOG_IDX(cpu, bank) \ 648c2ecf20Sopenharmony_ci (cpu_physical_id(cpu) * l1_percpu_entry + (bank)) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define ELOG_ENTRY_DATA(idx) \ 678c2ecf20Sopenharmony_ci (*(l1_entry_base + (idx))) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define ELOG_ENTRY_ADDR(phyaddr) \ 708c2ecf20Sopenharmony_ci (phyaddr - elog_base + (u8 *)elog_addr) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct acpi_hest_generic_status *extlog_elog_entry_check(int cpu, int bank) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci int idx; 758c2ecf20Sopenharmony_ci u64 data; 768c2ecf20Sopenharmony_ci struct acpi_hest_generic_status *estatus; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci WARN_ON(cpu < 0); 798c2ecf20Sopenharmony_ci idx = ELOG_IDX(cpu, bank); 808c2ecf20Sopenharmony_ci data = ELOG_ENTRY_DATA(idx); 818c2ecf20Sopenharmony_ci if ((data & ELOG_ENTRY_VALID) == 0) 828c2ecf20Sopenharmony_ci return NULL; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci data &= EXT_ELOG_ENTRY_MASK; 858c2ecf20Sopenharmony_ci estatus = (struct acpi_hest_generic_status *)ELOG_ENTRY_ADDR(data); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* if no valid data in elog entry, just return */ 888c2ecf20Sopenharmony_ci if (estatus->block_status == 0) 898c2ecf20Sopenharmony_ci return NULL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return estatus; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void __print_extlog_rcd(const char *pfx, 958c2ecf20Sopenharmony_ci struct acpi_hest_generic_status *estatus, int cpu) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci static atomic_t seqno; 988c2ecf20Sopenharmony_ci unsigned int curr_seqno; 998c2ecf20Sopenharmony_ci char pfx_seq[64]; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (!pfx) { 1028c2ecf20Sopenharmony_ci if (estatus->error_severity <= CPER_SEV_CORRECTED) 1038c2ecf20Sopenharmony_ci pfx = KERN_INFO; 1048c2ecf20Sopenharmony_ci else 1058c2ecf20Sopenharmony_ci pfx = KERN_ERR; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci curr_seqno = atomic_inc_return(&seqno); 1088c2ecf20Sopenharmony_ci snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}", pfx, curr_seqno); 1098c2ecf20Sopenharmony_ci printk("%s""Hardware error detected on CPU%d\n", pfx_seq, cpu); 1108c2ecf20Sopenharmony_ci cper_estatus_print(pfx_seq, estatus); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int print_extlog_rcd(const char *pfx, 1148c2ecf20Sopenharmony_ci struct acpi_hest_generic_status *estatus, int cpu) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci /* Not more than 2 messages every 5 seconds */ 1178c2ecf20Sopenharmony_ci static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); 1188c2ecf20Sopenharmony_ci static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); 1198c2ecf20Sopenharmony_ci struct ratelimit_state *ratelimit; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (estatus->error_severity == CPER_SEV_CORRECTED || 1228c2ecf20Sopenharmony_ci (estatus->error_severity == CPER_SEV_INFORMATIONAL)) 1238c2ecf20Sopenharmony_ci ratelimit = &ratelimit_corrected; 1248c2ecf20Sopenharmony_ci else 1258c2ecf20Sopenharmony_ci ratelimit = &ratelimit_uncorrected; 1268c2ecf20Sopenharmony_ci if (__ratelimit(ratelimit)) { 1278c2ecf20Sopenharmony_ci __print_extlog_rcd(pfx, estatus, cpu); 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 1; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int extlog_print(struct notifier_block *nb, unsigned long val, 1358c2ecf20Sopenharmony_ci void *data) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct mce *mce = (struct mce *)data; 1388c2ecf20Sopenharmony_ci int bank = mce->bank; 1398c2ecf20Sopenharmony_ci int cpu = mce->extcpu; 1408c2ecf20Sopenharmony_ci struct acpi_hest_generic_status *estatus, *tmp; 1418c2ecf20Sopenharmony_ci struct acpi_hest_generic_data *gdata; 1428c2ecf20Sopenharmony_ci const guid_t *fru_id; 1438c2ecf20Sopenharmony_ci char *fru_text; 1448c2ecf20Sopenharmony_ci guid_t *sec_type; 1458c2ecf20Sopenharmony_ci static u32 err_seq; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci estatus = extlog_elog_entry_check(cpu, bank); 1488c2ecf20Sopenharmony_ci if (!estatus) 1498c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (mce->kflags & MCE_HANDLED_CEC) { 1528c2ecf20Sopenharmony_ci estatus->block_status = 0; 1538c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN); 1578c2ecf20Sopenharmony_ci /* clear record status to enable BIOS to update it again */ 1588c2ecf20Sopenharmony_ci estatus->block_status = 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci tmp = (struct acpi_hest_generic_status *)elog_buf; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (!ras_userspace_consumers()) { 1638c2ecf20Sopenharmony_ci print_extlog_rcd(NULL, tmp, cpu); 1648c2ecf20Sopenharmony_ci goto out; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* log event via trace */ 1688c2ecf20Sopenharmony_ci err_seq++; 1698c2ecf20Sopenharmony_ci apei_estatus_for_each_section(tmp, gdata) { 1708c2ecf20Sopenharmony_ci if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 1718c2ecf20Sopenharmony_ci fru_id = (guid_t *)gdata->fru_id; 1728c2ecf20Sopenharmony_ci else 1738c2ecf20Sopenharmony_ci fru_id = &guid_null; 1748c2ecf20Sopenharmony_ci if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 1758c2ecf20Sopenharmony_ci fru_text = gdata->fru_text; 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci fru_text = ""; 1788c2ecf20Sopenharmony_ci sec_type = (guid_t *)gdata->section_type; 1798c2ecf20Sopenharmony_ci if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 1808c2ecf20Sopenharmony_ci struct cper_sec_mem_err *mem = (void *)(gdata + 1); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (gdata->error_data_length >= sizeof(*mem)) 1838c2ecf20Sopenharmony_ci trace_extlog_mem_event(mem, err_seq, fru_id, fru_text, 1848c2ecf20Sopenharmony_ci (u8)gdata->error_severity); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciout: 1898c2ecf20Sopenharmony_ci mce->kflags |= MCE_HANDLED_EXTLOG; 1908c2ecf20Sopenharmony_ci return NOTIFY_OK; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic bool __init extlog_get_l1addr(void) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci guid_t guid; 1968c2ecf20Sopenharmony_ci acpi_handle handle; 1978c2ecf20Sopenharmony_ci union acpi_object *obj; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (guid_parse(extlog_dsm_uuid, &guid)) 2008c2ecf20Sopenharmony_ci return false; 2018c2ecf20Sopenharmony_ci if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) 2028c2ecf20Sopenharmony_ci return false; 2038c2ecf20Sopenharmony_ci if (!acpi_check_dsm(handle, &guid, EXTLOG_DSM_REV, 1 << EXTLOG_FN_ADDR)) 2048c2ecf20Sopenharmony_ci return false; 2058c2ecf20Sopenharmony_ci obj = acpi_evaluate_dsm_typed(handle, &guid, EXTLOG_DSM_REV, 2068c2ecf20Sopenharmony_ci EXTLOG_FN_ADDR, NULL, ACPI_TYPE_INTEGER); 2078c2ecf20Sopenharmony_ci if (!obj) { 2088c2ecf20Sopenharmony_ci return false; 2098c2ecf20Sopenharmony_ci } else { 2108c2ecf20Sopenharmony_ci l1_dirbase = obj->integer.value; 2118c2ecf20Sopenharmony_ci ACPI_FREE(obj); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Spec says L1 directory must be 4K aligned, bail out if it isn't */ 2158c2ecf20Sopenharmony_ci if (l1_dirbase & ((1 << 12) - 1)) { 2168c2ecf20Sopenharmony_ci pr_warn(FW_BUG "L1 Directory is invalid at physical %llx\n", 2178c2ecf20Sopenharmony_ci l1_dirbase); 2188c2ecf20Sopenharmony_ci return false; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return true; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_cistatic struct notifier_block extlog_mce_dec = { 2248c2ecf20Sopenharmony_ci .notifier_call = extlog_print, 2258c2ecf20Sopenharmony_ci .priority = MCE_PRIO_EXTLOG, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int __init extlog_init(void) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct extlog_l1_head *l1_head; 2318c2ecf20Sopenharmony_ci void __iomem *extlog_l1_hdr; 2328c2ecf20Sopenharmony_ci size_t l1_hdr_size; 2338c2ecf20Sopenharmony_ci struct resource *r; 2348c2ecf20Sopenharmony_ci u64 cap; 2358c2ecf20Sopenharmony_ci int rc; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (rdmsrl_safe(MSR_IA32_MCG_CAP, &cap) || 2388c2ecf20Sopenharmony_ci !(cap & MCG_ELOG_P) || 2398c2ecf20Sopenharmony_ci !extlog_get_l1addr()) 2408c2ecf20Sopenharmony_ci return -ENODEV; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci rc = -EINVAL; 2438c2ecf20Sopenharmony_ci /* get L1 header to fetch necessary information */ 2448c2ecf20Sopenharmony_ci l1_hdr_size = sizeof(struct extlog_l1_head); 2458c2ecf20Sopenharmony_ci r = request_mem_region(l1_dirbase, l1_hdr_size, "L1 DIR HDR"); 2468c2ecf20Sopenharmony_ci if (!r) { 2478c2ecf20Sopenharmony_ci pr_warn(FW_BUG EMCA_BUG, 2488c2ecf20Sopenharmony_ci (unsigned long long)l1_dirbase, 2498c2ecf20Sopenharmony_ci (unsigned long long)l1_dirbase + l1_hdr_size); 2508c2ecf20Sopenharmony_ci goto err; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci extlog_l1_hdr = acpi_os_map_iomem(l1_dirbase, l1_hdr_size); 2548c2ecf20Sopenharmony_ci l1_head = (struct extlog_l1_head *)extlog_l1_hdr; 2558c2ecf20Sopenharmony_ci l1_size = l1_head->total_len; 2568c2ecf20Sopenharmony_ci l1_percpu_entry = l1_head->entries; 2578c2ecf20Sopenharmony_ci elog_base = l1_head->elog_base; 2588c2ecf20Sopenharmony_ci elog_size = l1_head->elog_len; 2598c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(extlog_l1_hdr, l1_hdr_size); 2608c2ecf20Sopenharmony_ci release_mem_region(l1_dirbase, l1_hdr_size); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* remap L1 header again based on completed information */ 2638c2ecf20Sopenharmony_ci r = request_mem_region(l1_dirbase, l1_size, "L1 Table"); 2648c2ecf20Sopenharmony_ci if (!r) { 2658c2ecf20Sopenharmony_ci pr_warn(FW_BUG EMCA_BUG, 2668c2ecf20Sopenharmony_ci (unsigned long long)l1_dirbase, 2678c2ecf20Sopenharmony_ci (unsigned long long)l1_dirbase + l1_size); 2688c2ecf20Sopenharmony_ci goto err; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci extlog_l1_addr = acpi_os_map_iomem(l1_dirbase, l1_size); 2718c2ecf20Sopenharmony_ci l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* remap elog table */ 2748c2ecf20Sopenharmony_ci r = request_mem_region(elog_base, elog_size, "Elog Table"); 2758c2ecf20Sopenharmony_ci if (!r) { 2768c2ecf20Sopenharmony_ci pr_warn(FW_BUG EMCA_BUG, 2778c2ecf20Sopenharmony_ci (unsigned long long)elog_base, 2788c2ecf20Sopenharmony_ci (unsigned long long)elog_base + elog_size); 2798c2ecf20Sopenharmony_ci goto err_release_l1_dir; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci elog_addr = acpi_os_map_iomem(elog_base, elog_size); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci rc = -ENOMEM; 2848c2ecf20Sopenharmony_ci /* allocate buffer to save elog record */ 2858c2ecf20Sopenharmony_ci elog_buf = kmalloc(ELOG_ENTRY_LEN, GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (elog_buf == NULL) 2878c2ecf20Sopenharmony_ci goto err_release_elog; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mce_register_decode_chain(&extlog_mce_dec); 2908c2ecf20Sopenharmony_ci /* enable OS to be involved to take over management from BIOS */ 2918c2ecf20Sopenharmony_ci ((struct extlog_l1_head *)extlog_l1_addr)->flags |= FLAG_OS_OPTIN; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cierr_release_elog: 2968c2ecf20Sopenharmony_ci if (elog_addr) 2978c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(elog_addr, elog_size); 2988c2ecf20Sopenharmony_ci release_mem_region(elog_base, elog_size); 2998c2ecf20Sopenharmony_cierr_release_l1_dir: 3008c2ecf20Sopenharmony_ci if (extlog_l1_addr) 3018c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(extlog_l1_addr, l1_size); 3028c2ecf20Sopenharmony_ci release_mem_region(l1_dirbase, l1_size); 3038c2ecf20Sopenharmony_cierr: 3048c2ecf20Sopenharmony_ci pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n"); 3058c2ecf20Sopenharmony_ci return rc; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void __exit extlog_exit(void) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci mce_unregister_decode_chain(&extlog_mce_dec); 3118c2ecf20Sopenharmony_ci if (extlog_l1_addr) { 3128c2ecf20Sopenharmony_ci ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; 3138c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(extlog_l1_addr, l1_size); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci if (elog_addr) 3168c2ecf20Sopenharmony_ci acpi_os_unmap_iomem(elog_addr, elog_size); 3178c2ecf20Sopenharmony_ci release_mem_region(elog_base, elog_size); 3188c2ecf20Sopenharmony_ci release_mem_region(l1_dirbase, l1_size); 3198c2ecf20Sopenharmony_ci kfree(elog_buf); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cimodule_init(extlog_init); 3238c2ecf20Sopenharmony_cimodule_exit(extlog_exit); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chen, Gong <gong.chen@intel.com>"); 3268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Extended MCA Error Log Driver"); 3278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328