162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * apei-base.c - ACPI Platform Error Interface (APEI) supporting 462306a36Sopenharmony_ci * infrastructure 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * APEI allows to report errors (for example from the chipset) to 762306a36Sopenharmony_ci * the operating system. This improves NMI handling especially. In 862306a36Sopenharmony_ci * addition it supports error serialization and error injection. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * For more information about APEI, please refer to ACPI Specification 1162306a36Sopenharmony_ci * version 4.0, chapter 17. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This file has Common functions used by more than one APEI table, 1462306a36Sopenharmony_ci * including framework of interpreter for ERST and EINJ; resource 1562306a36Sopenharmony_ci * management for APEI registers. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Copyright (C) 2009, Intel Corp. 1862306a36Sopenharmony_ci * Author: Huang Ying <ying.huang@intel.com> 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/acpi.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/io.h> 2762306a36Sopenharmony_ci#include <linux/kref.h> 2862306a36Sopenharmony_ci#include <linux/interrupt.h> 2962306a36Sopenharmony_ci#include <linux/debugfs.h> 3062306a36Sopenharmony_ci#include <acpi/apei.h> 3162306a36Sopenharmony_ci#include <asm/unaligned.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "apei-internal.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define APEI_PFX "APEI: " 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * APEI ERST (Error Record Serialization Table) and EINJ (Error 3962306a36Sopenharmony_ci * INJection) interpreter framework. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define APEI_EXEC_PRESERVE_REGISTER 0x1 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_civoid apei_exec_ctx_init(struct apei_exec_context *ctx, 4562306a36Sopenharmony_ci struct apei_exec_ins_type *ins_table, 4662306a36Sopenharmony_ci u32 instructions, 4762306a36Sopenharmony_ci struct acpi_whea_header *action_table, 4862306a36Sopenharmony_ci u32 entries) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci ctx->ins_table = ins_table; 5162306a36Sopenharmony_ci ctx->instructions = instructions; 5262306a36Sopenharmony_ci ctx->action_table = action_table; 5362306a36Sopenharmony_ci ctx->entries = entries; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_ctx_init); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciint __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int rc; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci rc = apei_read(val, &entry->register_region); 6262306a36Sopenharmony_ci if (rc) 6362306a36Sopenharmony_ci return rc; 6462306a36Sopenharmony_ci *val >>= entry->register_region.bit_offset; 6562306a36Sopenharmony_ci *val &= entry->mask; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciint apei_exec_read_register(struct apei_exec_context *ctx, 7162306a36Sopenharmony_ci struct acpi_whea_header *entry) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci int rc; 7462306a36Sopenharmony_ci u64 val = 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci rc = __apei_exec_read_register(entry, &val); 7762306a36Sopenharmony_ci if (rc) 7862306a36Sopenharmony_ci return rc; 7962306a36Sopenharmony_ci ctx->value = val; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_read_register); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciint apei_exec_read_register_value(struct apei_exec_context *ctx, 8662306a36Sopenharmony_ci struct acpi_whea_header *entry) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci int rc; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci rc = apei_exec_read_register(ctx, entry); 9162306a36Sopenharmony_ci if (rc) 9262306a36Sopenharmony_ci return rc; 9362306a36Sopenharmony_ci ctx->value = (ctx->value == entry->value); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_read_register_value); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci int rc; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci val &= entry->mask; 10462306a36Sopenharmony_ci val <<= entry->register_region.bit_offset; 10562306a36Sopenharmony_ci if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { 10662306a36Sopenharmony_ci u64 valr = 0; 10762306a36Sopenharmony_ci rc = apei_read(&valr, &entry->register_region); 10862306a36Sopenharmony_ci if (rc) 10962306a36Sopenharmony_ci return rc; 11062306a36Sopenharmony_ci valr &= ~(entry->mask << entry->register_region.bit_offset); 11162306a36Sopenharmony_ci val |= valr; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci rc = apei_write(val, &entry->register_region); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return rc; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint apei_exec_write_register(struct apei_exec_context *ctx, 11962306a36Sopenharmony_ci struct acpi_whea_header *entry) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return __apei_exec_write_register(entry, ctx->value); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_write_register); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciint apei_exec_write_register_value(struct apei_exec_context *ctx, 12662306a36Sopenharmony_ci struct acpi_whea_header *entry) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci ctx->value = entry->value; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return apei_exec_write_register(ctx, entry); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_write_register_value); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciint apei_exec_noop(struct apei_exec_context *ctx, 13562306a36Sopenharmony_ci struct acpi_whea_header *entry) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_noop); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Interpret the specified action. Go through whole action table, 14362306a36Sopenharmony_ci * execute all instructions belong to the action. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ciint __apei_exec_run(struct apei_exec_context *ctx, u8 action, 14662306a36Sopenharmony_ci bool optional) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int rc = -ENOENT; 14962306a36Sopenharmony_ci u32 i, ip; 15062306a36Sopenharmony_ci struct acpi_whea_header *entry; 15162306a36Sopenharmony_ci apei_exec_ins_func_t run; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ctx->ip = 0; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * "ip" is the instruction pointer of current instruction, 15762306a36Sopenharmony_ci * "ctx->ip" specifies the next instruction to executed, 15862306a36Sopenharmony_ci * instruction "run" function may change the "ctx->ip" to 15962306a36Sopenharmony_ci * implement "goto" semantics. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cirewind: 16262306a36Sopenharmony_ci ip = 0; 16362306a36Sopenharmony_ci for (i = 0; i < ctx->entries; i++) { 16462306a36Sopenharmony_ci entry = &ctx->action_table[i]; 16562306a36Sopenharmony_ci if (entry->action != action) 16662306a36Sopenharmony_ci continue; 16762306a36Sopenharmony_ci if (ip == ctx->ip) { 16862306a36Sopenharmony_ci if (entry->instruction >= ctx->instructions || 16962306a36Sopenharmony_ci !ctx->ins_table[entry->instruction].run) { 17062306a36Sopenharmony_ci pr_warn(FW_WARN APEI_PFX 17162306a36Sopenharmony_ci "Invalid action table, unknown instruction type: %d\n", 17262306a36Sopenharmony_ci entry->instruction); 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci run = ctx->ins_table[entry->instruction].run; 17662306a36Sopenharmony_ci rc = run(ctx, entry); 17762306a36Sopenharmony_ci if (rc < 0) 17862306a36Sopenharmony_ci return rc; 17962306a36Sopenharmony_ci else if (rc != APEI_EXEC_SET_IP) 18062306a36Sopenharmony_ci ctx->ip++; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci ip++; 18362306a36Sopenharmony_ci if (ctx->ip < ip) 18462306a36Sopenharmony_ci goto rewind; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return !optional && rc < 0 ? rc : 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__apei_exec_run); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_citypedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx, 19262306a36Sopenharmony_ci struct acpi_whea_header *entry, 19362306a36Sopenharmony_ci void *data); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int apei_exec_for_each_entry(struct apei_exec_context *ctx, 19662306a36Sopenharmony_ci apei_exec_entry_func_t func, 19762306a36Sopenharmony_ci void *data, 19862306a36Sopenharmony_ci int *end) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u8 ins; 20162306a36Sopenharmony_ci int i, rc; 20262306a36Sopenharmony_ci struct acpi_whea_header *entry; 20362306a36Sopenharmony_ci struct apei_exec_ins_type *ins_table = ctx->ins_table; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; i < ctx->entries; i++) { 20662306a36Sopenharmony_ci entry = ctx->action_table + i; 20762306a36Sopenharmony_ci ins = entry->instruction; 20862306a36Sopenharmony_ci if (end) 20962306a36Sopenharmony_ci *end = i; 21062306a36Sopenharmony_ci if (ins >= ctx->instructions || !ins_table[ins].run) { 21162306a36Sopenharmony_ci pr_warn(FW_WARN APEI_PFX 21262306a36Sopenharmony_ci "Invalid action table, unknown instruction type: %d\n", 21362306a36Sopenharmony_ci ins); 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci rc = func(ctx, entry, data); 21762306a36Sopenharmony_ci if (rc) 21862306a36Sopenharmony_ci return rc; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int pre_map_gar_callback(struct apei_exec_context *ctx, 22562306a36Sopenharmony_ci struct acpi_whea_header *entry, 22662306a36Sopenharmony_ci void *data) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci u8 ins = entry->instruction; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) 23162306a36Sopenharmony_ci return apei_map_generic_address(&entry->register_region); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * Pre-map all GARs in action table to make it possible to access them 23862306a36Sopenharmony_ci * in NMI handler. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ciint apei_exec_pre_map_gars(struct apei_exec_context *ctx) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int rc, end; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback, 24562306a36Sopenharmony_ci NULL, &end); 24662306a36Sopenharmony_ci if (rc) { 24762306a36Sopenharmony_ci struct apei_exec_context ctx_unmap; 24862306a36Sopenharmony_ci memcpy(&ctx_unmap, ctx, sizeof(*ctx)); 24962306a36Sopenharmony_ci ctx_unmap.entries = end; 25062306a36Sopenharmony_ci apei_exec_post_unmap_gars(&ctx_unmap); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return rc; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_pre_map_gars); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int post_unmap_gar_callback(struct apei_exec_context *ctx, 25862306a36Sopenharmony_ci struct acpi_whea_header *entry, 25962306a36Sopenharmony_ci void *data) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci u8 ins = entry->instruction; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) 26462306a36Sopenharmony_ci apei_unmap_generic_address(&entry->register_region); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* Post-unmap all GAR in action table. */ 27062306a36Sopenharmony_ciint apei_exec_post_unmap_gars(struct apei_exec_context *ctx) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci return apei_exec_for_each_entry(ctx, post_unmap_gar_callback, 27362306a36Sopenharmony_ci NULL, NULL); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Resource management for GARs in APEI 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistruct apei_res { 28162306a36Sopenharmony_ci struct list_head list; 28262306a36Sopenharmony_ci unsigned long start; 28362306a36Sopenharmony_ci unsigned long end; 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* Collect all resources requested, to avoid conflict */ 28762306a36Sopenharmony_cistatic struct apei_resources apei_resources_all = { 28862306a36Sopenharmony_ci .iomem = LIST_HEAD_INIT(apei_resources_all.iomem), 28962306a36Sopenharmony_ci .ioport = LIST_HEAD_INIT(apei_resources_all.ioport), 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int apei_res_add(struct list_head *res_list, 29362306a36Sopenharmony_ci unsigned long start, unsigned long size) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct apei_res *res, *resn, *res_ins = NULL; 29662306a36Sopenharmony_ci unsigned long end = start + size; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (end <= start) 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_cirepeat: 30162306a36Sopenharmony_ci list_for_each_entry_safe(res, resn, res_list, list) { 30262306a36Sopenharmony_ci if (res->start > end || res->end < start) 30362306a36Sopenharmony_ci continue; 30462306a36Sopenharmony_ci else if (end <= res->end && start >= res->start) { 30562306a36Sopenharmony_ci kfree(res_ins); 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci list_del(&res->list); 30962306a36Sopenharmony_ci res->start = start = min(res->start, start); 31062306a36Sopenharmony_ci res->end = end = max(res->end, end); 31162306a36Sopenharmony_ci kfree(res_ins); 31262306a36Sopenharmony_ci res_ins = res; 31362306a36Sopenharmony_ci goto repeat; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (res_ins) 31762306a36Sopenharmony_ci list_add(&res_ins->list, res_list); 31862306a36Sopenharmony_ci else { 31962306a36Sopenharmony_ci res_ins = kmalloc(sizeof(*res_ins), GFP_KERNEL); 32062306a36Sopenharmony_ci if (!res_ins) 32162306a36Sopenharmony_ci return -ENOMEM; 32262306a36Sopenharmony_ci res_ins->start = start; 32362306a36Sopenharmony_ci res_ins->end = end; 32462306a36Sopenharmony_ci list_add(&res_ins->list, res_list); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int apei_res_sub(struct list_head *res_list1, 33162306a36Sopenharmony_ci struct list_head *res_list2) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct apei_res *res1, *resn1, *res2, *res; 33462306a36Sopenharmony_ci res1 = list_entry(res_list1->next, struct apei_res, list); 33562306a36Sopenharmony_ci resn1 = list_entry(res1->list.next, struct apei_res, list); 33662306a36Sopenharmony_ci while (&res1->list != res_list1) { 33762306a36Sopenharmony_ci list_for_each_entry(res2, res_list2, list) { 33862306a36Sopenharmony_ci if (res1->start >= res2->end || 33962306a36Sopenharmony_ci res1->end <= res2->start) 34062306a36Sopenharmony_ci continue; 34162306a36Sopenharmony_ci else if (res1->end <= res2->end && 34262306a36Sopenharmony_ci res1->start >= res2->start) { 34362306a36Sopenharmony_ci list_del(&res1->list); 34462306a36Sopenharmony_ci kfree(res1); 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci } else if (res1->end > res2->end && 34762306a36Sopenharmony_ci res1->start < res2->start) { 34862306a36Sopenharmony_ci res = kmalloc(sizeof(*res), GFP_KERNEL); 34962306a36Sopenharmony_ci if (!res) 35062306a36Sopenharmony_ci return -ENOMEM; 35162306a36Sopenharmony_ci res->start = res2->end; 35262306a36Sopenharmony_ci res->end = res1->end; 35362306a36Sopenharmony_ci res1->end = res2->start; 35462306a36Sopenharmony_ci list_add(&res->list, &res1->list); 35562306a36Sopenharmony_ci resn1 = res; 35662306a36Sopenharmony_ci } else { 35762306a36Sopenharmony_ci if (res1->start < res2->start) 35862306a36Sopenharmony_ci res1->end = res2->start; 35962306a36Sopenharmony_ci else 36062306a36Sopenharmony_ci res1->start = res2->end; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci res1 = resn1; 36462306a36Sopenharmony_ci resn1 = list_entry(resn1->list.next, struct apei_res, list); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void apei_res_clean(struct list_head *res_list) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct apei_res *res, *resn; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci list_for_each_entry_safe(res, resn, res_list, list) { 37562306a36Sopenharmony_ci list_del(&res->list); 37662306a36Sopenharmony_ci kfree(res); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_civoid apei_resources_fini(struct apei_resources *resources) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci apei_res_clean(&resources->iomem); 38362306a36Sopenharmony_ci apei_res_clean(&resources->ioport); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_fini); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int apei_resources_merge(struct apei_resources *resources1, 38862306a36Sopenharmony_ci struct apei_resources *resources2) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int rc; 39162306a36Sopenharmony_ci struct apei_res *res; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci list_for_each_entry(res, &resources2->iomem, list) { 39462306a36Sopenharmony_ci rc = apei_res_add(&resources1->iomem, res->start, 39562306a36Sopenharmony_ci res->end - res->start); 39662306a36Sopenharmony_ci if (rc) 39762306a36Sopenharmony_ci return rc; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci list_for_each_entry(res, &resources2->ioport, list) { 40062306a36Sopenharmony_ci rc = apei_res_add(&resources1->ioport, res->start, 40162306a36Sopenharmony_ci res->end - res->start); 40262306a36Sopenharmony_ci if (rc) 40362306a36Sopenharmony_ci return rc; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ciint apei_resources_add(struct apei_resources *resources, 41062306a36Sopenharmony_ci unsigned long start, unsigned long size, 41162306a36Sopenharmony_ci bool iomem) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci if (iomem) 41462306a36Sopenharmony_ci return apei_res_add(&resources->iomem, start, size); 41562306a36Sopenharmony_ci else 41662306a36Sopenharmony_ci return apei_res_add(&resources->ioport, start, size); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_add); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* 42162306a36Sopenharmony_ci * EINJ has two groups of GARs (EINJ table entry and trigger table 42262306a36Sopenharmony_ci * entry), so common resources are subtracted from the trigger table 42362306a36Sopenharmony_ci * resources before the second requesting. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ciint apei_resources_sub(struct apei_resources *resources1, 42662306a36Sopenharmony_ci struct apei_resources *resources2) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci int rc; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci rc = apei_res_sub(&resources1->iomem, &resources2->iomem); 43162306a36Sopenharmony_ci if (rc) 43262306a36Sopenharmony_ci return rc; 43362306a36Sopenharmony_ci return apei_res_sub(&resources1->ioport, &resources2->ioport); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_sub); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int apei_get_res_callback(__u64 start, __u64 size, void *data) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct apei_resources *resources = data; 44062306a36Sopenharmony_ci return apei_res_add(&resources->iomem, start, size); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int apei_get_nvs_resources(struct apei_resources *resources) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci return acpi_nvs_for_each_region(apei_get_res_callback, resources); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ciint (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, 44962306a36Sopenharmony_ci void *data), void *data); 45062306a36Sopenharmony_cistatic int apei_get_arch_resources(struct apei_resources *resources) 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci return arch_apei_filter_addr(apei_get_res_callback, resources); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* 45762306a36Sopenharmony_ci * IO memory/port resource management mechanism is used to check 45862306a36Sopenharmony_ci * whether memory/port area used by GARs conflicts with normal memory 45962306a36Sopenharmony_ci * or IO memory/port of devices. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ciint apei_resources_request(struct apei_resources *resources, 46262306a36Sopenharmony_ci const char *desc) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct apei_res *res, *res_bak = NULL; 46562306a36Sopenharmony_ci struct resource *r; 46662306a36Sopenharmony_ci struct apei_resources nvs_resources, arch_res; 46762306a36Sopenharmony_ci int rc; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci rc = apei_resources_sub(resources, &apei_resources_all); 47062306a36Sopenharmony_ci if (rc) 47162306a36Sopenharmony_ci return rc; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Some firmware uses ACPI NVS region, that has been marked as 47562306a36Sopenharmony_ci * busy, so exclude it from APEI resources to avoid false 47662306a36Sopenharmony_ci * conflict. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci apei_resources_init(&nvs_resources); 47962306a36Sopenharmony_ci rc = apei_get_nvs_resources(&nvs_resources); 48062306a36Sopenharmony_ci if (rc) 48162306a36Sopenharmony_ci goto nvs_res_fini; 48262306a36Sopenharmony_ci rc = apei_resources_sub(resources, &nvs_resources); 48362306a36Sopenharmony_ci if (rc) 48462306a36Sopenharmony_ci goto nvs_res_fini; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (arch_apei_filter_addr) { 48762306a36Sopenharmony_ci apei_resources_init(&arch_res); 48862306a36Sopenharmony_ci rc = apei_get_arch_resources(&arch_res); 48962306a36Sopenharmony_ci if (rc) 49062306a36Sopenharmony_ci goto arch_res_fini; 49162306a36Sopenharmony_ci rc = apei_resources_sub(resources, &arch_res); 49262306a36Sopenharmony_ci if (rc) 49362306a36Sopenharmony_ci goto arch_res_fini; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci rc = -EINVAL; 49762306a36Sopenharmony_ci list_for_each_entry(res, &resources->iomem, list) { 49862306a36Sopenharmony_ci r = request_mem_region(res->start, res->end - res->start, 49962306a36Sopenharmony_ci desc); 50062306a36Sopenharmony_ci if (!r) { 50162306a36Sopenharmony_ci pr_err(APEI_PFX 50262306a36Sopenharmony_ci "Can not request [mem %#010llx-%#010llx] for %s registers\n", 50362306a36Sopenharmony_ci (unsigned long long)res->start, 50462306a36Sopenharmony_ci (unsigned long long)res->end - 1, desc); 50562306a36Sopenharmony_ci res_bak = res; 50662306a36Sopenharmony_ci goto err_unmap_iomem; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci list_for_each_entry(res, &resources->ioport, list) { 51162306a36Sopenharmony_ci r = request_region(res->start, res->end - res->start, desc); 51262306a36Sopenharmony_ci if (!r) { 51362306a36Sopenharmony_ci pr_err(APEI_PFX 51462306a36Sopenharmony_ci "Can not request [io %#06llx-%#06llx] for %s registers\n", 51562306a36Sopenharmony_ci (unsigned long long)res->start, 51662306a36Sopenharmony_ci (unsigned long long)res->end - 1, desc); 51762306a36Sopenharmony_ci res_bak = res; 51862306a36Sopenharmony_ci goto err_unmap_ioport; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci rc = apei_resources_merge(&apei_resources_all, resources); 52362306a36Sopenharmony_ci if (rc) { 52462306a36Sopenharmony_ci pr_err(APEI_PFX "Fail to merge resources!\n"); 52562306a36Sopenharmony_ci goto err_unmap_ioport; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci goto arch_res_fini; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cierr_unmap_ioport: 53162306a36Sopenharmony_ci list_for_each_entry(res, &resources->ioport, list) { 53262306a36Sopenharmony_ci if (res == res_bak) 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci release_region(res->start, res->end - res->start); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci res_bak = NULL; 53762306a36Sopenharmony_cierr_unmap_iomem: 53862306a36Sopenharmony_ci list_for_each_entry(res, &resources->iomem, list) { 53962306a36Sopenharmony_ci if (res == res_bak) 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci release_mem_region(res->start, res->end - res->start); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ciarch_res_fini: 54462306a36Sopenharmony_ci if (arch_apei_filter_addr) 54562306a36Sopenharmony_ci apei_resources_fini(&arch_res); 54662306a36Sopenharmony_cinvs_res_fini: 54762306a36Sopenharmony_ci apei_resources_fini(&nvs_resources); 54862306a36Sopenharmony_ci return rc; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_request); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_civoid apei_resources_release(struct apei_resources *resources) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci int rc; 55562306a36Sopenharmony_ci struct apei_res *res; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci list_for_each_entry(res, &resources->iomem, list) 55862306a36Sopenharmony_ci release_mem_region(res->start, res->end - res->start); 55962306a36Sopenharmony_ci list_for_each_entry(res, &resources->ioport, list) 56062306a36Sopenharmony_ci release_region(res->start, res->end - res->start); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci rc = apei_resources_sub(&apei_resources_all, resources); 56362306a36Sopenharmony_ci if (rc) 56462306a36Sopenharmony_ci pr_err(APEI_PFX "Fail to sub resources!\n"); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_release); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, 56962306a36Sopenharmony_ci u32 *access_bit_width) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci u32 bit_width, bit_offset, access_size_code, space_id; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci bit_width = reg->bit_width; 57462306a36Sopenharmony_ci bit_offset = reg->bit_offset; 57562306a36Sopenharmony_ci access_size_code = reg->access_width; 57662306a36Sopenharmony_ci space_id = reg->space_id; 57762306a36Sopenharmony_ci *paddr = get_unaligned(®->address); 57862306a36Sopenharmony_ci if (!*paddr) { 57962306a36Sopenharmony_ci pr_warn(FW_BUG APEI_PFX 58062306a36Sopenharmony_ci "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n", 58162306a36Sopenharmony_ci *paddr, bit_width, bit_offset, access_size_code, 58262306a36Sopenharmony_ci space_id); 58362306a36Sopenharmony_ci return -EINVAL; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (access_size_code < 1 || access_size_code > 4) { 58762306a36Sopenharmony_ci pr_warn(FW_BUG APEI_PFX 58862306a36Sopenharmony_ci "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n", 58962306a36Sopenharmony_ci *paddr, bit_width, bit_offset, access_size_code, 59062306a36Sopenharmony_ci space_id); 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci *access_bit_width = 1UL << (access_size_code + 2); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Fixup common BIOS bug */ 59662306a36Sopenharmony_ci if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 && 59762306a36Sopenharmony_ci *access_bit_width < 32) 59862306a36Sopenharmony_ci *access_bit_width = 32; 59962306a36Sopenharmony_ci else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 && 60062306a36Sopenharmony_ci *access_bit_width < 64) 60162306a36Sopenharmony_ci *access_bit_width = 64; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if ((bit_width + bit_offset) > *access_bit_width) { 60462306a36Sopenharmony_ci pr_warn(FW_BUG APEI_PFX 60562306a36Sopenharmony_ci "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n", 60662306a36Sopenharmony_ci *paddr, bit_width, bit_offset, access_size_code, 60762306a36Sopenharmony_ci space_id); 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && 61262306a36Sopenharmony_ci space_id != ACPI_ADR_SPACE_SYSTEM_IO) { 61362306a36Sopenharmony_ci pr_warn(FW_BUG APEI_PFX 61462306a36Sopenharmony_ci "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n", 61562306a36Sopenharmony_ci *paddr, bit_width, bit_offset, access_size_code, 61662306a36Sopenharmony_ci space_id); 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ciint apei_map_generic_address(struct acpi_generic_address *reg) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci int rc; 62662306a36Sopenharmony_ci u32 access_bit_width; 62762306a36Sopenharmony_ci u64 address; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci rc = apei_check_gar(reg, &address, &access_bit_width); 63062306a36Sopenharmony_ci if (rc) 63162306a36Sopenharmony_ci return rc; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* IO space doesn't need mapping */ 63462306a36Sopenharmony_ci if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!acpi_os_map_generic_address(reg)) 63862306a36Sopenharmony_ci return -ENXIO; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_map_generic_address); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/* read GAR in interrupt (including NMI) or process context */ 64562306a36Sopenharmony_ciint apei_read(u64 *val, struct acpi_generic_address *reg) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci int rc; 64862306a36Sopenharmony_ci u32 access_bit_width; 64962306a36Sopenharmony_ci u64 address; 65062306a36Sopenharmony_ci acpi_status status; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci rc = apei_check_gar(reg, &address, &access_bit_width); 65362306a36Sopenharmony_ci if (rc) 65462306a36Sopenharmony_ci return rc; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci *val = 0; 65762306a36Sopenharmony_ci switch(reg->space_id) { 65862306a36Sopenharmony_ci case ACPI_ADR_SPACE_SYSTEM_MEMORY: 65962306a36Sopenharmony_ci status = acpi_os_read_memory((acpi_physical_address) address, 66062306a36Sopenharmony_ci val, access_bit_width); 66162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 66262306a36Sopenharmony_ci return -EIO; 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case ACPI_ADR_SPACE_SYSTEM_IO: 66562306a36Sopenharmony_ci status = acpi_os_read_port(address, (u32 *)val, 66662306a36Sopenharmony_ci access_bit_width); 66762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 66862306a36Sopenharmony_ci return -EIO; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci default: 67162306a36Sopenharmony_ci return -EINVAL; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_read); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/* write GAR in interrupt (including NMI) or process context */ 67962306a36Sopenharmony_ciint apei_write(u64 val, struct acpi_generic_address *reg) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci int rc; 68262306a36Sopenharmony_ci u32 access_bit_width; 68362306a36Sopenharmony_ci u64 address; 68462306a36Sopenharmony_ci acpi_status status; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci rc = apei_check_gar(reg, &address, &access_bit_width); 68762306a36Sopenharmony_ci if (rc) 68862306a36Sopenharmony_ci return rc; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci switch (reg->space_id) { 69162306a36Sopenharmony_ci case ACPI_ADR_SPACE_SYSTEM_MEMORY: 69262306a36Sopenharmony_ci status = acpi_os_write_memory((acpi_physical_address) address, 69362306a36Sopenharmony_ci val, access_bit_width); 69462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 69562306a36Sopenharmony_ci return -EIO; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci case ACPI_ADR_SPACE_SYSTEM_IO: 69862306a36Sopenharmony_ci status = acpi_os_write_port(address, val, access_bit_width); 69962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 70062306a36Sopenharmony_ci return -EIO; 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci default: 70362306a36Sopenharmony_ci return -EINVAL; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_write); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int collect_res_callback(struct apei_exec_context *ctx, 71162306a36Sopenharmony_ci struct acpi_whea_header *entry, 71262306a36Sopenharmony_ci void *data) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct apei_resources *resources = data; 71562306a36Sopenharmony_ci struct acpi_generic_address *reg = &entry->register_region; 71662306a36Sopenharmony_ci u8 ins = entry->instruction; 71762306a36Sopenharmony_ci u32 access_bit_width; 71862306a36Sopenharmony_ci u64 paddr; 71962306a36Sopenharmony_ci int rc; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)) 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci rc = apei_check_gar(reg, &paddr, &access_bit_width); 72562306a36Sopenharmony_ci if (rc) 72662306a36Sopenharmony_ci return rc; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci switch (reg->space_id) { 72962306a36Sopenharmony_ci case ACPI_ADR_SPACE_SYSTEM_MEMORY: 73062306a36Sopenharmony_ci return apei_res_add(&resources->iomem, paddr, 73162306a36Sopenharmony_ci access_bit_width / 8); 73262306a36Sopenharmony_ci case ACPI_ADR_SPACE_SYSTEM_IO: 73362306a36Sopenharmony_ci return apei_res_add(&resources->ioport, paddr, 73462306a36Sopenharmony_ci access_bit_width / 8); 73562306a36Sopenharmony_ci default: 73662306a36Sopenharmony_ci return -EINVAL; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci/* 74162306a36Sopenharmony_ci * Same register may be used by multiple instructions in GARs, so 74262306a36Sopenharmony_ci * resources are collected before requesting. 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_ciint apei_exec_collect_resources(struct apei_exec_context *ctx, 74562306a36Sopenharmony_ci struct apei_resources *resources) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci return apei_exec_for_each_entry(ctx, collect_res_callback, 74862306a36Sopenharmony_ci resources, NULL); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_collect_resources); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistruct dentry *apei_get_debugfs_dir(void) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci static struct dentry *dapei; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (!dapei) 75762306a36Sopenharmony_ci dapei = debugfs_create_dir("apei", NULL); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return dapei; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_get_debugfs_dir); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ciint __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, 76462306a36Sopenharmony_ci void *data) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci return 1; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(arch_apei_enable_cmcff); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_civoid __weak arch_apei_report_mem_error(int sev, 77162306a36Sopenharmony_ci struct cper_sec_mem_err *mem_err) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(arch_apei_report_mem_error); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ciint apei_osc_setup(void) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c"; 77962306a36Sopenharmony_ci acpi_handle handle; 78062306a36Sopenharmony_ci u32 capbuf[3]; 78162306a36Sopenharmony_ci struct acpi_osc_context context = { 78262306a36Sopenharmony_ci .uuid_str = whea_uuid_str, 78362306a36Sopenharmony_ci .rev = 1, 78462306a36Sopenharmony_ci .cap.length = sizeof(capbuf), 78562306a36Sopenharmony_ci .cap.pointer = capbuf, 78662306a36Sopenharmony_ci }; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; 78962306a36Sopenharmony_ci capbuf[OSC_SUPPORT_DWORD] = 1; 79062306a36Sopenharmony_ci capbuf[OSC_CONTROL_DWORD] = 0; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)) 79362306a36Sopenharmony_ci || ACPI_FAILURE(acpi_run_osc(handle, &context))) 79462306a36Sopenharmony_ci return -EIO; 79562306a36Sopenharmony_ci else { 79662306a36Sopenharmony_ci kfree(context.ret.pointer); 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_osc_setup); 801