162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * APEI Error Record Serialization Table support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * ERST is a way provided by APEI to save and retrieve hardware error 662306a36Sopenharmony_ci * information to and from a persistent store. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * For more information about ERST, please refer to ACPI Specification 962306a36Sopenharmony_ci * version 4.0, section 17.4. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright 2010 Intel Corp. 1262306a36Sopenharmony_ci * Author: Huang Ying <ying.huang@intel.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/acpi.h> 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci#include <linux/cper.h> 2362306a36Sopenharmony_ci#include <linux/nmi.h> 2462306a36Sopenharmony_ci#include <linux/hardirq.h> 2562306a36Sopenharmony_ci#include <linux/pstore.h> 2662306a36Sopenharmony_ci#include <linux/vmalloc.h> 2762306a36Sopenharmony_ci#include <linux/mm.h> /* kvfree() */ 2862306a36Sopenharmony_ci#include <acpi/apei.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "apei-internal.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#undef pr_fmt 3362306a36Sopenharmony_ci#define pr_fmt(fmt) "ERST: " fmt 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* ERST command status */ 3662306a36Sopenharmony_ci#define ERST_STATUS_SUCCESS 0x0 3762306a36Sopenharmony_ci#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1 3862306a36Sopenharmony_ci#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2 3962306a36Sopenharmony_ci#define ERST_STATUS_FAILED 0x3 4062306a36Sopenharmony_ci#define ERST_STATUS_RECORD_STORE_EMPTY 0x4 4162306a36Sopenharmony_ci#define ERST_STATUS_RECORD_NOT_FOUND 0x5 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define ERST_TAB_ENTRY(tab) \ 4462306a36Sopenharmony_ci ((struct acpi_whea_header *)((char *)(tab) + \ 4562306a36Sopenharmony_ci sizeof(struct acpi_table_erst))) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define SPIN_UNIT 100 /* 100ns */ 4862306a36Sopenharmony_ci/* Firmware should respond within 1 milliseconds */ 4962306a36Sopenharmony_ci#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) 5062306a36Sopenharmony_ci#define FIRMWARE_MAX_STALL 50 /* 50us */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciint erst_disable; 5362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_disable); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct acpi_table_erst *erst_tab; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* ERST Error Log Address Range attributes */ 5862306a36Sopenharmony_ci#define ERST_RANGE_RESERVED 0x0001 5962306a36Sopenharmony_ci#define ERST_RANGE_NVRAM 0x0002 6062306a36Sopenharmony_ci#define ERST_RANGE_SLOW 0x0004 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * ERST Error Log Address Range, used as buffer for reading/writing 6462306a36Sopenharmony_ci * error records. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic struct erst_erange { 6762306a36Sopenharmony_ci u64 base; 6862306a36Sopenharmony_ci u64 size; 6962306a36Sopenharmony_ci void __iomem *vaddr; 7062306a36Sopenharmony_ci u32 attr; 7162306a36Sopenharmony_ci} erst_erange; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * Prevent ERST interpreter to run simultaneously, because the 7562306a36Sopenharmony_ci * corresponding firmware implementation may not work properly when 7662306a36Sopenharmony_ci * invoked simultaneously. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * It is used to provide exclusive accessing for ERST Error Log 7962306a36Sopenharmony_ci * Address Range too. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(erst_lock); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic inline int erst_errno(int command_status) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci switch (command_status) { 8662306a36Sopenharmony_ci case ERST_STATUS_SUCCESS: 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci case ERST_STATUS_HARDWARE_NOT_AVAILABLE: 8962306a36Sopenharmony_ci return -ENODEV; 9062306a36Sopenharmony_ci case ERST_STATUS_NOT_ENOUGH_SPACE: 9162306a36Sopenharmony_ci return -ENOSPC; 9262306a36Sopenharmony_ci case ERST_STATUS_RECORD_STORE_EMPTY: 9362306a36Sopenharmony_ci case ERST_STATUS_RECORD_NOT_FOUND: 9462306a36Sopenharmony_ci return -ENOENT; 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int erst_timedout(u64 *t, u64 spin_unit) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci if ((s64)*t < spin_unit) { 10362306a36Sopenharmony_ci pr_warn(FW_WARN "Firmware does not respond in time.\n"); 10462306a36Sopenharmony_ci return 1; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci *t -= spin_unit; 10762306a36Sopenharmony_ci ndelay(spin_unit); 10862306a36Sopenharmony_ci touch_nmi_watchdog(); 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int erst_exec_load_var1(struct apei_exec_context *ctx, 11362306a36Sopenharmony_ci struct acpi_whea_header *entry) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return __apei_exec_read_register(entry, &ctx->var1); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int erst_exec_load_var2(struct apei_exec_context *ctx, 11962306a36Sopenharmony_ci struct acpi_whea_header *entry) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return __apei_exec_read_register(entry, &ctx->var2); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int erst_exec_store_var1(struct apei_exec_context *ctx, 12562306a36Sopenharmony_ci struct acpi_whea_header *entry) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci return __apei_exec_write_register(entry, ctx->var1); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int erst_exec_add(struct apei_exec_context *ctx, 13162306a36Sopenharmony_ci struct acpi_whea_header *entry) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci ctx->var1 += ctx->var2; 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int erst_exec_subtract(struct apei_exec_context *ctx, 13862306a36Sopenharmony_ci struct acpi_whea_header *entry) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci ctx->var1 -= ctx->var2; 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int erst_exec_add_value(struct apei_exec_context *ctx, 14562306a36Sopenharmony_ci struct acpi_whea_header *entry) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int rc; 14862306a36Sopenharmony_ci u64 val; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci rc = __apei_exec_read_register(entry, &val); 15162306a36Sopenharmony_ci if (rc) 15262306a36Sopenharmony_ci return rc; 15362306a36Sopenharmony_ci val += ctx->value; 15462306a36Sopenharmony_ci rc = __apei_exec_write_register(entry, val); 15562306a36Sopenharmony_ci return rc; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int erst_exec_subtract_value(struct apei_exec_context *ctx, 15962306a36Sopenharmony_ci struct acpi_whea_header *entry) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int rc; 16262306a36Sopenharmony_ci u64 val; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rc = __apei_exec_read_register(entry, &val); 16562306a36Sopenharmony_ci if (rc) 16662306a36Sopenharmony_ci return rc; 16762306a36Sopenharmony_ci val -= ctx->value; 16862306a36Sopenharmony_ci rc = __apei_exec_write_register(entry, val); 16962306a36Sopenharmony_ci return rc; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int erst_exec_stall(struct apei_exec_context *ctx, 17362306a36Sopenharmony_ci struct acpi_whea_header *entry) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u64 stall_time; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (ctx->value > FIRMWARE_MAX_STALL) { 17862306a36Sopenharmony_ci if (!in_nmi()) 17962306a36Sopenharmony_ci pr_warn(FW_WARN 18062306a36Sopenharmony_ci "Too long stall time for stall instruction: 0x%llx.\n", 18162306a36Sopenharmony_ci ctx->value); 18262306a36Sopenharmony_ci stall_time = FIRMWARE_MAX_STALL; 18362306a36Sopenharmony_ci } else 18462306a36Sopenharmony_ci stall_time = ctx->value; 18562306a36Sopenharmony_ci udelay(stall_time); 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int erst_exec_stall_while_true(struct apei_exec_context *ctx, 19062306a36Sopenharmony_ci struct acpi_whea_header *entry) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int rc; 19362306a36Sopenharmony_ci u64 val; 19462306a36Sopenharmony_ci u64 timeout = FIRMWARE_TIMEOUT; 19562306a36Sopenharmony_ci u64 stall_time; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (ctx->var1 > FIRMWARE_MAX_STALL) { 19862306a36Sopenharmony_ci if (!in_nmi()) 19962306a36Sopenharmony_ci pr_warn(FW_WARN 20062306a36Sopenharmony_ci "Too long stall time for stall while true instruction: 0x%llx.\n", 20162306a36Sopenharmony_ci ctx->var1); 20262306a36Sopenharmony_ci stall_time = FIRMWARE_MAX_STALL; 20362306a36Sopenharmony_ci } else 20462306a36Sopenharmony_ci stall_time = ctx->var1; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (;;) { 20762306a36Sopenharmony_ci rc = __apei_exec_read_register(entry, &val); 20862306a36Sopenharmony_ci if (rc) 20962306a36Sopenharmony_ci return rc; 21062306a36Sopenharmony_ci if (val != ctx->value) 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC)) 21362306a36Sopenharmony_ci return -EIO; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int erst_exec_skip_next_instruction_if_true( 21962306a36Sopenharmony_ci struct apei_exec_context *ctx, 22062306a36Sopenharmony_ci struct acpi_whea_header *entry) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci int rc; 22362306a36Sopenharmony_ci u64 val; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci rc = __apei_exec_read_register(entry, &val); 22662306a36Sopenharmony_ci if (rc) 22762306a36Sopenharmony_ci return rc; 22862306a36Sopenharmony_ci if (val == ctx->value) { 22962306a36Sopenharmony_ci ctx->ip += 2; 23062306a36Sopenharmony_ci return APEI_EXEC_SET_IP; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int erst_exec_goto(struct apei_exec_context *ctx, 23762306a36Sopenharmony_ci struct acpi_whea_header *entry) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci ctx->ip = ctx->value; 24062306a36Sopenharmony_ci return APEI_EXEC_SET_IP; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int erst_exec_set_src_address_base(struct apei_exec_context *ctx, 24462306a36Sopenharmony_ci struct acpi_whea_header *entry) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return __apei_exec_read_register(entry, &ctx->src_base); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int erst_exec_set_dst_address_base(struct apei_exec_context *ctx, 25062306a36Sopenharmony_ci struct acpi_whea_header *entry) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci return __apei_exec_read_register(entry, &ctx->dst_base); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int erst_exec_move_data(struct apei_exec_context *ctx, 25662306a36Sopenharmony_ci struct acpi_whea_header *entry) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int rc; 25962306a36Sopenharmony_ci u64 offset; 26062306a36Sopenharmony_ci void *src, *dst; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* ioremap does not work in interrupt context */ 26362306a36Sopenharmony_ci if (in_interrupt()) { 26462306a36Sopenharmony_ci pr_warn("MOVE_DATA can not be used in interrupt context.\n"); 26562306a36Sopenharmony_ci return -EBUSY; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci rc = __apei_exec_read_register(entry, &offset); 26962306a36Sopenharmony_ci if (rc) 27062306a36Sopenharmony_ci return rc; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci src = ioremap(ctx->src_base + offset, ctx->var2); 27362306a36Sopenharmony_ci if (!src) 27462306a36Sopenharmony_ci return -ENOMEM; 27562306a36Sopenharmony_ci dst = ioremap(ctx->dst_base + offset, ctx->var2); 27662306a36Sopenharmony_ci if (!dst) { 27762306a36Sopenharmony_ci iounmap(src); 27862306a36Sopenharmony_ci return -ENOMEM; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci memmove(dst, src, ctx->var2); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci iounmap(src); 28462306a36Sopenharmony_ci iounmap(dst); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic struct apei_exec_ins_type erst_ins_type[] = { 29062306a36Sopenharmony_ci [ACPI_ERST_READ_REGISTER] = { 29162306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 29262306a36Sopenharmony_ci .run = apei_exec_read_register, 29362306a36Sopenharmony_ci }, 29462306a36Sopenharmony_ci [ACPI_ERST_READ_REGISTER_VALUE] = { 29562306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 29662306a36Sopenharmony_ci .run = apei_exec_read_register_value, 29762306a36Sopenharmony_ci }, 29862306a36Sopenharmony_ci [ACPI_ERST_WRITE_REGISTER] = { 29962306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 30062306a36Sopenharmony_ci .run = apei_exec_write_register, 30162306a36Sopenharmony_ci }, 30262306a36Sopenharmony_ci [ACPI_ERST_WRITE_REGISTER_VALUE] = { 30362306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 30462306a36Sopenharmony_ci .run = apei_exec_write_register_value, 30562306a36Sopenharmony_ci }, 30662306a36Sopenharmony_ci [ACPI_ERST_NOOP] = { 30762306a36Sopenharmony_ci .flags = 0, 30862306a36Sopenharmony_ci .run = apei_exec_noop, 30962306a36Sopenharmony_ci }, 31062306a36Sopenharmony_ci [ACPI_ERST_LOAD_VAR1] = { 31162306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 31262306a36Sopenharmony_ci .run = erst_exec_load_var1, 31362306a36Sopenharmony_ci }, 31462306a36Sopenharmony_ci [ACPI_ERST_LOAD_VAR2] = { 31562306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 31662306a36Sopenharmony_ci .run = erst_exec_load_var2, 31762306a36Sopenharmony_ci }, 31862306a36Sopenharmony_ci [ACPI_ERST_STORE_VAR1] = { 31962306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 32062306a36Sopenharmony_ci .run = erst_exec_store_var1, 32162306a36Sopenharmony_ci }, 32262306a36Sopenharmony_ci [ACPI_ERST_ADD] = { 32362306a36Sopenharmony_ci .flags = 0, 32462306a36Sopenharmony_ci .run = erst_exec_add, 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci [ACPI_ERST_SUBTRACT] = { 32762306a36Sopenharmony_ci .flags = 0, 32862306a36Sopenharmony_ci .run = erst_exec_subtract, 32962306a36Sopenharmony_ci }, 33062306a36Sopenharmony_ci [ACPI_ERST_ADD_VALUE] = { 33162306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 33262306a36Sopenharmony_ci .run = erst_exec_add_value, 33362306a36Sopenharmony_ci }, 33462306a36Sopenharmony_ci [ACPI_ERST_SUBTRACT_VALUE] = { 33562306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 33662306a36Sopenharmony_ci .run = erst_exec_subtract_value, 33762306a36Sopenharmony_ci }, 33862306a36Sopenharmony_ci [ACPI_ERST_STALL] = { 33962306a36Sopenharmony_ci .flags = 0, 34062306a36Sopenharmony_ci .run = erst_exec_stall, 34162306a36Sopenharmony_ci }, 34262306a36Sopenharmony_ci [ACPI_ERST_STALL_WHILE_TRUE] = { 34362306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 34462306a36Sopenharmony_ci .run = erst_exec_stall_while_true, 34562306a36Sopenharmony_ci }, 34662306a36Sopenharmony_ci [ACPI_ERST_SKIP_NEXT_IF_TRUE] = { 34762306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 34862306a36Sopenharmony_ci .run = erst_exec_skip_next_instruction_if_true, 34962306a36Sopenharmony_ci }, 35062306a36Sopenharmony_ci [ACPI_ERST_GOTO] = { 35162306a36Sopenharmony_ci .flags = 0, 35262306a36Sopenharmony_ci .run = erst_exec_goto, 35362306a36Sopenharmony_ci }, 35462306a36Sopenharmony_ci [ACPI_ERST_SET_SRC_ADDRESS_BASE] = { 35562306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 35662306a36Sopenharmony_ci .run = erst_exec_set_src_address_base, 35762306a36Sopenharmony_ci }, 35862306a36Sopenharmony_ci [ACPI_ERST_SET_DST_ADDRESS_BASE] = { 35962306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 36062306a36Sopenharmony_ci .run = erst_exec_set_dst_address_base, 36162306a36Sopenharmony_ci }, 36262306a36Sopenharmony_ci [ACPI_ERST_MOVE_DATA] = { 36362306a36Sopenharmony_ci .flags = APEI_EXEC_INS_ACCESS_REGISTER, 36462306a36Sopenharmony_ci .run = erst_exec_move_data, 36562306a36Sopenharmony_ci }, 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic inline void erst_exec_ctx_init(struct apei_exec_context *ctx) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type), 37162306a36Sopenharmony_ci ERST_TAB_ENTRY(erst_tab), erst_tab->entries); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int erst_get_erange(struct erst_erange *range) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct apei_exec_context ctx; 37762306a36Sopenharmony_ci int rc; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 38062306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE); 38162306a36Sopenharmony_ci if (rc) 38262306a36Sopenharmony_ci return rc; 38362306a36Sopenharmony_ci range->base = apei_exec_ctx_get_output(&ctx); 38462306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH); 38562306a36Sopenharmony_ci if (rc) 38662306a36Sopenharmony_ci return rc; 38762306a36Sopenharmony_ci range->size = apei_exec_ctx_get_output(&ctx); 38862306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES); 38962306a36Sopenharmony_ci if (rc) 39062306a36Sopenharmony_ci return rc; 39162306a36Sopenharmony_ci range->attr = apei_exec_ctx_get_output(&ctx); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic ssize_t __erst_get_record_count(void) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct apei_exec_context ctx; 39962306a36Sopenharmony_ci int rc; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 40262306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT); 40362306a36Sopenharmony_ci if (rc) 40462306a36Sopenharmony_ci return rc; 40562306a36Sopenharmony_ci return apei_exec_ctx_get_output(&ctx); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cissize_t erst_get_record_count(void) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci ssize_t count; 41162306a36Sopenharmony_ci unsigned long flags; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (erst_disable) 41462306a36Sopenharmony_ci return -ENODEV; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci raw_spin_lock_irqsave(&erst_lock, flags); 41762306a36Sopenharmony_ci count = __erst_get_record_count(); 41862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&erst_lock, flags); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return count; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_count); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci#define ERST_RECORD_ID_CACHE_SIZE_MIN 16 42562306a36Sopenharmony_ci#define ERST_RECORD_ID_CACHE_SIZE_MAX 1024 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistruct erst_record_id_cache { 42862306a36Sopenharmony_ci struct mutex lock; 42962306a36Sopenharmony_ci u64 *entries; 43062306a36Sopenharmony_ci int len; 43162306a36Sopenharmony_ci int size; 43262306a36Sopenharmony_ci int refcount; 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic struct erst_record_id_cache erst_record_id_cache = { 43662306a36Sopenharmony_ci .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock), 43762306a36Sopenharmony_ci .refcount = 0, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int __erst_get_next_record_id(u64 *record_id) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct apei_exec_context ctx; 44362306a36Sopenharmony_ci int rc; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 44662306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID); 44762306a36Sopenharmony_ci if (rc) 44862306a36Sopenharmony_ci return rc; 44962306a36Sopenharmony_ci *record_id = apei_exec_ctx_get_output(&ctx); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint erst_get_record_id_begin(int *pos) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int rc; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (erst_disable) 45962306a36Sopenharmony_ci return -ENODEV; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci rc = mutex_lock_interruptible(&erst_record_id_cache.lock); 46262306a36Sopenharmony_ci if (rc) 46362306a36Sopenharmony_ci return rc; 46462306a36Sopenharmony_ci erst_record_id_cache.refcount++; 46562306a36Sopenharmony_ci mutex_unlock(&erst_record_id_cache.lock); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci *pos = 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_id_begin); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/* erst_record_id_cache.lock must be held by caller */ 47462306a36Sopenharmony_cistatic int __erst_record_id_cache_add_one(void) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci u64 id, prev_id, first_id; 47762306a36Sopenharmony_ci int i, rc; 47862306a36Sopenharmony_ci u64 *entries; 47962306a36Sopenharmony_ci unsigned long flags; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID; 48262306a36Sopenharmony_ciretry: 48362306a36Sopenharmony_ci raw_spin_lock_irqsave(&erst_lock, flags); 48462306a36Sopenharmony_ci rc = __erst_get_next_record_id(&id); 48562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&erst_lock, flags); 48662306a36Sopenharmony_ci if (rc == -ENOENT) 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci if (rc) 48962306a36Sopenharmony_ci return rc; 49062306a36Sopenharmony_ci if (id == APEI_ERST_INVALID_RECORD_ID) 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci /* can not skip current ID, or loop back to first ID */ 49362306a36Sopenharmony_ci if (id == prev_id || id == first_id) 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci if (first_id == APEI_ERST_INVALID_RECORD_ID) 49662306a36Sopenharmony_ci first_id = id; 49762306a36Sopenharmony_ci prev_id = id; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci entries = erst_record_id_cache.entries; 50062306a36Sopenharmony_ci for (i = 0; i < erst_record_id_cache.len; i++) { 50162306a36Sopenharmony_ci if (entries[i] == id) 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci /* record id already in cache, try next */ 50562306a36Sopenharmony_ci if (i < erst_record_id_cache.len) 50662306a36Sopenharmony_ci goto retry; 50762306a36Sopenharmony_ci if (erst_record_id_cache.len >= erst_record_id_cache.size) { 50862306a36Sopenharmony_ci int new_size; 50962306a36Sopenharmony_ci u64 *new_entries; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci new_size = erst_record_id_cache.size * 2; 51262306a36Sopenharmony_ci new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN, 51362306a36Sopenharmony_ci ERST_RECORD_ID_CACHE_SIZE_MAX); 51462306a36Sopenharmony_ci if (new_size <= erst_record_id_cache.size) { 51562306a36Sopenharmony_ci if (printk_ratelimit()) 51662306a36Sopenharmony_ci pr_warn(FW_WARN "too many record IDs!\n"); 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci new_entries = kvmalloc_array(new_size, sizeof(entries[0]), 52062306a36Sopenharmony_ci GFP_KERNEL); 52162306a36Sopenharmony_ci if (!new_entries) 52262306a36Sopenharmony_ci return -ENOMEM; 52362306a36Sopenharmony_ci memcpy(new_entries, entries, 52462306a36Sopenharmony_ci erst_record_id_cache.len * sizeof(entries[0])); 52562306a36Sopenharmony_ci kvfree(entries); 52662306a36Sopenharmony_ci erst_record_id_cache.entries = entries = new_entries; 52762306a36Sopenharmony_ci erst_record_id_cache.size = new_size; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci entries[i] = id; 53062306a36Sopenharmony_ci erst_record_id_cache.len++; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 1; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* 53662306a36Sopenharmony_ci * Get the record ID of an existing error record on the persistent 53762306a36Sopenharmony_ci * storage. If there is no error record on the persistent storage, the 53862306a36Sopenharmony_ci * returned record_id is APEI_ERST_INVALID_RECORD_ID. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ciint erst_get_record_id_next(int *pos, u64 *record_id) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int rc = 0; 54362306a36Sopenharmony_ci u64 *entries; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (erst_disable) 54662306a36Sopenharmony_ci return -ENODEV; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* must be enclosed by erst_get_record_id_begin/end */ 54962306a36Sopenharmony_ci BUG_ON(!erst_record_id_cache.refcount); 55062306a36Sopenharmony_ci BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci mutex_lock(&erst_record_id_cache.lock); 55362306a36Sopenharmony_ci entries = erst_record_id_cache.entries; 55462306a36Sopenharmony_ci for (; *pos < erst_record_id_cache.len; (*pos)++) 55562306a36Sopenharmony_ci if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID) 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci /* found next record id in cache */ 55862306a36Sopenharmony_ci if (*pos < erst_record_id_cache.len) { 55962306a36Sopenharmony_ci *record_id = entries[*pos]; 56062306a36Sopenharmony_ci (*pos)++; 56162306a36Sopenharmony_ci goto out_unlock; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Try to add one more record ID to cache */ 56562306a36Sopenharmony_ci rc = __erst_record_id_cache_add_one(); 56662306a36Sopenharmony_ci if (rc < 0) 56762306a36Sopenharmony_ci goto out_unlock; 56862306a36Sopenharmony_ci /* successfully add one new ID */ 56962306a36Sopenharmony_ci if (rc == 1) { 57062306a36Sopenharmony_ci *record_id = erst_record_id_cache.entries[*pos]; 57162306a36Sopenharmony_ci (*pos)++; 57262306a36Sopenharmony_ci rc = 0; 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci *pos = -1; 57562306a36Sopenharmony_ci *record_id = APEI_ERST_INVALID_RECORD_ID; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ciout_unlock: 57862306a36Sopenharmony_ci mutex_unlock(&erst_record_id_cache.lock); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return rc; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_id_next); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* erst_record_id_cache.lock must be held by caller */ 58562306a36Sopenharmony_cistatic void __erst_record_id_cache_compact(void) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci int i, wpos = 0; 58862306a36Sopenharmony_ci u64 *entries; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (erst_record_id_cache.refcount) 59162306a36Sopenharmony_ci return; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci entries = erst_record_id_cache.entries; 59462306a36Sopenharmony_ci for (i = 0; i < erst_record_id_cache.len; i++) { 59562306a36Sopenharmony_ci if (entries[i] == APEI_ERST_INVALID_RECORD_ID) 59662306a36Sopenharmony_ci continue; 59762306a36Sopenharmony_ci if (wpos != i) 59862306a36Sopenharmony_ci entries[wpos] = entries[i]; 59962306a36Sopenharmony_ci wpos++; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci erst_record_id_cache.len = wpos; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_civoid erst_get_record_id_end(void) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * erst_disable != 0 should be detected by invoker via the 60862306a36Sopenharmony_ci * return value of erst_get_record_id_begin/next, so this 60962306a36Sopenharmony_ci * function should not be called for erst_disable != 0. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci BUG_ON(erst_disable); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci mutex_lock(&erst_record_id_cache.lock); 61462306a36Sopenharmony_ci erst_record_id_cache.refcount--; 61562306a36Sopenharmony_ci BUG_ON(erst_record_id_cache.refcount < 0); 61662306a36Sopenharmony_ci __erst_record_id_cache_compact(); 61762306a36Sopenharmony_ci mutex_unlock(&erst_record_id_cache.lock); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_id_end); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int __erst_write_to_storage(u64 offset) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct apei_exec_context ctx; 62462306a36Sopenharmony_ci u64 timeout = FIRMWARE_TIMEOUT; 62562306a36Sopenharmony_ci u64 val; 62662306a36Sopenharmony_ci int rc; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 62962306a36Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE); 63062306a36Sopenharmony_ci if (rc) 63162306a36Sopenharmony_ci return rc; 63262306a36Sopenharmony_ci apei_exec_ctx_set_input(&ctx, offset); 63362306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); 63462306a36Sopenharmony_ci if (rc) 63562306a36Sopenharmony_ci return rc; 63662306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 63762306a36Sopenharmony_ci if (rc) 63862306a36Sopenharmony_ci return rc; 63962306a36Sopenharmony_ci for (;;) { 64062306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 64162306a36Sopenharmony_ci if (rc) 64262306a36Sopenharmony_ci return rc; 64362306a36Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 64462306a36Sopenharmony_ci if (!val) 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci if (erst_timedout(&timeout, SPIN_UNIT)) 64762306a36Sopenharmony_ci return -EIO; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 65062306a36Sopenharmony_ci if (rc) 65162306a36Sopenharmony_ci return rc; 65262306a36Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 65362306a36Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); 65462306a36Sopenharmony_ci if (rc) 65562306a36Sopenharmony_ci return rc; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return erst_errno(val); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int __erst_read_from_storage(u64 record_id, u64 offset) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct apei_exec_context ctx; 66362306a36Sopenharmony_ci u64 timeout = FIRMWARE_TIMEOUT; 66462306a36Sopenharmony_ci u64 val; 66562306a36Sopenharmony_ci int rc; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 66862306a36Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ); 66962306a36Sopenharmony_ci if (rc) 67062306a36Sopenharmony_ci return rc; 67162306a36Sopenharmony_ci apei_exec_ctx_set_input(&ctx, offset); 67262306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); 67362306a36Sopenharmony_ci if (rc) 67462306a36Sopenharmony_ci return rc; 67562306a36Sopenharmony_ci apei_exec_ctx_set_input(&ctx, record_id); 67662306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); 67762306a36Sopenharmony_ci if (rc) 67862306a36Sopenharmony_ci return rc; 67962306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 68062306a36Sopenharmony_ci if (rc) 68162306a36Sopenharmony_ci return rc; 68262306a36Sopenharmony_ci for (;;) { 68362306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 68462306a36Sopenharmony_ci if (rc) 68562306a36Sopenharmony_ci return rc; 68662306a36Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 68762306a36Sopenharmony_ci if (!val) 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci if (erst_timedout(&timeout, SPIN_UNIT)) 69062306a36Sopenharmony_ci return -EIO; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 69362306a36Sopenharmony_ci if (rc) 69462306a36Sopenharmony_ci return rc; 69562306a36Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 69662306a36Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); 69762306a36Sopenharmony_ci if (rc) 69862306a36Sopenharmony_ci return rc; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return erst_errno(val); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int __erst_clear_from_storage(u64 record_id) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct apei_exec_context ctx; 70662306a36Sopenharmony_ci u64 timeout = FIRMWARE_TIMEOUT; 70762306a36Sopenharmony_ci u64 val; 70862306a36Sopenharmony_ci int rc; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 71162306a36Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR); 71262306a36Sopenharmony_ci if (rc) 71362306a36Sopenharmony_ci return rc; 71462306a36Sopenharmony_ci apei_exec_ctx_set_input(&ctx, record_id); 71562306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); 71662306a36Sopenharmony_ci if (rc) 71762306a36Sopenharmony_ci return rc; 71862306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 71962306a36Sopenharmony_ci if (rc) 72062306a36Sopenharmony_ci return rc; 72162306a36Sopenharmony_ci for (;;) { 72262306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 72362306a36Sopenharmony_ci if (rc) 72462306a36Sopenharmony_ci return rc; 72562306a36Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 72662306a36Sopenharmony_ci if (!val) 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci if (erst_timedout(&timeout, SPIN_UNIT)) 72962306a36Sopenharmony_ci return -EIO; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 73262306a36Sopenharmony_ci if (rc) 73362306a36Sopenharmony_ci return rc; 73462306a36Sopenharmony_ci val = apei_exec_ctx_get_output(&ctx); 73562306a36Sopenharmony_ci rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); 73662306a36Sopenharmony_ci if (rc) 73762306a36Sopenharmony_ci return rc; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return erst_errno(val); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci/* NVRAM ERST Error Log Address Range is not supported yet */ 74362306a36Sopenharmony_cistatic void pr_unimpl_nvram(void) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci if (printk_ratelimit()) 74662306a36Sopenharmony_ci pr_warn("NVRAM ERST Log Address Range not implemented yet.\n"); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic int __erst_write_to_nvram(const struct cper_record_header *record) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci /* do not print message, because printk is not safe for NMI */ 75262306a36Sopenharmony_ci return -ENOSYS; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci pr_unimpl_nvram(); 75862306a36Sopenharmony_ci return -ENOSYS; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int __erst_clear_from_nvram(u64 record_id) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci pr_unimpl_nvram(); 76462306a36Sopenharmony_ci return -ENOSYS; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ciint erst_write(const struct cper_record_header *record) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci int rc; 77062306a36Sopenharmony_ci unsigned long flags; 77162306a36Sopenharmony_ci struct cper_record_header *rcd_erange; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (erst_disable) 77462306a36Sopenharmony_ci return -ENODEV; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE)) 77762306a36Sopenharmony_ci return -EINVAL; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (erst_erange.attr & ERST_RANGE_NVRAM) { 78062306a36Sopenharmony_ci if (!raw_spin_trylock_irqsave(&erst_lock, flags)) 78162306a36Sopenharmony_ci return -EBUSY; 78262306a36Sopenharmony_ci rc = __erst_write_to_nvram(record); 78362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&erst_lock, flags); 78462306a36Sopenharmony_ci return rc; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (record->record_length > erst_erange.size) 78862306a36Sopenharmony_ci return -EINVAL; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (!raw_spin_trylock_irqsave(&erst_lock, flags)) 79162306a36Sopenharmony_ci return -EBUSY; 79262306a36Sopenharmony_ci memcpy(erst_erange.vaddr, record, record->record_length); 79362306a36Sopenharmony_ci rcd_erange = erst_erange.vaddr; 79462306a36Sopenharmony_ci /* signature for serialization system */ 79562306a36Sopenharmony_ci memcpy(&rcd_erange->persistence_information, "ER", 2); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci rc = __erst_write_to_storage(0); 79862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&erst_lock, flags); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return rc; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_write); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int __erst_read_to_erange(u64 record_id, u64 *offset) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci int rc; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (erst_erange.attr & ERST_RANGE_NVRAM) 80962306a36Sopenharmony_ci return __erst_read_to_erange_from_nvram( 81062306a36Sopenharmony_ci record_id, offset); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci rc = __erst_read_from_storage(record_id, 0); 81362306a36Sopenharmony_ci if (rc) 81462306a36Sopenharmony_ci return rc; 81562306a36Sopenharmony_ci *offset = 0; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic ssize_t __erst_read(u64 record_id, struct cper_record_header *record, 82162306a36Sopenharmony_ci size_t buflen) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci int rc; 82462306a36Sopenharmony_ci u64 offset, len = 0; 82562306a36Sopenharmony_ci struct cper_record_header *rcd_tmp; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci rc = __erst_read_to_erange(record_id, &offset); 82862306a36Sopenharmony_ci if (rc) 82962306a36Sopenharmony_ci return rc; 83062306a36Sopenharmony_ci rcd_tmp = erst_erange.vaddr + offset; 83162306a36Sopenharmony_ci len = rcd_tmp->record_length; 83262306a36Sopenharmony_ci if (len <= buflen) 83362306a36Sopenharmony_ci memcpy(record, rcd_tmp, len); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return len; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci/* 83962306a36Sopenharmony_ci * If return value > buflen, the buffer size is not big enough, 84062306a36Sopenharmony_ci * else if return value < 0, something goes wrong, 84162306a36Sopenharmony_ci * else everything is OK, and return value is record length 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_cissize_t erst_read(u64 record_id, struct cper_record_header *record, 84462306a36Sopenharmony_ci size_t buflen) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci ssize_t len; 84762306a36Sopenharmony_ci unsigned long flags; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (erst_disable) 85062306a36Sopenharmony_ci return -ENODEV; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci raw_spin_lock_irqsave(&erst_lock, flags); 85362306a36Sopenharmony_ci len = __erst_read(record_id, record, buflen); 85462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&erst_lock, flags); 85562306a36Sopenharmony_ci return len; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_read); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic void erst_clear_cache(u64 record_id) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci int i; 86262306a36Sopenharmony_ci u64 *entries; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci mutex_lock(&erst_record_id_cache.lock); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci entries = erst_record_id_cache.entries; 86762306a36Sopenharmony_ci for (i = 0; i < erst_record_id_cache.len; i++) { 86862306a36Sopenharmony_ci if (entries[i] == record_id) 86962306a36Sopenharmony_ci entries[i] = APEI_ERST_INVALID_RECORD_ID; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci __erst_record_id_cache_compact(); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci mutex_unlock(&erst_record_id_cache.lock); 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cissize_t erst_read_record(u64 record_id, struct cper_record_header *record, 87762306a36Sopenharmony_ci size_t buflen, size_t recordlen, const guid_t *creatorid) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci ssize_t len; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* 88262306a36Sopenharmony_ci * if creatorid is NULL, read any record for erst-dbg module 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci if (creatorid == NULL) { 88562306a36Sopenharmony_ci len = erst_read(record_id, record, buflen); 88662306a36Sopenharmony_ci if (len == -ENOENT) 88762306a36Sopenharmony_ci erst_clear_cache(record_id); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return len; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci len = erst_read(record_id, record, buflen); 89362306a36Sopenharmony_ci /* 89462306a36Sopenharmony_ci * if erst_read return value is -ENOENT skip to next record_id, 89562306a36Sopenharmony_ci * and clear the record_id cache. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_ci if (len == -ENOENT) { 89862306a36Sopenharmony_ci erst_clear_cache(record_id); 89962306a36Sopenharmony_ci goto out; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (len < 0) 90362306a36Sopenharmony_ci goto out; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* 90662306a36Sopenharmony_ci * if erst_read return value is less than record head length, 90762306a36Sopenharmony_ci * consider it as -EIO, and clear the record_id cache. 90862306a36Sopenharmony_ci */ 90962306a36Sopenharmony_ci if (len < recordlen) { 91062306a36Sopenharmony_ci len = -EIO; 91162306a36Sopenharmony_ci erst_clear_cache(record_id); 91262306a36Sopenharmony_ci goto out; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* 91662306a36Sopenharmony_ci * if creatorid is not wanted, consider it as not found, 91762306a36Sopenharmony_ci * for skipping to next record_id. 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci if (!guid_equal(&record->creator_id, creatorid)) 92062306a36Sopenharmony_ci len = -ENOENT; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ciout: 92362306a36Sopenharmony_ci return len; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_read_record); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ciint erst_clear(u64 record_id) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci int rc, i; 93062306a36Sopenharmony_ci unsigned long flags; 93162306a36Sopenharmony_ci u64 *entries; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (erst_disable) 93462306a36Sopenharmony_ci return -ENODEV; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci rc = mutex_lock_interruptible(&erst_record_id_cache.lock); 93762306a36Sopenharmony_ci if (rc) 93862306a36Sopenharmony_ci return rc; 93962306a36Sopenharmony_ci raw_spin_lock_irqsave(&erst_lock, flags); 94062306a36Sopenharmony_ci if (erst_erange.attr & ERST_RANGE_NVRAM) 94162306a36Sopenharmony_ci rc = __erst_clear_from_nvram(record_id); 94262306a36Sopenharmony_ci else 94362306a36Sopenharmony_ci rc = __erst_clear_from_storage(record_id); 94462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&erst_lock, flags); 94562306a36Sopenharmony_ci if (rc) 94662306a36Sopenharmony_ci goto out; 94762306a36Sopenharmony_ci entries = erst_record_id_cache.entries; 94862306a36Sopenharmony_ci for (i = 0; i < erst_record_id_cache.len; i++) { 94962306a36Sopenharmony_ci if (entries[i] == record_id) 95062306a36Sopenharmony_ci entries[i] = APEI_ERST_INVALID_RECORD_ID; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci __erst_record_id_cache_compact(); 95362306a36Sopenharmony_ciout: 95462306a36Sopenharmony_ci mutex_unlock(&erst_record_id_cache.lock); 95562306a36Sopenharmony_ci return rc; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_clear); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int __init setup_erst_disable(char *str) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci erst_disable = 1; 96262306a36Sopenharmony_ci return 1; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci__setup("erst_disable", setup_erst_disable); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int erst_check_table(struct acpi_table_erst *erst_tab) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci if ((erst_tab->header_length != 97062306a36Sopenharmony_ci (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) 97162306a36Sopenharmony_ci && (erst_tab->header_length != sizeof(struct acpi_table_erst))) 97262306a36Sopenharmony_ci return -EINVAL; 97362306a36Sopenharmony_ci if (erst_tab->header.length < sizeof(struct acpi_table_erst)) 97462306a36Sopenharmony_ci return -EINVAL; 97562306a36Sopenharmony_ci if (erst_tab->entries != 97662306a36Sopenharmony_ci (erst_tab->header.length - sizeof(struct acpi_table_erst)) / 97762306a36Sopenharmony_ci sizeof(struct acpi_erst_entry)) 97862306a36Sopenharmony_ci return -EINVAL; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic int erst_open_pstore(struct pstore_info *psi); 98462306a36Sopenharmony_cistatic int erst_close_pstore(struct pstore_info *psi); 98562306a36Sopenharmony_cistatic ssize_t erst_reader(struct pstore_record *record); 98662306a36Sopenharmony_cistatic int erst_writer(struct pstore_record *record); 98762306a36Sopenharmony_cistatic int erst_clearer(struct pstore_record *record); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic struct pstore_info erst_info = { 99062306a36Sopenharmony_ci .owner = THIS_MODULE, 99162306a36Sopenharmony_ci .name = "erst", 99262306a36Sopenharmony_ci .flags = PSTORE_FLAGS_DMESG, 99362306a36Sopenharmony_ci .open = erst_open_pstore, 99462306a36Sopenharmony_ci .close = erst_close_pstore, 99562306a36Sopenharmony_ci .read = erst_reader, 99662306a36Sopenharmony_ci .write = erst_writer, 99762306a36Sopenharmony_ci .erase = erst_clearer 99862306a36Sopenharmony_ci}; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci#define CPER_CREATOR_PSTORE \ 100162306a36Sopenharmony_ci GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ 100262306a36Sopenharmony_ci 0x64, 0x90, 0xb8, 0x9d) 100362306a36Sopenharmony_ci#define CPER_SECTION_TYPE_DMESG \ 100462306a36Sopenharmony_ci GUID_INIT(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ 100562306a36Sopenharmony_ci 0x94, 0x19, 0xeb, 0x12) 100662306a36Sopenharmony_ci#define CPER_SECTION_TYPE_DMESG_Z \ 100762306a36Sopenharmony_ci GUID_INIT(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \ 100862306a36Sopenharmony_ci 0x34, 0xdd, 0xfa, 0xc6) 100962306a36Sopenharmony_ci#define CPER_SECTION_TYPE_MCE \ 101062306a36Sopenharmony_ci GUID_INIT(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ 101162306a36Sopenharmony_ci 0x04, 0x4a, 0x38, 0xfc) 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistruct cper_pstore_record { 101462306a36Sopenharmony_ci struct cper_record_header hdr; 101562306a36Sopenharmony_ci struct cper_section_descriptor sec_hdr; 101662306a36Sopenharmony_ci char data[]; 101762306a36Sopenharmony_ci} __packed; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic int reader_pos; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic int erst_open_pstore(struct pstore_info *psi) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci if (erst_disable) 102462306a36Sopenharmony_ci return -ENODEV; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return erst_get_record_id_begin(&reader_pos); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic int erst_close_pstore(struct pstore_info *psi) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci erst_get_record_id_end(); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return 0; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic ssize_t erst_reader(struct pstore_record *record) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci int rc; 103962306a36Sopenharmony_ci ssize_t len = 0; 104062306a36Sopenharmony_ci u64 record_id; 104162306a36Sopenharmony_ci struct cper_pstore_record *rcd; 104262306a36Sopenharmony_ci size_t rcd_len = sizeof(*rcd) + erst_info.bufsize; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (erst_disable) 104562306a36Sopenharmony_ci return -ENODEV; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci rcd = kmalloc(rcd_len, GFP_KERNEL); 104862306a36Sopenharmony_ci if (!rcd) { 104962306a36Sopenharmony_ci rc = -ENOMEM; 105062306a36Sopenharmony_ci goto out; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ciskip: 105362306a36Sopenharmony_ci rc = erst_get_record_id_next(&reader_pos, &record_id); 105462306a36Sopenharmony_ci if (rc) 105562306a36Sopenharmony_ci goto out; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* no more record */ 105862306a36Sopenharmony_ci if (record_id == APEI_ERST_INVALID_RECORD_ID) { 105962306a36Sopenharmony_ci rc = -EINVAL; 106062306a36Sopenharmony_ci goto out; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci len = erst_read_record(record_id, &rcd->hdr, rcd_len, sizeof(*rcd), 106462306a36Sopenharmony_ci &CPER_CREATOR_PSTORE); 106562306a36Sopenharmony_ci /* The record may be cleared by others, try read next record */ 106662306a36Sopenharmony_ci if (len == -ENOENT) 106762306a36Sopenharmony_ci goto skip; 106862306a36Sopenharmony_ci else if (len < 0) 106962306a36Sopenharmony_ci goto out; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci record->buf = kmalloc(len, GFP_KERNEL); 107262306a36Sopenharmony_ci if (record->buf == NULL) { 107362306a36Sopenharmony_ci rc = -ENOMEM; 107462306a36Sopenharmony_ci goto out; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci memcpy(record->buf, rcd->data, len - sizeof(*rcd)); 107762306a36Sopenharmony_ci record->id = record_id; 107862306a36Sopenharmony_ci record->compressed = false; 107962306a36Sopenharmony_ci record->ecc_notice_size = 0; 108062306a36Sopenharmony_ci if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG_Z)) { 108162306a36Sopenharmony_ci record->type = PSTORE_TYPE_DMESG; 108262306a36Sopenharmony_ci record->compressed = true; 108362306a36Sopenharmony_ci } else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG)) 108462306a36Sopenharmony_ci record->type = PSTORE_TYPE_DMESG; 108562306a36Sopenharmony_ci else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_MCE)) 108662306a36Sopenharmony_ci record->type = PSTORE_TYPE_MCE; 108762306a36Sopenharmony_ci else 108862306a36Sopenharmony_ci record->type = PSTORE_TYPE_MAX; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) 109162306a36Sopenharmony_ci record->time.tv_sec = rcd->hdr.timestamp; 109262306a36Sopenharmony_ci else 109362306a36Sopenharmony_ci record->time.tv_sec = 0; 109462306a36Sopenharmony_ci record->time.tv_nsec = 0; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ciout: 109762306a36Sopenharmony_ci kfree(rcd); 109862306a36Sopenharmony_ci return (rc < 0) ? rc : (len - sizeof(*rcd)); 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int erst_writer(struct pstore_record *record) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci struct cper_pstore_record *rcd = (struct cper_pstore_record *) 110462306a36Sopenharmony_ci (erst_info.buf - sizeof(*rcd)); 110562306a36Sopenharmony_ci int ret; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci memset(rcd, 0, sizeof(*rcd)); 110862306a36Sopenharmony_ci memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); 110962306a36Sopenharmony_ci rcd->hdr.revision = CPER_RECORD_REV; 111062306a36Sopenharmony_ci rcd->hdr.signature_end = CPER_SIG_END; 111162306a36Sopenharmony_ci rcd->hdr.section_count = 1; 111262306a36Sopenharmony_ci rcd->hdr.error_severity = CPER_SEV_FATAL; 111362306a36Sopenharmony_ci /* timestamp valid. platform_id, partition_id are invalid */ 111462306a36Sopenharmony_ci rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; 111562306a36Sopenharmony_ci rcd->hdr.timestamp = ktime_get_real_seconds(); 111662306a36Sopenharmony_ci rcd->hdr.record_length = sizeof(*rcd) + record->size; 111762306a36Sopenharmony_ci rcd->hdr.creator_id = CPER_CREATOR_PSTORE; 111862306a36Sopenharmony_ci rcd->hdr.notification_type = CPER_NOTIFY_MCE; 111962306a36Sopenharmony_ci rcd->hdr.record_id = cper_next_record_id(); 112062306a36Sopenharmony_ci rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci rcd->sec_hdr.section_offset = sizeof(*rcd); 112362306a36Sopenharmony_ci rcd->sec_hdr.section_length = record->size; 112462306a36Sopenharmony_ci rcd->sec_hdr.revision = CPER_SEC_REV; 112562306a36Sopenharmony_ci /* fru_id and fru_text is invalid */ 112662306a36Sopenharmony_ci rcd->sec_hdr.validation_bits = 0; 112762306a36Sopenharmony_ci rcd->sec_hdr.flags = CPER_SEC_PRIMARY; 112862306a36Sopenharmony_ci switch (record->type) { 112962306a36Sopenharmony_ci case PSTORE_TYPE_DMESG: 113062306a36Sopenharmony_ci if (record->compressed) 113162306a36Sopenharmony_ci rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; 113262306a36Sopenharmony_ci else 113362306a36Sopenharmony_ci rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; 113462306a36Sopenharmony_ci break; 113562306a36Sopenharmony_ci case PSTORE_TYPE_MCE: 113662306a36Sopenharmony_ci rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci default: 113962306a36Sopenharmony_ci return -EINVAL; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci rcd->sec_hdr.section_severity = CPER_SEV_FATAL; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci ret = erst_write(&rcd->hdr); 114462306a36Sopenharmony_ci record->id = rcd->hdr.record_id; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci return ret; 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int erst_clearer(struct pstore_record *record) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci return erst_clear(record->id); 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic int __init erst_init(void) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci int rc = 0; 115762306a36Sopenharmony_ci acpi_status status; 115862306a36Sopenharmony_ci struct apei_exec_context ctx; 115962306a36Sopenharmony_ci struct apei_resources erst_resources; 116062306a36Sopenharmony_ci struct resource *r; 116162306a36Sopenharmony_ci char *buf; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (acpi_disabled) 116462306a36Sopenharmony_ci goto err; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (erst_disable) { 116762306a36Sopenharmony_ci pr_info( 116862306a36Sopenharmony_ci "Error Record Serialization Table (ERST) support is disabled.\n"); 116962306a36Sopenharmony_ci goto err; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_ERST, 0, 117362306a36Sopenharmony_ci (struct acpi_table_header **)&erst_tab); 117462306a36Sopenharmony_ci if (status == AE_NOT_FOUND) 117562306a36Sopenharmony_ci goto err; 117662306a36Sopenharmony_ci else if (ACPI_FAILURE(status)) { 117762306a36Sopenharmony_ci const char *msg = acpi_format_exception(status); 117862306a36Sopenharmony_ci pr_err("Failed to get table, %s\n", msg); 117962306a36Sopenharmony_ci rc = -EINVAL; 118062306a36Sopenharmony_ci goto err; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci rc = erst_check_table(erst_tab); 118462306a36Sopenharmony_ci if (rc) { 118562306a36Sopenharmony_ci pr_err(FW_BUG "ERST table is invalid.\n"); 118662306a36Sopenharmony_ci goto err_put_erst_tab; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci apei_resources_init(&erst_resources); 119062306a36Sopenharmony_ci erst_exec_ctx_init(&ctx); 119162306a36Sopenharmony_ci rc = apei_exec_collect_resources(&ctx, &erst_resources); 119262306a36Sopenharmony_ci if (rc) 119362306a36Sopenharmony_ci goto err_fini; 119462306a36Sopenharmony_ci rc = apei_resources_request(&erst_resources, "APEI ERST"); 119562306a36Sopenharmony_ci if (rc) 119662306a36Sopenharmony_ci goto err_fini; 119762306a36Sopenharmony_ci rc = apei_exec_pre_map_gars(&ctx); 119862306a36Sopenharmony_ci if (rc) 119962306a36Sopenharmony_ci goto err_release; 120062306a36Sopenharmony_ci rc = erst_get_erange(&erst_erange); 120162306a36Sopenharmony_ci if (rc) { 120262306a36Sopenharmony_ci if (rc == -ENODEV) 120362306a36Sopenharmony_ci pr_info( 120462306a36Sopenharmony_ci "The corresponding hardware device or firmware implementation " 120562306a36Sopenharmony_ci "is not available.\n"); 120662306a36Sopenharmony_ci else 120762306a36Sopenharmony_ci pr_err("Failed to get Error Log Address Range.\n"); 120862306a36Sopenharmony_ci goto err_unmap_reg; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST"); 121262306a36Sopenharmony_ci if (!r) { 121362306a36Sopenharmony_ci pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n", 121462306a36Sopenharmony_ci (unsigned long long)erst_erange.base, 121562306a36Sopenharmony_ci (unsigned long long)erst_erange.base + erst_erange.size - 1); 121662306a36Sopenharmony_ci rc = -EIO; 121762306a36Sopenharmony_ci goto err_unmap_reg; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci rc = -ENOMEM; 122062306a36Sopenharmony_ci erst_erange.vaddr = ioremap_cache(erst_erange.base, 122162306a36Sopenharmony_ci erst_erange.size); 122262306a36Sopenharmony_ci if (!erst_erange.vaddr) 122362306a36Sopenharmony_ci goto err_release_erange; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci pr_info( 122662306a36Sopenharmony_ci "Error Record Serialization Table (ERST) support is initialized.\n"); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci buf = kmalloc(erst_erange.size, GFP_KERNEL); 122962306a36Sopenharmony_ci if (buf) { 123062306a36Sopenharmony_ci erst_info.buf = buf + sizeof(struct cper_pstore_record); 123162306a36Sopenharmony_ci erst_info.bufsize = erst_erange.size - 123262306a36Sopenharmony_ci sizeof(struct cper_pstore_record); 123362306a36Sopenharmony_ci rc = pstore_register(&erst_info); 123462306a36Sopenharmony_ci if (rc) { 123562306a36Sopenharmony_ci if (rc != -EPERM) 123662306a36Sopenharmony_ci pr_info( 123762306a36Sopenharmony_ci "Could not register with persistent store.\n"); 123862306a36Sopenharmony_ci erst_info.buf = NULL; 123962306a36Sopenharmony_ci erst_info.bufsize = 0; 124062306a36Sopenharmony_ci kfree(buf); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } else 124362306a36Sopenharmony_ci pr_err( 124462306a36Sopenharmony_ci "Failed to allocate %lld bytes for persistent store error log.\n", 124562306a36Sopenharmony_ci erst_erange.size); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* Cleanup ERST Resources */ 124862306a36Sopenharmony_ci apei_resources_fini(&erst_resources); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci return 0; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cierr_release_erange: 125362306a36Sopenharmony_ci release_mem_region(erst_erange.base, erst_erange.size); 125462306a36Sopenharmony_cierr_unmap_reg: 125562306a36Sopenharmony_ci apei_exec_post_unmap_gars(&ctx); 125662306a36Sopenharmony_cierr_release: 125762306a36Sopenharmony_ci apei_resources_release(&erst_resources); 125862306a36Sopenharmony_cierr_fini: 125962306a36Sopenharmony_ci apei_resources_fini(&erst_resources); 126062306a36Sopenharmony_cierr_put_erst_tab: 126162306a36Sopenharmony_ci acpi_put_table((struct acpi_table_header *)erst_tab); 126262306a36Sopenharmony_cierr: 126362306a36Sopenharmony_ci erst_disable = 1; 126462306a36Sopenharmony_ci return rc; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cidevice_initcall(erst_init); 1268