xref: /kernel/linux/linux-5.10/drivers/acpi/apei/erst.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * APEI Error Record Serialization Table support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * ERST is a way provided by APEI to save and retrieve hardware error
68c2ecf20Sopenharmony_ci * information to and from a persistent store.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * For more information about ERST, please refer to ACPI Specification
98c2ecf20Sopenharmony_ci * version 4.0, section 17.4.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright 2010 Intel Corp.
128c2ecf20Sopenharmony_ci *   Author: Huang Ying <ying.huang@intel.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/acpi.h>
218c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
228c2ecf20Sopenharmony_ci#include <linux/cper.h>
238c2ecf20Sopenharmony_ci#include <linux/nmi.h>
248c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
258c2ecf20Sopenharmony_ci#include <linux/pstore.h>
268c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
278c2ecf20Sopenharmony_ci#include <linux/mm.h> /* kvfree() */
288c2ecf20Sopenharmony_ci#include <acpi/apei.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "apei-internal.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#undef pr_fmt
338c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ERST: " fmt
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* ERST command status */
368c2ecf20Sopenharmony_ci#define ERST_STATUS_SUCCESS			0x0
378c2ecf20Sopenharmony_ci#define ERST_STATUS_NOT_ENOUGH_SPACE		0x1
388c2ecf20Sopenharmony_ci#define ERST_STATUS_HARDWARE_NOT_AVAILABLE	0x2
398c2ecf20Sopenharmony_ci#define ERST_STATUS_FAILED			0x3
408c2ecf20Sopenharmony_ci#define ERST_STATUS_RECORD_STORE_EMPTY		0x4
418c2ecf20Sopenharmony_ci#define ERST_STATUS_RECORD_NOT_FOUND		0x5
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define ERST_TAB_ENTRY(tab)						\
448c2ecf20Sopenharmony_ci	((struct acpi_whea_header *)((char *)(tab) +			\
458c2ecf20Sopenharmony_ci				     sizeof(struct acpi_table_erst)))
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define SPIN_UNIT		100			/* 100ns */
488c2ecf20Sopenharmony_ci/* Firmware should respond within 1 milliseconds */
498c2ecf20Sopenharmony_ci#define FIRMWARE_TIMEOUT	(1 * NSEC_PER_MSEC)
508c2ecf20Sopenharmony_ci#define FIRMWARE_MAX_STALL	50			/* 50us */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciint erst_disable;
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_disable);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct acpi_table_erst *erst_tab;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* ERST Error Log Address Range atrributes */
588c2ecf20Sopenharmony_ci#define ERST_RANGE_RESERVED	0x0001
598c2ecf20Sopenharmony_ci#define ERST_RANGE_NVRAM	0x0002
608c2ecf20Sopenharmony_ci#define ERST_RANGE_SLOW		0x0004
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/*
638c2ecf20Sopenharmony_ci * ERST Error Log Address Range, used as buffer for reading/writing
648c2ecf20Sopenharmony_ci * error records.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic struct erst_erange {
678c2ecf20Sopenharmony_ci	u64 base;
688c2ecf20Sopenharmony_ci	u64 size;
698c2ecf20Sopenharmony_ci	void __iomem *vaddr;
708c2ecf20Sopenharmony_ci	u32 attr;
718c2ecf20Sopenharmony_ci} erst_erange;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/*
748c2ecf20Sopenharmony_ci * Prevent ERST interpreter to run simultaneously, because the
758c2ecf20Sopenharmony_ci * corresponding firmware implementation may not work properly when
768c2ecf20Sopenharmony_ci * invoked simultaneously.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * It is used to provide exclusive accessing for ERST Error Log
798c2ecf20Sopenharmony_ci * Address Range too.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(erst_lock);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic inline int erst_errno(int command_status)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	switch (command_status) {
868c2ecf20Sopenharmony_ci	case ERST_STATUS_SUCCESS:
878c2ecf20Sopenharmony_ci		return 0;
888c2ecf20Sopenharmony_ci	case ERST_STATUS_HARDWARE_NOT_AVAILABLE:
898c2ecf20Sopenharmony_ci		return -ENODEV;
908c2ecf20Sopenharmony_ci	case ERST_STATUS_NOT_ENOUGH_SPACE:
918c2ecf20Sopenharmony_ci		return -ENOSPC;
928c2ecf20Sopenharmony_ci	case ERST_STATUS_RECORD_STORE_EMPTY:
938c2ecf20Sopenharmony_ci	case ERST_STATUS_RECORD_NOT_FOUND:
948c2ecf20Sopenharmony_ci		return -ENOENT;
958c2ecf20Sopenharmony_ci	default:
968c2ecf20Sopenharmony_ci		return -EINVAL;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int erst_timedout(u64 *t, u64 spin_unit)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	if ((s64)*t < spin_unit) {
1038c2ecf20Sopenharmony_ci		pr_warn(FW_WARN "Firmware does not respond in time.\n");
1048c2ecf20Sopenharmony_ci		return 1;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	*t -= spin_unit;
1078c2ecf20Sopenharmony_ci	ndelay(spin_unit);
1088c2ecf20Sopenharmony_ci	touch_nmi_watchdog();
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int erst_exec_load_var1(struct apei_exec_context *ctx,
1138c2ecf20Sopenharmony_ci			       struct acpi_whea_header *entry)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	return __apei_exec_read_register(entry, &ctx->var1);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int erst_exec_load_var2(struct apei_exec_context *ctx,
1198c2ecf20Sopenharmony_ci			       struct acpi_whea_header *entry)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	return __apei_exec_read_register(entry, &ctx->var2);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int erst_exec_store_var1(struct apei_exec_context *ctx,
1258c2ecf20Sopenharmony_ci				struct acpi_whea_header *entry)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return __apei_exec_write_register(entry, ctx->var1);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int erst_exec_add(struct apei_exec_context *ctx,
1318c2ecf20Sopenharmony_ci			 struct acpi_whea_header *entry)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	ctx->var1 += ctx->var2;
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int erst_exec_subtract(struct apei_exec_context *ctx,
1388c2ecf20Sopenharmony_ci			      struct acpi_whea_header *entry)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	ctx->var1 -= ctx->var2;
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int erst_exec_add_value(struct apei_exec_context *ctx,
1458c2ecf20Sopenharmony_ci			       struct acpi_whea_header *entry)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int rc;
1488c2ecf20Sopenharmony_ci	u64 val;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	rc = __apei_exec_read_register(entry, &val);
1518c2ecf20Sopenharmony_ci	if (rc)
1528c2ecf20Sopenharmony_ci		return rc;
1538c2ecf20Sopenharmony_ci	val += ctx->value;
1548c2ecf20Sopenharmony_ci	rc = __apei_exec_write_register(entry, val);
1558c2ecf20Sopenharmony_ci	return rc;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int erst_exec_subtract_value(struct apei_exec_context *ctx,
1598c2ecf20Sopenharmony_ci				    struct acpi_whea_header *entry)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	int rc;
1628c2ecf20Sopenharmony_ci	u64 val;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	rc = __apei_exec_read_register(entry, &val);
1658c2ecf20Sopenharmony_ci	if (rc)
1668c2ecf20Sopenharmony_ci		return rc;
1678c2ecf20Sopenharmony_ci	val -= ctx->value;
1688c2ecf20Sopenharmony_ci	rc = __apei_exec_write_register(entry, val);
1698c2ecf20Sopenharmony_ci	return rc;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int erst_exec_stall(struct apei_exec_context *ctx,
1738c2ecf20Sopenharmony_ci			   struct acpi_whea_header *entry)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	u64 stall_time;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (ctx->value > FIRMWARE_MAX_STALL) {
1788c2ecf20Sopenharmony_ci		if (!in_nmi())
1798c2ecf20Sopenharmony_ci			pr_warn(FW_WARN
1808c2ecf20Sopenharmony_ci			"Too long stall time for stall instruction: 0x%llx.\n",
1818c2ecf20Sopenharmony_ci				   ctx->value);
1828c2ecf20Sopenharmony_ci		stall_time = FIRMWARE_MAX_STALL;
1838c2ecf20Sopenharmony_ci	} else
1848c2ecf20Sopenharmony_ci		stall_time = ctx->value;
1858c2ecf20Sopenharmony_ci	udelay(stall_time);
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int erst_exec_stall_while_true(struct apei_exec_context *ctx,
1908c2ecf20Sopenharmony_ci				      struct acpi_whea_header *entry)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	int rc;
1938c2ecf20Sopenharmony_ci	u64 val;
1948c2ecf20Sopenharmony_ci	u64 timeout = FIRMWARE_TIMEOUT;
1958c2ecf20Sopenharmony_ci	u64 stall_time;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (ctx->var1 > FIRMWARE_MAX_STALL) {
1988c2ecf20Sopenharmony_ci		if (!in_nmi())
1998c2ecf20Sopenharmony_ci			pr_warn(FW_WARN
2008c2ecf20Sopenharmony_ci		"Too long stall time for stall while true instruction: 0x%llx.\n",
2018c2ecf20Sopenharmony_ci				   ctx->var1);
2028c2ecf20Sopenharmony_ci		stall_time = FIRMWARE_MAX_STALL;
2038c2ecf20Sopenharmony_ci	} else
2048c2ecf20Sopenharmony_ci		stall_time = ctx->var1;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	for (;;) {
2078c2ecf20Sopenharmony_ci		rc = __apei_exec_read_register(entry, &val);
2088c2ecf20Sopenharmony_ci		if (rc)
2098c2ecf20Sopenharmony_ci			return rc;
2108c2ecf20Sopenharmony_ci		if (val != ctx->value)
2118c2ecf20Sopenharmony_ci			break;
2128c2ecf20Sopenharmony_ci		if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC))
2138c2ecf20Sopenharmony_ci			return -EIO;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic int erst_exec_skip_next_instruction_if_true(
2198c2ecf20Sopenharmony_ci	struct apei_exec_context *ctx,
2208c2ecf20Sopenharmony_ci	struct acpi_whea_header *entry)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	int rc;
2238c2ecf20Sopenharmony_ci	u64 val;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	rc = __apei_exec_read_register(entry, &val);
2268c2ecf20Sopenharmony_ci	if (rc)
2278c2ecf20Sopenharmony_ci		return rc;
2288c2ecf20Sopenharmony_ci	if (val == ctx->value) {
2298c2ecf20Sopenharmony_ci		ctx->ip += 2;
2308c2ecf20Sopenharmony_ci		return APEI_EXEC_SET_IP;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int erst_exec_goto(struct apei_exec_context *ctx,
2378c2ecf20Sopenharmony_ci			  struct acpi_whea_header *entry)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	ctx->ip = ctx->value;
2408c2ecf20Sopenharmony_ci	return APEI_EXEC_SET_IP;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int erst_exec_set_src_address_base(struct apei_exec_context *ctx,
2448c2ecf20Sopenharmony_ci					  struct acpi_whea_header *entry)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	return __apei_exec_read_register(entry, &ctx->src_base);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,
2508c2ecf20Sopenharmony_ci					  struct acpi_whea_header *entry)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	return __apei_exec_read_register(entry, &ctx->dst_base);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int erst_exec_move_data(struct apei_exec_context *ctx,
2568c2ecf20Sopenharmony_ci			       struct acpi_whea_header *entry)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int rc;
2598c2ecf20Sopenharmony_ci	u64 offset;
2608c2ecf20Sopenharmony_ci	void *src, *dst;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* ioremap does not work in interrupt context */
2638c2ecf20Sopenharmony_ci	if (in_interrupt()) {
2648c2ecf20Sopenharmony_ci		pr_warn("MOVE_DATA can not be used in interrupt context.\n");
2658c2ecf20Sopenharmony_ci		return -EBUSY;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	rc = __apei_exec_read_register(entry, &offset);
2698c2ecf20Sopenharmony_ci	if (rc)
2708c2ecf20Sopenharmony_ci		return rc;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	src = ioremap(ctx->src_base + offset, ctx->var2);
2738c2ecf20Sopenharmony_ci	if (!src)
2748c2ecf20Sopenharmony_ci		return -ENOMEM;
2758c2ecf20Sopenharmony_ci	dst = ioremap(ctx->dst_base + offset, ctx->var2);
2768c2ecf20Sopenharmony_ci	if (!dst) {
2778c2ecf20Sopenharmony_ci		iounmap(src);
2788c2ecf20Sopenharmony_ci		return -ENOMEM;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	memmove(dst, src, ctx->var2);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	iounmap(src);
2848c2ecf20Sopenharmony_ci	iounmap(dst);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic struct apei_exec_ins_type erst_ins_type[] = {
2908c2ecf20Sopenharmony_ci	[ACPI_ERST_READ_REGISTER] = {
2918c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
2928c2ecf20Sopenharmony_ci		.run = apei_exec_read_register,
2938c2ecf20Sopenharmony_ci	},
2948c2ecf20Sopenharmony_ci	[ACPI_ERST_READ_REGISTER_VALUE] = {
2958c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
2968c2ecf20Sopenharmony_ci		.run = apei_exec_read_register_value,
2978c2ecf20Sopenharmony_ci	},
2988c2ecf20Sopenharmony_ci	[ACPI_ERST_WRITE_REGISTER] = {
2998c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3008c2ecf20Sopenharmony_ci		.run = apei_exec_write_register,
3018c2ecf20Sopenharmony_ci	},
3028c2ecf20Sopenharmony_ci	[ACPI_ERST_WRITE_REGISTER_VALUE] = {
3038c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3048c2ecf20Sopenharmony_ci		.run = apei_exec_write_register_value,
3058c2ecf20Sopenharmony_ci	},
3068c2ecf20Sopenharmony_ci	[ACPI_ERST_NOOP] = {
3078c2ecf20Sopenharmony_ci		.flags = 0,
3088c2ecf20Sopenharmony_ci		.run = apei_exec_noop,
3098c2ecf20Sopenharmony_ci	},
3108c2ecf20Sopenharmony_ci	[ACPI_ERST_LOAD_VAR1] = {
3118c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3128c2ecf20Sopenharmony_ci		.run = erst_exec_load_var1,
3138c2ecf20Sopenharmony_ci	},
3148c2ecf20Sopenharmony_ci	[ACPI_ERST_LOAD_VAR2] = {
3158c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3168c2ecf20Sopenharmony_ci		.run = erst_exec_load_var2,
3178c2ecf20Sopenharmony_ci	},
3188c2ecf20Sopenharmony_ci	[ACPI_ERST_STORE_VAR1] = {
3198c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3208c2ecf20Sopenharmony_ci		.run = erst_exec_store_var1,
3218c2ecf20Sopenharmony_ci	},
3228c2ecf20Sopenharmony_ci	[ACPI_ERST_ADD] = {
3238c2ecf20Sopenharmony_ci		.flags = 0,
3248c2ecf20Sopenharmony_ci		.run = erst_exec_add,
3258c2ecf20Sopenharmony_ci	},
3268c2ecf20Sopenharmony_ci	[ACPI_ERST_SUBTRACT] = {
3278c2ecf20Sopenharmony_ci		.flags = 0,
3288c2ecf20Sopenharmony_ci		.run = erst_exec_subtract,
3298c2ecf20Sopenharmony_ci	},
3308c2ecf20Sopenharmony_ci	[ACPI_ERST_ADD_VALUE] = {
3318c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3328c2ecf20Sopenharmony_ci		.run = erst_exec_add_value,
3338c2ecf20Sopenharmony_ci	},
3348c2ecf20Sopenharmony_ci	[ACPI_ERST_SUBTRACT_VALUE] = {
3358c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3368c2ecf20Sopenharmony_ci		.run = erst_exec_subtract_value,
3378c2ecf20Sopenharmony_ci	},
3388c2ecf20Sopenharmony_ci	[ACPI_ERST_STALL] = {
3398c2ecf20Sopenharmony_ci		.flags = 0,
3408c2ecf20Sopenharmony_ci		.run = erst_exec_stall,
3418c2ecf20Sopenharmony_ci	},
3428c2ecf20Sopenharmony_ci	[ACPI_ERST_STALL_WHILE_TRUE] = {
3438c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3448c2ecf20Sopenharmony_ci		.run = erst_exec_stall_while_true,
3458c2ecf20Sopenharmony_ci	},
3468c2ecf20Sopenharmony_ci	[ACPI_ERST_SKIP_NEXT_IF_TRUE] = {
3478c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3488c2ecf20Sopenharmony_ci		.run = erst_exec_skip_next_instruction_if_true,
3498c2ecf20Sopenharmony_ci	},
3508c2ecf20Sopenharmony_ci	[ACPI_ERST_GOTO] = {
3518c2ecf20Sopenharmony_ci		.flags = 0,
3528c2ecf20Sopenharmony_ci		.run = erst_exec_goto,
3538c2ecf20Sopenharmony_ci	},
3548c2ecf20Sopenharmony_ci	[ACPI_ERST_SET_SRC_ADDRESS_BASE] = {
3558c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3568c2ecf20Sopenharmony_ci		.run = erst_exec_set_src_address_base,
3578c2ecf20Sopenharmony_ci	},
3588c2ecf20Sopenharmony_ci	[ACPI_ERST_SET_DST_ADDRESS_BASE] = {
3598c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3608c2ecf20Sopenharmony_ci		.run = erst_exec_set_dst_address_base,
3618c2ecf20Sopenharmony_ci	},
3628c2ecf20Sopenharmony_ci	[ACPI_ERST_MOVE_DATA] = {
3638c2ecf20Sopenharmony_ci		.flags = APEI_EXEC_INS_ACCESS_REGISTER,
3648c2ecf20Sopenharmony_ci		.run = erst_exec_move_data,
3658c2ecf20Sopenharmony_ci	},
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic inline void erst_exec_ctx_init(struct apei_exec_context *ctx)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),
3718c2ecf20Sopenharmony_ci			   ERST_TAB_ENTRY(erst_tab), erst_tab->entries);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic int erst_get_erange(struct erst_erange *range)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
3778c2ecf20Sopenharmony_ci	int rc;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
3808c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);
3818c2ecf20Sopenharmony_ci	if (rc)
3828c2ecf20Sopenharmony_ci		return rc;
3838c2ecf20Sopenharmony_ci	range->base = apei_exec_ctx_get_output(&ctx);
3848c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);
3858c2ecf20Sopenharmony_ci	if (rc)
3868c2ecf20Sopenharmony_ci		return rc;
3878c2ecf20Sopenharmony_ci	range->size = apei_exec_ctx_get_output(&ctx);
3888c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);
3898c2ecf20Sopenharmony_ci	if (rc)
3908c2ecf20Sopenharmony_ci		return rc;
3918c2ecf20Sopenharmony_ci	range->attr = apei_exec_ctx_get_output(&ctx);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return 0;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic ssize_t __erst_get_record_count(void)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
3998c2ecf20Sopenharmony_ci	int rc;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
4028c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);
4038c2ecf20Sopenharmony_ci	if (rc)
4048c2ecf20Sopenharmony_ci		return rc;
4058c2ecf20Sopenharmony_ci	return apei_exec_ctx_get_output(&ctx);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cissize_t erst_get_record_count(void)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	ssize_t count;
4118c2ecf20Sopenharmony_ci	unsigned long flags;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (erst_disable)
4148c2ecf20Sopenharmony_ci		return -ENODEV;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&erst_lock, flags);
4178c2ecf20Sopenharmony_ci	count = __erst_get_record_count();
4188c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&erst_lock, flags);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return count;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_count);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci#define ERST_RECORD_ID_CACHE_SIZE_MIN	16
4258c2ecf20Sopenharmony_ci#define ERST_RECORD_ID_CACHE_SIZE_MAX	1024
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistruct erst_record_id_cache {
4288c2ecf20Sopenharmony_ci	struct mutex lock;
4298c2ecf20Sopenharmony_ci	u64 *entries;
4308c2ecf20Sopenharmony_ci	int len;
4318c2ecf20Sopenharmony_ci	int size;
4328c2ecf20Sopenharmony_ci	int refcount;
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic struct erst_record_id_cache erst_record_id_cache = {
4368c2ecf20Sopenharmony_ci	.lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock),
4378c2ecf20Sopenharmony_ci	.refcount = 0,
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int __erst_get_next_record_id(u64 *record_id)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
4438c2ecf20Sopenharmony_ci	int rc;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
4468c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);
4478c2ecf20Sopenharmony_ci	if (rc)
4488c2ecf20Sopenharmony_ci		return rc;
4498c2ecf20Sopenharmony_ci	*record_id = apei_exec_ctx_get_output(&ctx);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ciint erst_get_record_id_begin(int *pos)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	int rc;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (erst_disable)
4598c2ecf20Sopenharmony_ci		return -ENODEV;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
4628c2ecf20Sopenharmony_ci	if (rc)
4638c2ecf20Sopenharmony_ci		return rc;
4648c2ecf20Sopenharmony_ci	erst_record_id_cache.refcount++;
4658c2ecf20Sopenharmony_ci	mutex_unlock(&erst_record_id_cache.lock);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	*pos = 0;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	return 0;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_id_begin);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/* erst_record_id_cache.lock must be held by caller */
4748c2ecf20Sopenharmony_cistatic int __erst_record_id_cache_add_one(void)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	u64 id, prev_id, first_id;
4778c2ecf20Sopenharmony_ci	int i, rc;
4788c2ecf20Sopenharmony_ci	u64 *entries;
4798c2ecf20Sopenharmony_ci	unsigned long flags;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID;
4828c2ecf20Sopenharmony_ciretry:
4838c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&erst_lock, flags);
4848c2ecf20Sopenharmony_ci	rc = __erst_get_next_record_id(&id);
4858c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&erst_lock, flags);
4868c2ecf20Sopenharmony_ci	if (rc == -ENOENT)
4878c2ecf20Sopenharmony_ci		return 0;
4888c2ecf20Sopenharmony_ci	if (rc)
4898c2ecf20Sopenharmony_ci		return rc;
4908c2ecf20Sopenharmony_ci	if (id == APEI_ERST_INVALID_RECORD_ID)
4918c2ecf20Sopenharmony_ci		return 0;
4928c2ecf20Sopenharmony_ci	/* can not skip current ID, or loop back to first ID */
4938c2ecf20Sopenharmony_ci	if (id == prev_id || id == first_id)
4948c2ecf20Sopenharmony_ci		return 0;
4958c2ecf20Sopenharmony_ci	if (first_id == APEI_ERST_INVALID_RECORD_ID)
4968c2ecf20Sopenharmony_ci		first_id = id;
4978c2ecf20Sopenharmony_ci	prev_id = id;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	entries = erst_record_id_cache.entries;
5008c2ecf20Sopenharmony_ci	for (i = 0; i < erst_record_id_cache.len; i++) {
5018c2ecf20Sopenharmony_ci		if (entries[i] == id)
5028c2ecf20Sopenharmony_ci			break;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	/* record id already in cache, try next */
5058c2ecf20Sopenharmony_ci	if (i < erst_record_id_cache.len)
5068c2ecf20Sopenharmony_ci		goto retry;
5078c2ecf20Sopenharmony_ci	if (erst_record_id_cache.len >= erst_record_id_cache.size) {
5088c2ecf20Sopenharmony_ci		int new_size;
5098c2ecf20Sopenharmony_ci		u64 *new_entries;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		new_size = erst_record_id_cache.size * 2;
5128c2ecf20Sopenharmony_ci		new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN,
5138c2ecf20Sopenharmony_ci				     ERST_RECORD_ID_CACHE_SIZE_MAX);
5148c2ecf20Sopenharmony_ci		if (new_size <= erst_record_id_cache.size) {
5158c2ecf20Sopenharmony_ci			if (printk_ratelimit())
5168c2ecf20Sopenharmony_ci				pr_warn(FW_WARN "too many record IDs!\n");
5178c2ecf20Sopenharmony_ci			return 0;
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci		new_entries = kvmalloc_array(new_size, sizeof(entries[0]),
5208c2ecf20Sopenharmony_ci					     GFP_KERNEL);
5218c2ecf20Sopenharmony_ci		if (!new_entries)
5228c2ecf20Sopenharmony_ci			return -ENOMEM;
5238c2ecf20Sopenharmony_ci		memcpy(new_entries, entries,
5248c2ecf20Sopenharmony_ci		       erst_record_id_cache.len * sizeof(entries[0]));
5258c2ecf20Sopenharmony_ci		kvfree(entries);
5268c2ecf20Sopenharmony_ci		erst_record_id_cache.entries = entries = new_entries;
5278c2ecf20Sopenharmony_ci		erst_record_id_cache.size = new_size;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	entries[i] = id;
5308c2ecf20Sopenharmony_ci	erst_record_id_cache.len++;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return 1;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci/*
5368c2ecf20Sopenharmony_ci * Get the record ID of an existing error record on the persistent
5378c2ecf20Sopenharmony_ci * storage. If there is no error record on the persistent storage, the
5388c2ecf20Sopenharmony_ci * returned record_id is APEI_ERST_INVALID_RECORD_ID.
5398c2ecf20Sopenharmony_ci */
5408c2ecf20Sopenharmony_ciint erst_get_record_id_next(int *pos, u64 *record_id)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	int rc = 0;
5438c2ecf20Sopenharmony_ci	u64 *entries;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (erst_disable)
5468c2ecf20Sopenharmony_ci		return -ENODEV;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* must be enclosed by erst_get_record_id_begin/end */
5498c2ecf20Sopenharmony_ci	BUG_ON(!erst_record_id_cache.refcount);
5508c2ecf20Sopenharmony_ci	BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	mutex_lock(&erst_record_id_cache.lock);
5538c2ecf20Sopenharmony_ci	entries = erst_record_id_cache.entries;
5548c2ecf20Sopenharmony_ci	for (; *pos < erst_record_id_cache.len; (*pos)++)
5558c2ecf20Sopenharmony_ci		if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID)
5568c2ecf20Sopenharmony_ci			break;
5578c2ecf20Sopenharmony_ci	/* found next record id in cache */
5588c2ecf20Sopenharmony_ci	if (*pos < erst_record_id_cache.len) {
5598c2ecf20Sopenharmony_ci		*record_id = entries[*pos];
5608c2ecf20Sopenharmony_ci		(*pos)++;
5618c2ecf20Sopenharmony_ci		goto out_unlock;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* Try to add one more record ID to cache */
5658c2ecf20Sopenharmony_ci	rc = __erst_record_id_cache_add_one();
5668c2ecf20Sopenharmony_ci	if (rc < 0)
5678c2ecf20Sopenharmony_ci		goto out_unlock;
5688c2ecf20Sopenharmony_ci	/* successfully add one new ID */
5698c2ecf20Sopenharmony_ci	if (rc == 1) {
5708c2ecf20Sopenharmony_ci		*record_id = erst_record_id_cache.entries[*pos];
5718c2ecf20Sopenharmony_ci		(*pos)++;
5728c2ecf20Sopenharmony_ci		rc = 0;
5738c2ecf20Sopenharmony_ci	} else {
5748c2ecf20Sopenharmony_ci		*pos = -1;
5758c2ecf20Sopenharmony_ci		*record_id = APEI_ERST_INVALID_RECORD_ID;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ciout_unlock:
5788c2ecf20Sopenharmony_ci	mutex_unlock(&erst_record_id_cache.lock);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return rc;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_id_next);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci/* erst_record_id_cache.lock must be held by caller */
5858c2ecf20Sopenharmony_cistatic void __erst_record_id_cache_compact(void)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	int i, wpos = 0;
5888c2ecf20Sopenharmony_ci	u64 *entries;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (erst_record_id_cache.refcount)
5918c2ecf20Sopenharmony_ci		return;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	entries = erst_record_id_cache.entries;
5948c2ecf20Sopenharmony_ci	for (i = 0; i < erst_record_id_cache.len; i++) {
5958c2ecf20Sopenharmony_ci		if (entries[i] == APEI_ERST_INVALID_RECORD_ID)
5968c2ecf20Sopenharmony_ci			continue;
5978c2ecf20Sopenharmony_ci		if (wpos != i)
5988c2ecf20Sopenharmony_ci			entries[wpos] = entries[i];
5998c2ecf20Sopenharmony_ci		wpos++;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci	erst_record_id_cache.len = wpos;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_civoid erst_get_record_id_end(void)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	/*
6078c2ecf20Sopenharmony_ci	 * erst_disable != 0 should be detected by invoker via the
6088c2ecf20Sopenharmony_ci	 * return value of erst_get_record_id_begin/next, so this
6098c2ecf20Sopenharmony_ci	 * function should not be called for erst_disable != 0.
6108c2ecf20Sopenharmony_ci	 */
6118c2ecf20Sopenharmony_ci	BUG_ON(erst_disable);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	mutex_lock(&erst_record_id_cache.lock);
6148c2ecf20Sopenharmony_ci	erst_record_id_cache.refcount--;
6158c2ecf20Sopenharmony_ci	BUG_ON(erst_record_id_cache.refcount < 0);
6168c2ecf20Sopenharmony_ci	__erst_record_id_cache_compact();
6178c2ecf20Sopenharmony_ci	mutex_unlock(&erst_record_id_cache.lock);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_get_record_id_end);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic int __erst_write_to_storage(u64 offset)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
6248c2ecf20Sopenharmony_ci	u64 timeout = FIRMWARE_TIMEOUT;
6258c2ecf20Sopenharmony_ci	u64 val;
6268c2ecf20Sopenharmony_ci	int rc;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
6298c2ecf20Sopenharmony_ci	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);
6308c2ecf20Sopenharmony_ci	if (rc)
6318c2ecf20Sopenharmony_ci		return rc;
6328c2ecf20Sopenharmony_ci	apei_exec_ctx_set_input(&ctx, offset);
6338c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
6348c2ecf20Sopenharmony_ci	if (rc)
6358c2ecf20Sopenharmony_ci		return rc;
6368c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
6378c2ecf20Sopenharmony_ci	if (rc)
6388c2ecf20Sopenharmony_ci		return rc;
6398c2ecf20Sopenharmony_ci	for (;;) {
6408c2ecf20Sopenharmony_ci		rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
6418c2ecf20Sopenharmony_ci		if (rc)
6428c2ecf20Sopenharmony_ci			return rc;
6438c2ecf20Sopenharmony_ci		val = apei_exec_ctx_get_output(&ctx);
6448c2ecf20Sopenharmony_ci		if (!val)
6458c2ecf20Sopenharmony_ci			break;
6468c2ecf20Sopenharmony_ci		if (erst_timedout(&timeout, SPIN_UNIT))
6478c2ecf20Sopenharmony_ci			return -EIO;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
6508c2ecf20Sopenharmony_ci	if (rc)
6518c2ecf20Sopenharmony_ci		return rc;
6528c2ecf20Sopenharmony_ci	val = apei_exec_ctx_get_output(&ctx);
6538c2ecf20Sopenharmony_ci	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
6548c2ecf20Sopenharmony_ci	if (rc)
6558c2ecf20Sopenharmony_ci		return rc;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return erst_errno(val);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int __erst_read_from_storage(u64 record_id, u64 offset)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
6638c2ecf20Sopenharmony_ci	u64 timeout = FIRMWARE_TIMEOUT;
6648c2ecf20Sopenharmony_ci	u64 val;
6658c2ecf20Sopenharmony_ci	int rc;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
6688c2ecf20Sopenharmony_ci	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);
6698c2ecf20Sopenharmony_ci	if (rc)
6708c2ecf20Sopenharmony_ci		return rc;
6718c2ecf20Sopenharmony_ci	apei_exec_ctx_set_input(&ctx, offset);
6728c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
6738c2ecf20Sopenharmony_ci	if (rc)
6748c2ecf20Sopenharmony_ci		return rc;
6758c2ecf20Sopenharmony_ci	apei_exec_ctx_set_input(&ctx, record_id);
6768c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
6778c2ecf20Sopenharmony_ci	if (rc)
6788c2ecf20Sopenharmony_ci		return rc;
6798c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
6808c2ecf20Sopenharmony_ci	if (rc)
6818c2ecf20Sopenharmony_ci		return rc;
6828c2ecf20Sopenharmony_ci	for (;;) {
6838c2ecf20Sopenharmony_ci		rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
6848c2ecf20Sopenharmony_ci		if (rc)
6858c2ecf20Sopenharmony_ci			return rc;
6868c2ecf20Sopenharmony_ci		val = apei_exec_ctx_get_output(&ctx);
6878c2ecf20Sopenharmony_ci		if (!val)
6888c2ecf20Sopenharmony_ci			break;
6898c2ecf20Sopenharmony_ci		if (erst_timedout(&timeout, SPIN_UNIT))
6908c2ecf20Sopenharmony_ci			return -EIO;
6918c2ecf20Sopenharmony_ci	};
6928c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
6938c2ecf20Sopenharmony_ci	if (rc)
6948c2ecf20Sopenharmony_ci		return rc;
6958c2ecf20Sopenharmony_ci	val = apei_exec_ctx_get_output(&ctx);
6968c2ecf20Sopenharmony_ci	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
6978c2ecf20Sopenharmony_ci	if (rc)
6988c2ecf20Sopenharmony_ci		return rc;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	return erst_errno(val);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic int __erst_clear_from_storage(u64 record_id)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
7068c2ecf20Sopenharmony_ci	u64 timeout = FIRMWARE_TIMEOUT;
7078c2ecf20Sopenharmony_ci	u64 val;
7088c2ecf20Sopenharmony_ci	int rc;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
7118c2ecf20Sopenharmony_ci	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);
7128c2ecf20Sopenharmony_ci	if (rc)
7138c2ecf20Sopenharmony_ci		return rc;
7148c2ecf20Sopenharmony_ci	apei_exec_ctx_set_input(&ctx, record_id);
7158c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
7168c2ecf20Sopenharmony_ci	if (rc)
7178c2ecf20Sopenharmony_ci		return rc;
7188c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
7198c2ecf20Sopenharmony_ci	if (rc)
7208c2ecf20Sopenharmony_ci		return rc;
7218c2ecf20Sopenharmony_ci	for (;;) {
7228c2ecf20Sopenharmony_ci		rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
7238c2ecf20Sopenharmony_ci		if (rc)
7248c2ecf20Sopenharmony_ci			return rc;
7258c2ecf20Sopenharmony_ci		val = apei_exec_ctx_get_output(&ctx);
7268c2ecf20Sopenharmony_ci		if (!val)
7278c2ecf20Sopenharmony_ci			break;
7288c2ecf20Sopenharmony_ci		if (erst_timedout(&timeout, SPIN_UNIT))
7298c2ecf20Sopenharmony_ci			return -EIO;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci	rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
7328c2ecf20Sopenharmony_ci	if (rc)
7338c2ecf20Sopenharmony_ci		return rc;
7348c2ecf20Sopenharmony_ci	val = apei_exec_ctx_get_output(&ctx);
7358c2ecf20Sopenharmony_ci	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
7368c2ecf20Sopenharmony_ci	if (rc)
7378c2ecf20Sopenharmony_ci		return rc;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	return erst_errno(val);
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci/* NVRAM ERST Error Log Address Range is not supported yet */
7438c2ecf20Sopenharmony_cistatic void pr_unimpl_nvram(void)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	if (printk_ratelimit())
7468c2ecf20Sopenharmony_ci		pr_warn("NVRAM ERST Log Address Range not implemented yet.\n");
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int __erst_write_to_nvram(const struct cper_record_header *record)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	/* do not print message, because printk is not safe for NMI */
7528c2ecf20Sopenharmony_ci	return -ENOSYS;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	pr_unimpl_nvram();
7588c2ecf20Sopenharmony_ci	return -ENOSYS;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic int __erst_clear_from_nvram(u64 record_id)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	pr_unimpl_nvram();
7648c2ecf20Sopenharmony_ci	return -ENOSYS;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ciint erst_write(const struct cper_record_header *record)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	int rc;
7708c2ecf20Sopenharmony_ci	unsigned long flags;
7718c2ecf20Sopenharmony_ci	struct cper_record_header *rcd_erange;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	if (erst_disable)
7748c2ecf20Sopenharmony_ci		return -ENODEV;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))
7778c2ecf20Sopenharmony_ci		return -EINVAL;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (erst_erange.attr & ERST_RANGE_NVRAM) {
7808c2ecf20Sopenharmony_ci		if (!raw_spin_trylock_irqsave(&erst_lock, flags))
7818c2ecf20Sopenharmony_ci			return -EBUSY;
7828c2ecf20Sopenharmony_ci		rc = __erst_write_to_nvram(record);
7838c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&erst_lock, flags);
7848c2ecf20Sopenharmony_ci		return rc;
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (record->record_length > erst_erange.size)
7888c2ecf20Sopenharmony_ci		return -EINVAL;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	if (!raw_spin_trylock_irqsave(&erst_lock, flags))
7918c2ecf20Sopenharmony_ci		return -EBUSY;
7928c2ecf20Sopenharmony_ci	memcpy(erst_erange.vaddr, record, record->record_length);
7938c2ecf20Sopenharmony_ci	rcd_erange = erst_erange.vaddr;
7948c2ecf20Sopenharmony_ci	/* signature for serialization system */
7958c2ecf20Sopenharmony_ci	memcpy(&rcd_erange->persistence_information, "ER", 2);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	rc = __erst_write_to_storage(0);
7988c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&erst_lock, flags);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	return rc;
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_write);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic int __erst_read_to_erange(u64 record_id, u64 *offset)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	int rc;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (erst_erange.attr & ERST_RANGE_NVRAM)
8098c2ecf20Sopenharmony_ci		return __erst_read_to_erange_from_nvram(
8108c2ecf20Sopenharmony_ci			record_id, offset);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	rc = __erst_read_from_storage(record_id, 0);
8138c2ecf20Sopenharmony_ci	if (rc)
8148c2ecf20Sopenharmony_ci		return rc;
8158c2ecf20Sopenharmony_ci	*offset = 0;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	return 0;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic ssize_t __erst_read(u64 record_id, struct cper_record_header *record,
8218c2ecf20Sopenharmony_ci			   size_t buflen)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	int rc;
8248c2ecf20Sopenharmony_ci	u64 offset, len = 0;
8258c2ecf20Sopenharmony_ci	struct cper_record_header *rcd_tmp;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	rc = __erst_read_to_erange(record_id, &offset);
8288c2ecf20Sopenharmony_ci	if (rc)
8298c2ecf20Sopenharmony_ci		return rc;
8308c2ecf20Sopenharmony_ci	rcd_tmp = erst_erange.vaddr + offset;
8318c2ecf20Sopenharmony_ci	len = rcd_tmp->record_length;
8328c2ecf20Sopenharmony_ci	if (len <= buflen)
8338c2ecf20Sopenharmony_ci		memcpy(record, rcd_tmp, len);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	return len;
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci/*
8398c2ecf20Sopenharmony_ci * If return value > buflen, the buffer size is not big enough,
8408c2ecf20Sopenharmony_ci * else if return value < 0, something goes wrong,
8418c2ecf20Sopenharmony_ci * else everything is OK, and return value is record length
8428c2ecf20Sopenharmony_ci */
8438c2ecf20Sopenharmony_cissize_t erst_read(u64 record_id, struct cper_record_header *record,
8448c2ecf20Sopenharmony_ci		  size_t buflen)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	ssize_t len;
8478c2ecf20Sopenharmony_ci	unsigned long flags;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (erst_disable)
8508c2ecf20Sopenharmony_ci		return -ENODEV;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&erst_lock, flags);
8538c2ecf20Sopenharmony_ci	len = __erst_read(record_id, record, buflen);
8548c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&erst_lock, flags);
8558c2ecf20Sopenharmony_ci	return len;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_read);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ciint erst_clear(u64 record_id)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	int rc, i;
8628c2ecf20Sopenharmony_ci	unsigned long flags;
8638c2ecf20Sopenharmony_ci	u64 *entries;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (erst_disable)
8668c2ecf20Sopenharmony_ci		return -ENODEV;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
8698c2ecf20Sopenharmony_ci	if (rc)
8708c2ecf20Sopenharmony_ci		return rc;
8718c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&erst_lock, flags);
8728c2ecf20Sopenharmony_ci	if (erst_erange.attr & ERST_RANGE_NVRAM)
8738c2ecf20Sopenharmony_ci		rc = __erst_clear_from_nvram(record_id);
8748c2ecf20Sopenharmony_ci	else
8758c2ecf20Sopenharmony_ci		rc = __erst_clear_from_storage(record_id);
8768c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&erst_lock, flags);
8778c2ecf20Sopenharmony_ci	if (rc)
8788c2ecf20Sopenharmony_ci		goto out;
8798c2ecf20Sopenharmony_ci	entries = erst_record_id_cache.entries;
8808c2ecf20Sopenharmony_ci	for (i = 0; i < erst_record_id_cache.len; i++) {
8818c2ecf20Sopenharmony_ci		if (entries[i] == record_id)
8828c2ecf20Sopenharmony_ci			entries[i] = APEI_ERST_INVALID_RECORD_ID;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci	__erst_record_id_cache_compact();
8858c2ecf20Sopenharmony_ciout:
8868c2ecf20Sopenharmony_ci	mutex_unlock(&erst_record_id_cache.lock);
8878c2ecf20Sopenharmony_ci	return rc;
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(erst_clear);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic int __init setup_erst_disable(char *str)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	erst_disable = 1;
8948c2ecf20Sopenharmony_ci	return 1;
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci__setup("erst_disable", setup_erst_disable);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic int erst_check_table(struct acpi_table_erst *erst_tab)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	if ((erst_tab->header_length !=
9028c2ecf20Sopenharmony_ci	     (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header)))
9038c2ecf20Sopenharmony_ci	    && (erst_tab->header_length != sizeof(struct acpi_table_erst)))
9048c2ecf20Sopenharmony_ci		return -EINVAL;
9058c2ecf20Sopenharmony_ci	if (erst_tab->header.length < sizeof(struct acpi_table_erst))
9068c2ecf20Sopenharmony_ci		return -EINVAL;
9078c2ecf20Sopenharmony_ci	if (erst_tab->entries !=
9088c2ecf20Sopenharmony_ci	    (erst_tab->header.length - sizeof(struct acpi_table_erst)) /
9098c2ecf20Sopenharmony_ci	    sizeof(struct acpi_erst_entry))
9108c2ecf20Sopenharmony_ci		return -EINVAL;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return 0;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic int erst_open_pstore(struct pstore_info *psi);
9168c2ecf20Sopenharmony_cistatic int erst_close_pstore(struct pstore_info *psi);
9178c2ecf20Sopenharmony_cistatic ssize_t erst_reader(struct pstore_record *record);
9188c2ecf20Sopenharmony_cistatic int erst_writer(struct pstore_record *record);
9198c2ecf20Sopenharmony_cistatic int erst_clearer(struct pstore_record *record);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic struct pstore_info erst_info = {
9228c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
9238c2ecf20Sopenharmony_ci	.name		= "erst",
9248c2ecf20Sopenharmony_ci	.flags		= PSTORE_FLAGS_DMESG,
9258c2ecf20Sopenharmony_ci	.open		= erst_open_pstore,
9268c2ecf20Sopenharmony_ci	.close		= erst_close_pstore,
9278c2ecf20Sopenharmony_ci	.read		= erst_reader,
9288c2ecf20Sopenharmony_ci	.write		= erst_writer,
9298c2ecf20Sopenharmony_ci	.erase		= erst_clearer
9308c2ecf20Sopenharmony_ci};
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci#define CPER_CREATOR_PSTORE						\
9338c2ecf20Sopenharmony_ci	GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c,	\
9348c2ecf20Sopenharmony_ci		  0x64, 0x90, 0xb8, 0x9d)
9358c2ecf20Sopenharmony_ci#define CPER_SECTION_TYPE_DMESG						\
9368c2ecf20Sopenharmony_ci	GUID_INIT(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54,	\
9378c2ecf20Sopenharmony_ci		  0x94, 0x19, 0xeb, 0x12)
9388c2ecf20Sopenharmony_ci#define CPER_SECTION_TYPE_DMESG_Z					\
9398c2ecf20Sopenharmony_ci	GUID_INIT(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d,	\
9408c2ecf20Sopenharmony_ci		  0x34, 0xdd, 0xfa, 0xc6)
9418c2ecf20Sopenharmony_ci#define CPER_SECTION_TYPE_MCE						\
9428c2ecf20Sopenharmony_ci	GUID_INIT(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,	\
9438c2ecf20Sopenharmony_ci		  0x04, 0x4a, 0x38, 0xfc)
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistruct cper_pstore_record {
9468c2ecf20Sopenharmony_ci	struct cper_record_header hdr;
9478c2ecf20Sopenharmony_ci	struct cper_section_descriptor sec_hdr;
9488c2ecf20Sopenharmony_ci	char data[];
9498c2ecf20Sopenharmony_ci} __packed;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic int reader_pos;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cistatic int erst_open_pstore(struct pstore_info *psi)
9548c2ecf20Sopenharmony_ci{
9558c2ecf20Sopenharmony_ci	int rc;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (erst_disable)
9588c2ecf20Sopenharmony_ci		return -ENODEV;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	rc = erst_get_record_id_begin(&reader_pos);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	return rc;
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cistatic int erst_close_pstore(struct pstore_info *psi)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	erst_get_record_id_end();
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	return 0;
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cistatic ssize_t erst_reader(struct pstore_record *record)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	int rc;
9758c2ecf20Sopenharmony_ci	ssize_t len = 0;
9768c2ecf20Sopenharmony_ci	u64 record_id;
9778c2ecf20Sopenharmony_ci	struct cper_pstore_record *rcd;
9788c2ecf20Sopenharmony_ci	size_t rcd_len = sizeof(*rcd) + erst_info.bufsize;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (erst_disable)
9818c2ecf20Sopenharmony_ci		return -ENODEV;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	rcd = kmalloc(rcd_len, GFP_KERNEL);
9848c2ecf20Sopenharmony_ci	if (!rcd) {
9858c2ecf20Sopenharmony_ci		rc = -ENOMEM;
9868c2ecf20Sopenharmony_ci		goto out;
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_ciskip:
9898c2ecf20Sopenharmony_ci	rc = erst_get_record_id_next(&reader_pos, &record_id);
9908c2ecf20Sopenharmony_ci	if (rc)
9918c2ecf20Sopenharmony_ci		goto out;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* no more record */
9948c2ecf20Sopenharmony_ci	if (record_id == APEI_ERST_INVALID_RECORD_ID) {
9958c2ecf20Sopenharmony_ci		rc = -EINVAL;
9968c2ecf20Sopenharmony_ci		goto out;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	len = erst_read(record_id, &rcd->hdr, rcd_len);
10008c2ecf20Sopenharmony_ci	/* The record may be cleared by others, try read next record */
10018c2ecf20Sopenharmony_ci	if (len == -ENOENT)
10028c2ecf20Sopenharmony_ci		goto skip;
10038c2ecf20Sopenharmony_ci	else if (len < 0 || len < sizeof(*rcd)) {
10048c2ecf20Sopenharmony_ci		rc = -EIO;
10058c2ecf20Sopenharmony_ci		goto out;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci	if (!guid_equal(&rcd->hdr.creator_id, &CPER_CREATOR_PSTORE))
10088c2ecf20Sopenharmony_ci		goto skip;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	record->buf = kmalloc(len, GFP_KERNEL);
10118c2ecf20Sopenharmony_ci	if (record->buf == NULL) {
10128c2ecf20Sopenharmony_ci		rc = -ENOMEM;
10138c2ecf20Sopenharmony_ci		goto out;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci	memcpy(record->buf, rcd->data, len - sizeof(*rcd));
10168c2ecf20Sopenharmony_ci	record->id = record_id;
10178c2ecf20Sopenharmony_ci	record->compressed = false;
10188c2ecf20Sopenharmony_ci	record->ecc_notice_size = 0;
10198c2ecf20Sopenharmony_ci	if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG_Z)) {
10208c2ecf20Sopenharmony_ci		record->type = PSTORE_TYPE_DMESG;
10218c2ecf20Sopenharmony_ci		record->compressed = true;
10228c2ecf20Sopenharmony_ci	} else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG))
10238c2ecf20Sopenharmony_ci		record->type = PSTORE_TYPE_DMESG;
10248c2ecf20Sopenharmony_ci	else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_MCE))
10258c2ecf20Sopenharmony_ci		record->type = PSTORE_TYPE_MCE;
10268c2ecf20Sopenharmony_ci	else
10278c2ecf20Sopenharmony_ci		record->type = PSTORE_TYPE_MAX;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
10308c2ecf20Sopenharmony_ci		record->time.tv_sec = rcd->hdr.timestamp;
10318c2ecf20Sopenharmony_ci	else
10328c2ecf20Sopenharmony_ci		record->time.tv_sec = 0;
10338c2ecf20Sopenharmony_ci	record->time.tv_nsec = 0;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ciout:
10368c2ecf20Sopenharmony_ci	kfree(rcd);
10378c2ecf20Sopenharmony_ci	return (rc < 0) ? rc : (len - sizeof(*rcd));
10388c2ecf20Sopenharmony_ci}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_cistatic int erst_writer(struct pstore_record *record)
10418c2ecf20Sopenharmony_ci{
10428c2ecf20Sopenharmony_ci	struct cper_pstore_record *rcd = (struct cper_pstore_record *)
10438c2ecf20Sopenharmony_ci					(erst_info.buf - sizeof(*rcd));
10448c2ecf20Sopenharmony_ci	int ret;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	memset(rcd, 0, sizeof(*rcd));
10478c2ecf20Sopenharmony_ci	memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
10488c2ecf20Sopenharmony_ci	rcd->hdr.revision = CPER_RECORD_REV;
10498c2ecf20Sopenharmony_ci	rcd->hdr.signature_end = CPER_SIG_END;
10508c2ecf20Sopenharmony_ci	rcd->hdr.section_count = 1;
10518c2ecf20Sopenharmony_ci	rcd->hdr.error_severity = CPER_SEV_FATAL;
10528c2ecf20Sopenharmony_ci	/* timestamp valid. platform_id, partition_id are invalid */
10538c2ecf20Sopenharmony_ci	rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
10548c2ecf20Sopenharmony_ci	rcd->hdr.timestamp = ktime_get_real_seconds();
10558c2ecf20Sopenharmony_ci	rcd->hdr.record_length = sizeof(*rcd) + record->size;
10568c2ecf20Sopenharmony_ci	rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
10578c2ecf20Sopenharmony_ci	rcd->hdr.notification_type = CPER_NOTIFY_MCE;
10588c2ecf20Sopenharmony_ci	rcd->hdr.record_id = cper_next_record_id();
10598c2ecf20Sopenharmony_ci	rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	rcd->sec_hdr.section_offset = sizeof(*rcd);
10628c2ecf20Sopenharmony_ci	rcd->sec_hdr.section_length = record->size;
10638c2ecf20Sopenharmony_ci	rcd->sec_hdr.revision = CPER_SEC_REV;
10648c2ecf20Sopenharmony_ci	/* fru_id and fru_text is invalid */
10658c2ecf20Sopenharmony_ci	rcd->sec_hdr.validation_bits = 0;
10668c2ecf20Sopenharmony_ci	rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
10678c2ecf20Sopenharmony_ci	switch (record->type) {
10688c2ecf20Sopenharmony_ci	case PSTORE_TYPE_DMESG:
10698c2ecf20Sopenharmony_ci		if (record->compressed)
10708c2ecf20Sopenharmony_ci			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
10718c2ecf20Sopenharmony_ci		else
10728c2ecf20Sopenharmony_ci			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
10738c2ecf20Sopenharmony_ci		break;
10748c2ecf20Sopenharmony_ci	case PSTORE_TYPE_MCE:
10758c2ecf20Sopenharmony_ci		rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
10768c2ecf20Sopenharmony_ci		break;
10778c2ecf20Sopenharmony_ci	default:
10788c2ecf20Sopenharmony_ci		return -EINVAL;
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci	rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	ret = erst_write(&rcd->hdr);
10838c2ecf20Sopenharmony_ci	record->id = rcd->hdr.record_id;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	return ret;
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_cistatic int erst_clearer(struct pstore_record *record)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	return erst_clear(record->id);
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int __init erst_init(void)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	int rc = 0;
10968c2ecf20Sopenharmony_ci	acpi_status status;
10978c2ecf20Sopenharmony_ci	struct apei_exec_context ctx;
10988c2ecf20Sopenharmony_ci	struct apei_resources erst_resources;
10998c2ecf20Sopenharmony_ci	struct resource *r;
11008c2ecf20Sopenharmony_ci	char *buf;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (acpi_disabled)
11038c2ecf20Sopenharmony_ci		goto err;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (erst_disable) {
11068c2ecf20Sopenharmony_ci		pr_info(
11078c2ecf20Sopenharmony_ci	"Error Record Serialization Table (ERST) support is disabled.\n");
11088c2ecf20Sopenharmony_ci		goto err;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_ERST, 0,
11128c2ecf20Sopenharmony_ci				(struct acpi_table_header **)&erst_tab);
11138c2ecf20Sopenharmony_ci	if (status == AE_NOT_FOUND)
11148c2ecf20Sopenharmony_ci		goto err;
11158c2ecf20Sopenharmony_ci	else if (ACPI_FAILURE(status)) {
11168c2ecf20Sopenharmony_ci		const char *msg = acpi_format_exception(status);
11178c2ecf20Sopenharmony_ci		pr_err("Failed to get table, %s\n", msg);
11188c2ecf20Sopenharmony_ci		rc = -EINVAL;
11198c2ecf20Sopenharmony_ci		goto err;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	rc = erst_check_table(erst_tab);
11238c2ecf20Sopenharmony_ci	if (rc) {
11248c2ecf20Sopenharmony_ci		pr_err(FW_BUG "ERST table is invalid.\n");
11258c2ecf20Sopenharmony_ci		goto err_put_erst_tab;
11268c2ecf20Sopenharmony_ci	}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	apei_resources_init(&erst_resources);
11298c2ecf20Sopenharmony_ci	erst_exec_ctx_init(&ctx);
11308c2ecf20Sopenharmony_ci	rc = apei_exec_collect_resources(&ctx, &erst_resources);
11318c2ecf20Sopenharmony_ci	if (rc)
11328c2ecf20Sopenharmony_ci		goto err_fini;
11338c2ecf20Sopenharmony_ci	rc = apei_resources_request(&erst_resources, "APEI ERST");
11348c2ecf20Sopenharmony_ci	if (rc)
11358c2ecf20Sopenharmony_ci		goto err_fini;
11368c2ecf20Sopenharmony_ci	rc = apei_exec_pre_map_gars(&ctx);
11378c2ecf20Sopenharmony_ci	if (rc)
11388c2ecf20Sopenharmony_ci		goto err_release;
11398c2ecf20Sopenharmony_ci	rc = erst_get_erange(&erst_erange);
11408c2ecf20Sopenharmony_ci	if (rc) {
11418c2ecf20Sopenharmony_ci		if (rc == -ENODEV)
11428c2ecf20Sopenharmony_ci			pr_info(
11438c2ecf20Sopenharmony_ci	"The corresponding hardware device or firmware implementation "
11448c2ecf20Sopenharmony_ci	"is not available.\n");
11458c2ecf20Sopenharmony_ci		else
11468c2ecf20Sopenharmony_ci			pr_err("Failed to get Error Log Address Range.\n");
11478c2ecf20Sopenharmony_ci		goto err_unmap_reg;
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");
11518c2ecf20Sopenharmony_ci	if (!r) {
11528c2ecf20Sopenharmony_ci		pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n",
11538c2ecf20Sopenharmony_ci		       (unsigned long long)erst_erange.base,
11548c2ecf20Sopenharmony_ci		       (unsigned long long)erst_erange.base + erst_erange.size - 1);
11558c2ecf20Sopenharmony_ci		rc = -EIO;
11568c2ecf20Sopenharmony_ci		goto err_unmap_reg;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci	rc = -ENOMEM;
11598c2ecf20Sopenharmony_ci	erst_erange.vaddr = ioremap_cache(erst_erange.base,
11608c2ecf20Sopenharmony_ci					  erst_erange.size);
11618c2ecf20Sopenharmony_ci	if (!erst_erange.vaddr)
11628c2ecf20Sopenharmony_ci		goto err_release_erange;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	pr_info(
11658c2ecf20Sopenharmony_ci	"Error Record Serialization Table (ERST) support is initialized.\n");
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	buf = kmalloc(erst_erange.size, GFP_KERNEL);
11688c2ecf20Sopenharmony_ci	if (buf) {
11698c2ecf20Sopenharmony_ci		erst_info.buf = buf + sizeof(struct cper_pstore_record);
11708c2ecf20Sopenharmony_ci		erst_info.bufsize = erst_erange.size -
11718c2ecf20Sopenharmony_ci				    sizeof(struct cper_pstore_record);
11728c2ecf20Sopenharmony_ci		rc = pstore_register(&erst_info);
11738c2ecf20Sopenharmony_ci		if (rc) {
11748c2ecf20Sopenharmony_ci			if (rc != -EPERM)
11758c2ecf20Sopenharmony_ci				pr_info(
11768c2ecf20Sopenharmony_ci				"Could not register with persistent store.\n");
11778c2ecf20Sopenharmony_ci			erst_info.buf = NULL;
11788c2ecf20Sopenharmony_ci			erst_info.bufsize = 0;
11798c2ecf20Sopenharmony_ci			kfree(buf);
11808c2ecf20Sopenharmony_ci		}
11818c2ecf20Sopenharmony_ci	} else
11828c2ecf20Sopenharmony_ci		pr_err(
11838c2ecf20Sopenharmony_ci		"Failed to allocate %lld bytes for persistent store error log.\n",
11848c2ecf20Sopenharmony_ci		erst_erange.size);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	/* Cleanup ERST Resources */
11878c2ecf20Sopenharmony_ci	apei_resources_fini(&erst_resources);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	return 0;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_cierr_release_erange:
11928c2ecf20Sopenharmony_ci	release_mem_region(erst_erange.base, erst_erange.size);
11938c2ecf20Sopenharmony_cierr_unmap_reg:
11948c2ecf20Sopenharmony_ci	apei_exec_post_unmap_gars(&ctx);
11958c2ecf20Sopenharmony_cierr_release:
11968c2ecf20Sopenharmony_ci	apei_resources_release(&erst_resources);
11978c2ecf20Sopenharmony_cierr_fini:
11988c2ecf20Sopenharmony_ci	apei_resources_fini(&erst_resources);
11998c2ecf20Sopenharmony_cierr_put_erst_tab:
12008c2ecf20Sopenharmony_ci	acpi_put_table((struct acpi_table_header *)erst_tab);
12018c2ecf20Sopenharmony_cierr:
12028c2ecf20Sopenharmony_ci	erst_disable = 1;
12038c2ecf20Sopenharmony_ci	return rc;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cidevice_initcall(erst_init);
1207