brintos

brintos / linux-shallow public Read only

0
0
Text · 30.1 KiB · bf65e34 Raw
1301 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * APEI Error Record Serialization Table support4 *5 * ERST is a way provided by APEI to save and retrieve hardware error6 * information to and from a persistent store.7 *8 * For more information about ERST, please refer to ACPI Specification9 * version 4.0, section 17.4.10 *11 * Copyright 2010 Intel Corp.12 *   Author: Huang Ying <ying.huang@intel.com>13 */14 15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/init.h>18#include <linux/delay.h>19#include <linux/io.h>20#include <linux/acpi.h>21#include <linux/uaccess.h>22#include <linux/cper.h>23#include <linux/nmi.h>24#include <linux/hardirq.h>25#include <linux/pstore.h>26#include <linux/vmalloc.h>27#include <linux/mm.h> /* kvfree() */28#include <acpi/apei.h>29 30#include "apei-internal.h"31 32#undef pr_fmt33#define pr_fmt(fmt) "ERST: " fmt34 35/* ERST command status */36#define ERST_STATUS_SUCCESS			0x037#define ERST_STATUS_NOT_ENOUGH_SPACE		0x138#define ERST_STATUS_HARDWARE_NOT_AVAILABLE	0x239#define ERST_STATUS_FAILED			0x340#define ERST_STATUS_RECORD_STORE_EMPTY		0x441#define ERST_STATUS_RECORD_NOT_FOUND		0x542 43#define ERST_TAB_ENTRY(tab)						\44	((struct acpi_whea_header *)((char *)(tab) +			\45				     sizeof(struct acpi_table_erst)))46 47#define SPIN_UNIT		100			/* 100ns */48/* Firmware should respond within 1 milliseconds */49#define FIRMWARE_TIMEOUT	(1 * NSEC_PER_MSEC)50#define FIRMWARE_MAX_STALL	50			/* 50us */51 52int erst_disable;53EXPORT_SYMBOL_GPL(erst_disable);54 55static struct acpi_table_erst *erst_tab;56 57/* ERST Error Log Address Range attributes */58#define ERST_RANGE_RESERVED	0x000159#define ERST_RANGE_NVRAM	0x000260#define ERST_RANGE_SLOW		0x000461 62/* ERST Exec max timings */63#define ERST_EXEC_TIMING_MAX_MASK      0xFFFFFFFF0000000064#define ERST_EXEC_TIMING_MAX_SHIFT     3265 66/*67 * ERST Error Log Address Range, used as buffer for reading/writing68 * error records.69 */70static struct erst_erange {71	u64 base;72	u64 size;73	void __iomem *vaddr;74	u32 attr;75	u64 timings;76} erst_erange;77 78/*79 * Prevent ERST interpreter to run simultaneously, because the80 * corresponding firmware implementation may not work properly when81 * invoked simultaneously.82 *83 * It is used to provide exclusive accessing for ERST Error Log84 * Address Range too.85 */86static DEFINE_RAW_SPINLOCK(erst_lock);87 88static inline int erst_errno(int command_status)89{90	switch (command_status) {91	case ERST_STATUS_SUCCESS:92		return 0;93	case ERST_STATUS_HARDWARE_NOT_AVAILABLE:94		return -ENODEV;95	case ERST_STATUS_NOT_ENOUGH_SPACE:96		return -ENOSPC;97	case ERST_STATUS_RECORD_STORE_EMPTY:98	case ERST_STATUS_RECORD_NOT_FOUND:99		return -ENOENT;100	default:101		return -EINVAL;102	}103}104 105static inline u64 erst_get_timeout(void)106{107	u64 timeout = FIRMWARE_TIMEOUT;108 109	if (erst_erange.attr & ERST_RANGE_SLOW) {110		timeout = ((erst_erange.timings & ERST_EXEC_TIMING_MAX_MASK) >>111			ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_MSEC;112		if (timeout < FIRMWARE_TIMEOUT)113			timeout = FIRMWARE_TIMEOUT;114	}115	return timeout;116}117 118static int erst_timedout(u64 *t, u64 spin_unit)119{120	if ((s64)*t < spin_unit) {121		pr_warn(FW_WARN "Firmware does not respond in time.\n");122		return 1;123	}124	*t -= spin_unit;125	ndelay(spin_unit);126	touch_nmi_watchdog();127	return 0;128}129 130static int erst_exec_load_var1(struct apei_exec_context *ctx,131			       struct acpi_whea_header *entry)132{133	return __apei_exec_read_register(entry, &ctx->var1);134}135 136static int erst_exec_load_var2(struct apei_exec_context *ctx,137			       struct acpi_whea_header *entry)138{139	return __apei_exec_read_register(entry, &ctx->var2);140}141 142static int erst_exec_store_var1(struct apei_exec_context *ctx,143				struct acpi_whea_header *entry)144{145	return __apei_exec_write_register(entry, ctx->var1);146}147 148static int erst_exec_add(struct apei_exec_context *ctx,149			 struct acpi_whea_header *entry)150{151	ctx->var1 += ctx->var2;152	return 0;153}154 155static int erst_exec_subtract(struct apei_exec_context *ctx,156			      struct acpi_whea_header *entry)157{158	ctx->var1 -= ctx->var2;159	return 0;160}161 162static int erst_exec_add_value(struct apei_exec_context *ctx,163			       struct acpi_whea_header *entry)164{165	int rc;166	u64 val;167 168	rc = __apei_exec_read_register(entry, &val);169	if (rc)170		return rc;171	val += ctx->value;172	rc = __apei_exec_write_register(entry, val);173	return rc;174}175 176static int erst_exec_subtract_value(struct apei_exec_context *ctx,177				    struct acpi_whea_header *entry)178{179	int rc;180	u64 val;181 182	rc = __apei_exec_read_register(entry, &val);183	if (rc)184		return rc;185	val -= ctx->value;186	rc = __apei_exec_write_register(entry, val);187	return rc;188}189 190static int erst_exec_stall(struct apei_exec_context *ctx,191			   struct acpi_whea_header *entry)192{193	u64 stall_time;194 195	if (ctx->value > FIRMWARE_MAX_STALL) {196		if (!in_nmi())197			pr_warn(FW_WARN198			"Too long stall time for stall instruction: 0x%llx.\n",199				   ctx->value);200		stall_time = FIRMWARE_MAX_STALL;201	} else202		stall_time = ctx->value;203	udelay(stall_time);204	return 0;205}206 207static int erst_exec_stall_while_true(struct apei_exec_context *ctx,208				      struct acpi_whea_header *entry)209{210	int rc;211	u64 val;212	u64 timeout;213	u64 stall_time;214 215	timeout = erst_get_timeout();216 217	if (ctx->var1 > FIRMWARE_MAX_STALL) {218		if (!in_nmi())219			pr_warn(FW_WARN220		"Too long stall time for stall while true instruction: 0x%llx.\n",221				   ctx->var1);222		stall_time = FIRMWARE_MAX_STALL;223	} else224		stall_time = ctx->var1;225 226	for (;;) {227		rc = __apei_exec_read_register(entry, &val);228		if (rc)229			return rc;230		if (val != ctx->value)231			break;232		if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC))233			return -EIO;234	}235	return 0;236}237 238static int erst_exec_skip_next_instruction_if_true(239	struct apei_exec_context *ctx,240	struct acpi_whea_header *entry)241{242	int rc;243	u64 val;244 245	rc = __apei_exec_read_register(entry, &val);246	if (rc)247		return rc;248	if (val == ctx->value) {249		ctx->ip += 2;250		return APEI_EXEC_SET_IP;251	}252 253	return 0;254}255 256static int erst_exec_goto(struct apei_exec_context *ctx,257			  struct acpi_whea_header *entry)258{259	ctx->ip = ctx->value;260	return APEI_EXEC_SET_IP;261}262 263static int erst_exec_set_src_address_base(struct apei_exec_context *ctx,264					  struct acpi_whea_header *entry)265{266	return __apei_exec_read_register(entry, &ctx->src_base);267}268 269static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,270					  struct acpi_whea_header *entry)271{272	return __apei_exec_read_register(entry, &ctx->dst_base);273}274 275static int erst_exec_move_data(struct apei_exec_context *ctx,276			       struct acpi_whea_header *entry)277{278	int rc;279	u64 offset;280	void *src, *dst;281 282	/* ioremap does not work in interrupt context */283	if (in_interrupt()) {284		pr_warn("MOVE_DATA can not be used in interrupt context.\n");285		return -EBUSY;286	}287 288	rc = __apei_exec_read_register(entry, &offset);289	if (rc)290		return rc;291 292	src = ioremap(ctx->src_base + offset, ctx->var2);293	if (!src)294		return -ENOMEM;295	dst = ioremap(ctx->dst_base + offset, ctx->var2);296	if (!dst) {297		iounmap(src);298		return -ENOMEM;299	}300 301	memmove(dst, src, ctx->var2);302 303	iounmap(src);304	iounmap(dst);305 306	return 0;307}308 309static struct apei_exec_ins_type erst_ins_type[] = {310	[ACPI_ERST_READ_REGISTER] = {311		.flags = APEI_EXEC_INS_ACCESS_REGISTER,312		.run = apei_exec_read_register,313	},314	[ACPI_ERST_READ_REGISTER_VALUE] = {315		.flags = APEI_EXEC_INS_ACCESS_REGISTER,316		.run = apei_exec_read_register_value,317	},318	[ACPI_ERST_WRITE_REGISTER] = {319		.flags = APEI_EXEC_INS_ACCESS_REGISTER,320		.run = apei_exec_write_register,321	},322	[ACPI_ERST_WRITE_REGISTER_VALUE] = {323		.flags = APEI_EXEC_INS_ACCESS_REGISTER,324		.run = apei_exec_write_register_value,325	},326	[ACPI_ERST_NOOP] = {327		.flags = 0,328		.run = apei_exec_noop,329	},330	[ACPI_ERST_LOAD_VAR1] = {331		.flags = APEI_EXEC_INS_ACCESS_REGISTER,332		.run = erst_exec_load_var1,333	},334	[ACPI_ERST_LOAD_VAR2] = {335		.flags = APEI_EXEC_INS_ACCESS_REGISTER,336		.run = erst_exec_load_var2,337	},338	[ACPI_ERST_STORE_VAR1] = {339		.flags = APEI_EXEC_INS_ACCESS_REGISTER,340		.run = erst_exec_store_var1,341	},342	[ACPI_ERST_ADD] = {343		.flags = 0,344		.run = erst_exec_add,345	},346	[ACPI_ERST_SUBTRACT] = {347		.flags = 0,348		.run = erst_exec_subtract,349	},350	[ACPI_ERST_ADD_VALUE] = {351		.flags = APEI_EXEC_INS_ACCESS_REGISTER,352		.run = erst_exec_add_value,353	},354	[ACPI_ERST_SUBTRACT_VALUE] = {355		.flags = APEI_EXEC_INS_ACCESS_REGISTER,356		.run = erst_exec_subtract_value,357	},358	[ACPI_ERST_STALL] = {359		.flags = 0,360		.run = erst_exec_stall,361	},362	[ACPI_ERST_STALL_WHILE_TRUE] = {363		.flags = APEI_EXEC_INS_ACCESS_REGISTER,364		.run = erst_exec_stall_while_true,365	},366	[ACPI_ERST_SKIP_NEXT_IF_TRUE] = {367		.flags = APEI_EXEC_INS_ACCESS_REGISTER,368		.run = erst_exec_skip_next_instruction_if_true,369	},370	[ACPI_ERST_GOTO] = {371		.flags = 0,372		.run = erst_exec_goto,373	},374	[ACPI_ERST_SET_SRC_ADDRESS_BASE] = {375		.flags = APEI_EXEC_INS_ACCESS_REGISTER,376		.run = erst_exec_set_src_address_base,377	},378	[ACPI_ERST_SET_DST_ADDRESS_BASE] = {379		.flags = APEI_EXEC_INS_ACCESS_REGISTER,380		.run = erst_exec_set_dst_address_base,381	},382	[ACPI_ERST_MOVE_DATA] = {383		.flags = APEI_EXEC_INS_ACCESS_REGISTER,384		.run = erst_exec_move_data,385	},386};387 388static inline void erst_exec_ctx_init(struct apei_exec_context *ctx)389{390	apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),391			   ERST_TAB_ENTRY(erst_tab), erst_tab->entries);392}393 394static int erst_get_erange(struct erst_erange *range)395{396	struct apei_exec_context ctx;397	int rc;398 399	erst_exec_ctx_init(&ctx);400	rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);401	if (rc)402		return rc;403	range->base = apei_exec_ctx_get_output(&ctx);404	rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);405	if (rc)406		return rc;407	range->size = apei_exec_ctx_get_output(&ctx);408	rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);409	if (rc)410		return rc;411	range->attr = apei_exec_ctx_get_output(&ctx);412	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_TIMINGS);413	if (rc == 0)414		range->timings = apei_exec_ctx_get_output(&ctx);415	else if (rc == -ENOENT)416		range->timings = 0;417	else418		return rc;419 420	return 0;421}422 423static ssize_t __erst_get_record_count(void)424{425	struct apei_exec_context ctx;426	int rc;427 428	erst_exec_ctx_init(&ctx);429	rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);430	if (rc)431		return rc;432	return apei_exec_ctx_get_output(&ctx);433}434 435ssize_t erst_get_record_count(void)436{437	ssize_t count;438	unsigned long flags;439 440	if (erst_disable)441		return -ENODEV;442 443	raw_spin_lock_irqsave(&erst_lock, flags);444	count = __erst_get_record_count();445	raw_spin_unlock_irqrestore(&erst_lock, flags);446 447	return count;448}449EXPORT_SYMBOL_GPL(erst_get_record_count);450 451#define ERST_RECORD_ID_CACHE_SIZE_MIN	16452#define ERST_RECORD_ID_CACHE_SIZE_MAX	1024453 454struct erst_record_id_cache {455	struct mutex lock;456	u64 *entries;457	int len;458	int size;459	int refcount;460};461 462static struct erst_record_id_cache erst_record_id_cache = {463	.lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock),464	.refcount = 0,465};466 467static int __erst_get_next_record_id(u64 *record_id)468{469	struct apei_exec_context ctx;470	int rc;471 472	erst_exec_ctx_init(&ctx);473	rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);474	if (rc)475		return rc;476	*record_id = apei_exec_ctx_get_output(&ctx);477 478	return 0;479}480 481int erst_get_record_id_begin(int *pos)482{483	int rc;484 485	if (erst_disable)486		return -ENODEV;487 488	rc = mutex_lock_interruptible(&erst_record_id_cache.lock);489	if (rc)490		return rc;491	erst_record_id_cache.refcount++;492	mutex_unlock(&erst_record_id_cache.lock);493 494	*pos = 0;495 496	return 0;497}498EXPORT_SYMBOL_GPL(erst_get_record_id_begin);499 500/* erst_record_id_cache.lock must be held by caller */501static int __erst_record_id_cache_add_one(void)502{503	u64 id, prev_id, first_id;504	int i, rc;505	u64 *entries;506	unsigned long flags;507 508	id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID;509retry:510	raw_spin_lock_irqsave(&erst_lock, flags);511	rc = __erst_get_next_record_id(&id);512	raw_spin_unlock_irqrestore(&erst_lock, flags);513	if (rc == -ENOENT)514		return 0;515	if (rc)516		return rc;517	if (id == APEI_ERST_INVALID_RECORD_ID)518		return 0;519	/* can not skip current ID, or loop back to first ID */520	if (id == prev_id || id == first_id)521		return 0;522	if (first_id == APEI_ERST_INVALID_RECORD_ID)523		first_id = id;524	prev_id = id;525 526	entries = erst_record_id_cache.entries;527	for (i = 0; i < erst_record_id_cache.len; i++) {528		if (entries[i] == id)529			break;530	}531	/* record id already in cache, try next */532	if (i < erst_record_id_cache.len)533		goto retry;534	if (erst_record_id_cache.len >= erst_record_id_cache.size) {535		int new_size;536		u64 *new_entries;537 538		new_size = erst_record_id_cache.size * 2;539		new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN,540				     ERST_RECORD_ID_CACHE_SIZE_MAX);541		if (new_size <= erst_record_id_cache.size) {542			if (printk_ratelimit())543				pr_warn(FW_WARN "too many record IDs!\n");544			return 0;545		}546		new_entries = kvmalloc_array(new_size, sizeof(entries[0]),547					     GFP_KERNEL);548		if (!new_entries)549			return -ENOMEM;550		memcpy(new_entries, entries,551		       erst_record_id_cache.len * sizeof(entries[0]));552		kvfree(entries);553		erst_record_id_cache.entries = entries = new_entries;554		erst_record_id_cache.size = new_size;555	}556	entries[i] = id;557	erst_record_id_cache.len++;558 559	return 1;560}561 562/*563 * Get the record ID of an existing error record on the persistent564 * storage. If there is no error record on the persistent storage, the565 * returned record_id is APEI_ERST_INVALID_RECORD_ID.566 */567int erst_get_record_id_next(int *pos, u64 *record_id)568{569	int rc = 0;570	u64 *entries;571 572	if (erst_disable)573		return -ENODEV;574 575	/* must be enclosed by erst_get_record_id_begin/end */576	BUG_ON(!erst_record_id_cache.refcount);577	BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len);578 579	mutex_lock(&erst_record_id_cache.lock);580	entries = erst_record_id_cache.entries;581	for (; *pos < erst_record_id_cache.len; (*pos)++)582		if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID)583			break;584	/* found next record id in cache */585	if (*pos < erst_record_id_cache.len) {586		*record_id = entries[*pos];587		(*pos)++;588		goto out_unlock;589	}590 591	/* Try to add one more record ID to cache */592	rc = __erst_record_id_cache_add_one();593	if (rc < 0)594		goto out_unlock;595	/* successfully add one new ID */596	if (rc == 1) {597		*record_id = erst_record_id_cache.entries[*pos];598		(*pos)++;599		rc = 0;600	} else {601		*pos = -1;602		*record_id = APEI_ERST_INVALID_RECORD_ID;603	}604out_unlock:605	mutex_unlock(&erst_record_id_cache.lock);606 607	return rc;608}609EXPORT_SYMBOL_GPL(erst_get_record_id_next);610 611/* erst_record_id_cache.lock must be held by caller */612static void __erst_record_id_cache_compact(void)613{614	int i, wpos = 0;615	u64 *entries;616 617	if (erst_record_id_cache.refcount)618		return;619 620	entries = erst_record_id_cache.entries;621	for (i = 0; i < erst_record_id_cache.len; i++) {622		if (entries[i] == APEI_ERST_INVALID_RECORD_ID)623			continue;624		if (wpos != i)625			entries[wpos] = entries[i];626		wpos++;627	}628	erst_record_id_cache.len = wpos;629}630 631void erst_get_record_id_end(void)632{633	/*634	 * erst_disable != 0 should be detected by invoker via the635	 * return value of erst_get_record_id_begin/next, so this636	 * function should not be called for erst_disable != 0.637	 */638	BUG_ON(erst_disable);639 640	mutex_lock(&erst_record_id_cache.lock);641	erst_record_id_cache.refcount--;642	BUG_ON(erst_record_id_cache.refcount < 0);643	__erst_record_id_cache_compact();644	mutex_unlock(&erst_record_id_cache.lock);645}646EXPORT_SYMBOL_GPL(erst_get_record_id_end);647 648static int __erst_write_to_storage(u64 offset)649{650	struct apei_exec_context ctx;651	u64 timeout;652	u64 val;653	int rc;654 655	timeout = erst_get_timeout();656 657	erst_exec_ctx_init(&ctx);658	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);659	if (rc)660		return rc;661	apei_exec_ctx_set_input(&ctx, offset);662	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);663	if (rc)664		return rc;665	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);666	if (rc)667		return rc;668	for (;;) {669		rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);670		if (rc)671			return rc;672		val = apei_exec_ctx_get_output(&ctx);673		if (!val)674			break;675		if (erst_timedout(&timeout, SPIN_UNIT))676			return -EIO;677	}678	rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);679	if (rc)680		return rc;681	val = apei_exec_ctx_get_output(&ctx);682	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);683	if (rc)684		return rc;685 686	return erst_errno(val);687}688 689static int __erst_read_from_storage(u64 record_id, u64 offset)690{691	struct apei_exec_context ctx;692	u64 timeout;693	u64 val;694	int rc;695 696	timeout = erst_get_timeout();697 698	erst_exec_ctx_init(&ctx);699	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);700	if (rc)701		return rc;702	apei_exec_ctx_set_input(&ctx, offset);703	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);704	if (rc)705		return rc;706	apei_exec_ctx_set_input(&ctx, record_id);707	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);708	if (rc)709		return rc;710	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);711	if (rc)712		return rc;713	for (;;) {714		rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);715		if (rc)716			return rc;717		val = apei_exec_ctx_get_output(&ctx);718		if (!val)719			break;720		if (erst_timedout(&timeout, SPIN_UNIT))721			return -EIO;722	}723	rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);724	if (rc)725		return rc;726	val = apei_exec_ctx_get_output(&ctx);727	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);728	if (rc)729		return rc;730 731	return erst_errno(val);732}733 734static int __erst_clear_from_storage(u64 record_id)735{736	struct apei_exec_context ctx;737	u64 timeout;738	u64 val;739	int rc;740 741	timeout = erst_get_timeout();742 743	erst_exec_ctx_init(&ctx);744	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);745	if (rc)746		return rc;747	apei_exec_ctx_set_input(&ctx, record_id);748	rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);749	if (rc)750		return rc;751	rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);752	if (rc)753		return rc;754	for (;;) {755		rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);756		if (rc)757			return rc;758		val = apei_exec_ctx_get_output(&ctx);759		if (!val)760			break;761		if (erst_timedout(&timeout, SPIN_UNIT))762			return -EIO;763	}764	rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);765	if (rc)766		return rc;767	val = apei_exec_ctx_get_output(&ctx);768	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);769	if (rc)770		return rc;771 772	return erst_errno(val);773}774 775/* NVRAM ERST Error Log Address Range is not supported yet */776static void pr_unimpl_nvram(void)777{778	if (printk_ratelimit())779		pr_warn("NVRAM ERST Log Address Range not implemented yet.\n");780}781 782static int __erst_write_to_nvram(const struct cper_record_header *record)783{784	/* do not print message, because printk is not safe for NMI */785	return -ENOSYS;786}787 788static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)789{790	pr_unimpl_nvram();791	return -ENOSYS;792}793 794static int __erst_clear_from_nvram(u64 record_id)795{796	pr_unimpl_nvram();797	return -ENOSYS;798}799 800int erst_write(const struct cper_record_header *record)801{802	int rc;803	unsigned long flags;804	struct cper_record_header *rcd_erange;805 806	if (erst_disable)807		return -ENODEV;808 809	if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))810		return -EINVAL;811 812	if (erst_erange.attr & ERST_RANGE_NVRAM) {813		if (!raw_spin_trylock_irqsave(&erst_lock, flags))814			return -EBUSY;815		rc = __erst_write_to_nvram(record);816		raw_spin_unlock_irqrestore(&erst_lock, flags);817		return rc;818	}819 820	if (record->record_length > erst_erange.size)821		return -EINVAL;822 823	if (!raw_spin_trylock_irqsave(&erst_lock, flags))824		return -EBUSY;825	memcpy(erst_erange.vaddr, record, record->record_length);826	rcd_erange = erst_erange.vaddr;827	/* signature for serialization system */828	memcpy(&rcd_erange->persistence_information, "ER", 2);829 830	rc = __erst_write_to_storage(0);831	raw_spin_unlock_irqrestore(&erst_lock, flags);832 833	return rc;834}835EXPORT_SYMBOL_GPL(erst_write);836 837static int __erst_read_to_erange(u64 record_id, u64 *offset)838{839	int rc;840 841	if (erst_erange.attr & ERST_RANGE_NVRAM)842		return __erst_read_to_erange_from_nvram(843			record_id, offset);844 845	rc = __erst_read_from_storage(record_id, 0);846	if (rc)847		return rc;848	*offset = 0;849 850	return 0;851}852 853static ssize_t __erst_read(u64 record_id, struct cper_record_header *record,854			   size_t buflen)855{856	int rc;857	u64 offset, len = 0;858	struct cper_record_header *rcd_tmp;859 860	rc = __erst_read_to_erange(record_id, &offset);861	if (rc)862		return rc;863	rcd_tmp = erst_erange.vaddr + offset;864	len = rcd_tmp->record_length;865	if (len <= buflen)866		memcpy(record, rcd_tmp, len);867 868	return len;869}870 871/*872 * If return value > buflen, the buffer size is not big enough,873 * else if return value < 0, something goes wrong,874 * else everything is OK, and return value is record length875 */876ssize_t erst_read(u64 record_id, struct cper_record_header *record,877		  size_t buflen)878{879	ssize_t len;880	unsigned long flags;881 882	if (erst_disable)883		return -ENODEV;884 885	raw_spin_lock_irqsave(&erst_lock, flags);886	len = __erst_read(record_id, record, buflen);887	raw_spin_unlock_irqrestore(&erst_lock, flags);888	return len;889}890EXPORT_SYMBOL_GPL(erst_read);891 892static void erst_clear_cache(u64 record_id)893{894	int i;895	u64 *entries;896 897	mutex_lock(&erst_record_id_cache.lock);898 899	entries = erst_record_id_cache.entries;900	for (i = 0; i < erst_record_id_cache.len; i++) {901		if (entries[i] == record_id)902			entries[i] = APEI_ERST_INVALID_RECORD_ID;903	}904	__erst_record_id_cache_compact();905 906	mutex_unlock(&erst_record_id_cache.lock);907}908 909ssize_t erst_read_record(u64 record_id, struct cper_record_header *record,910		size_t buflen, size_t recordlen, const guid_t *creatorid)911{912	ssize_t len;913 914	/*915	 * if creatorid is NULL, read any record for erst-dbg module916	 */917	if (creatorid == NULL) {918		len = erst_read(record_id, record, buflen);919		if (len == -ENOENT)920			erst_clear_cache(record_id);921 922		return len;923	}924 925	len = erst_read(record_id, record, buflen);926	/*927	 * if erst_read return value is -ENOENT skip to next record_id,928	 * and clear the record_id cache.929	 */930	if (len == -ENOENT) {931		erst_clear_cache(record_id);932		goto out;933	}934 935	if (len < 0)936		goto out;937 938	/*939	 * if erst_read return value is less than record head length,940	 * consider it as -EIO, and clear the record_id cache.941	 */942	if (len < recordlen) {943		len = -EIO;944		erst_clear_cache(record_id);945		goto out;946	}947 948	/*949	 * if creatorid is not wanted, consider it as not found,950	 * for skipping to next record_id.951	 */952	if (!guid_equal(&record->creator_id, creatorid))953		len = -ENOENT;954 955out:956	return len;957}958EXPORT_SYMBOL_GPL(erst_read_record);959 960int erst_clear(u64 record_id)961{962	int rc, i;963	unsigned long flags;964	u64 *entries;965 966	if (erst_disable)967		return -ENODEV;968 969	rc = mutex_lock_interruptible(&erst_record_id_cache.lock);970	if (rc)971		return rc;972	raw_spin_lock_irqsave(&erst_lock, flags);973	if (erst_erange.attr & ERST_RANGE_NVRAM)974		rc = __erst_clear_from_nvram(record_id);975	else976		rc = __erst_clear_from_storage(record_id);977	raw_spin_unlock_irqrestore(&erst_lock, flags);978	if (rc)979		goto out;980	entries = erst_record_id_cache.entries;981	for (i = 0; i < erst_record_id_cache.len; i++) {982		if (entries[i] == record_id)983			entries[i] = APEI_ERST_INVALID_RECORD_ID;984	}985	__erst_record_id_cache_compact();986out:987	mutex_unlock(&erst_record_id_cache.lock);988	return rc;989}990EXPORT_SYMBOL_GPL(erst_clear);991 992static int __init setup_erst_disable(char *str)993{994	erst_disable = 1;995	return 1;996}997 998__setup("erst_disable", setup_erst_disable);999 1000static int erst_check_table(struct acpi_table_erst *erst_tab)1001{1002	if ((erst_tab->header_length !=1003	     (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header)))1004	    && (erst_tab->header_length != sizeof(struct acpi_table_erst)))1005		return -EINVAL;1006	if (erst_tab->header.length < sizeof(struct acpi_table_erst))1007		return -EINVAL;1008	if (erst_tab->entries !=1009	    (erst_tab->header.length - sizeof(struct acpi_table_erst)) /1010	    sizeof(struct acpi_erst_entry))1011		return -EINVAL;1012 1013	return 0;1014}1015 1016static int erst_open_pstore(struct pstore_info *psi);1017static int erst_close_pstore(struct pstore_info *psi);1018static ssize_t erst_reader(struct pstore_record *record);1019static int erst_writer(struct pstore_record *record);1020static int erst_clearer(struct pstore_record *record);1021 1022static struct pstore_info erst_info = {1023	.owner		= THIS_MODULE,1024	.name		= "erst",1025	.flags		= PSTORE_FLAGS_DMESG,1026	.open		= erst_open_pstore,1027	.close		= erst_close_pstore,1028	.read		= erst_reader,1029	.write		= erst_writer,1030	.erase		= erst_clearer1031};1032 1033#define CPER_CREATOR_PSTORE						\1034	GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c,	\1035		  0x64, 0x90, 0xb8, 0x9d)1036#define CPER_SECTION_TYPE_DMESG						\1037	GUID_INIT(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54,	\1038		  0x94, 0x19, 0xeb, 0x12)1039#define CPER_SECTION_TYPE_DMESG_Z					\1040	GUID_INIT(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d,	\1041		  0x34, 0xdd, 0xfa, 0xc6)1042#define CPER_SECTION_TYPE_MCE						\1043	GUID_INIT(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,	\1044		  0x04, 0x4a, 0x38, 0xfc)1045 1046struct cper_pstore_record {1047	struct cper_record_header hdr;1048	struct cper_section_descriptor sec_hdr;1049	char data[];1050} __packed;1051 1052static int reader_pos;1053 1054static int erst_open_pstore(struct pstore_info *psi)1055{1056	if (erst_disable)1057		return -ENODEV;1058 1059	return erst_get_record_id_begin(&reader_pos);1060}1061 1062static int erst_close_pstore(struct pstore_info *psi)1063{1064	erst_get_record_id_end();1065 1066	return 0;1067}1068 1069static ssize_t erst_reader(struct pstore_record *record)1070{1071	int rc;1072	ssize_t len = 0;1073	u64 record_id;1074	struct cper_pstore_record *rcd;1075	size_t rcd_len = sizeof(*rcd) + erst_info.bufsize;1076 1077	if (erst_disable)1078		return -ENODEV;1079 1080	rcd = kmalloc(rcd_len, GFP_KERNEL);1081	if (!rcd) {1082		rc = -ENOMEM;1083		goto out;1084	}1085skip:1086	rc = erst_get_record_id_next(&reader_pos, &record_id);1087	if (rc)1088		goto out;1089 1090	/* no more record */1091	if (record_id == APEI_ERST_INVALID_RECORD_ID) {1092		rc = -EINVAL;1093		goto out;1094	}1095 1096	len = erst_read_record(record_id, &rcd->hdr, rcd_len, sizeof(*rcd),1097			&CPER_CREATOR_PSTORE);1098	/* The record may be cleared by others, try read next record */1099	if (len == -ENOENT)1100		goto skip;1101	else if (len < 0)1102		goto out;1103 1104	record->buf = kmalloc(len, GFP_KERNEL);1105	if (record->buf == NULL) {1106		rc = -ENOMEM;1107		goto out;1108	}1109	memcpy(record->buf, rcd->data, len - sizeof(*rcd));1110	record->id = record_id;1111	record->compressed = false;1112	record->ecc_notice_size = 0;1113	if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG_Z)) {1114		record->type = PSTORE_TYPE_DMESG;1115		record->compressed = true;1116	} else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG))1117		record->type = PSTORE_TYPE_DMESG;1118	else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_MCE))1119		record->type = PSTORE_TYPE_MCE;1120	else1121		record->type = PSTORE_TYPE_MAX;1122 1123	if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)1124		record->time.tv_sec = rcd->hdr.timestamp;1125	else1126		record->time.tv_sec = 0;1127	record->time.tv_nsec = 0;1128 1129out:1130	kfree(rcd);1131	return (rc < 0) ? rc : (len - sizeof(*rcd));1132}1133 1134static int erst_writer(struct pstore_record *record)1135{1136	struct cper_pstore_record *rcd = (struct cper_pstore_record *)1137					(erst_info.buf - sizeof(*rcd));1138	int ret;1139 1140	memset(rcd, 0, sizeof(*rcd));1141	memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);1142	rcd->hdr.revision = CPER_RECORD_REV;1143	rcd->hdr.signature_end = CPER_SIG_END;1144	rcd->hdr.section_count = 1;1145	rcd->hdr.error_severity = CPER_SEV_FATAL;1146	/* timestamp valid. platform_id, partition_id are invalid */1147	rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;1148	rcd->hdr.timestamp = ktime_get_real_seconds();1149	rcd->hdr.record_length = sizeof(*rcd) + record->size;1150	rcd->hdr.creator_id = CPER_CREATOR_PSTORE;1151	rcd->hdr.notification_type = CPER_NOTIFY_MCE;1152	rcd->hdr.record_id = cper_next_record_id();1153	rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;1154 1155	rcd->sec_hdr.section_offset = sizeof(*rcd);1156	rcd->sec_hdr.section_length = record->size;1157	rcd->sec_hdr.revision = CPER_SEC_REV;1158	/* fru_id and fru_text is invalid */1159	rcd->sec_hdr.validation_bits = 0;1160	rcd->sec_hdr.flags = CPER_SEC_PRIMARY;1161	switch (record->type) {1162	case PSTORE_TYPE_DMESG:1163		if (record->compressed)1164			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;1165		else1166			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;1167		break;1168	case PSTORE_TYPE_MCE:1169		rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;1170		break;1171	default:1172		return -EINVAL;1173	}1174	rcd->sec_hdr.section_severity = CPER_SEV_FATAL;1175 1176	ret = erst_write(&rcd->hdr);1177	record->id = rcd->hdr.record_id;1178 1179	return ret;1180}1181 1182static int erst_clearer(struct pstore_record *record)1183{1184	return erst_clear(record->id);1185}1186 1187static int __init erst_init(void)1188{1189	int rc = 0;1190	acpi_status status;1191	struct apei_exec_context ctx;1192	struct apei_resources erst_resources;1193	struct resource *r;1194	char *buf;1195 1196	if (acpi_disabled)1197		goto err;1198 1199	if (erst_disable) {1200		pr_info(1201	"Error Record Serialization Table (ERST) support is disabled.\n");1202		goto err;1203	}1204 1205	status = acpi_get_table(ACPI_SIG_ERST, 0,1206				(struct acpi_table_header **)&erst_tab);1207	if (status == AE_NOT_FOUND)1208		goto err;1209	else if (ACPI_FAILURE(status)) {1210		const char *msg = acpi_format_exception(status);1211		pr_err("Failed to get table, %s\n", msg);1212		rc = -EINVAL;1213		goto err;1214	}1215 1216	rc = erst_check_table(erst_tab);1217	if (rc) {1218		pr_err(FW_BUG "ERST table is invalid.\n");1219		goto err_put_erst_tab;1220	}1221 1222	apei_resources_init(&erst_resources);1223	erst_exec_ctx_init(&ctx);1224	rc = apei_exec_collect_resources(&ctx, &erst_resources);1225	if (rc)1226		goto err_fini;1227	rc = apei_resources_request(&erst_resources, "APEI ERST");1228	if (rc)1229		goto err_fini;1230	rc = apei_exec_pre_map_gars(&ctx);1231	if (rc)1232		goto err_release;1233	rc = erst_get_erange(&erst_erange);1234	if (rc) {1235		if (rc == -ENODEV)1236			pr_info(1237	"The corresponding hardware device or firmware implementation "1238	"is not available.\n");1239		else1240			pr_err("Failed to get Error Log Address Range.\n");1241		goto err_unmap_reg;1242	}1243 1244	r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");1245	if (!r) {1246		pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n",1247		       (unsigned long long)erst_erange.base,1248		       (unsigned long long)erst_erange.base + erst_erange.size - 1);1249		rc = -EIO;1250		goto err_unmap_reg;1251	}1252	rc = -ENOMEM;1253	erst_erange.vaddr = ioremap_cache(erst_erange.base,1254					  erst_erange.size);1255	if (!erst_erange.vaddr)1256		goto err_release_erange;1257 1258	pr_info(1259	"Error Record Serialization Table (ERST) support is initialized.\n");1260 1261	buf = kmalloc(erst_erange.size, GFP_KERNEL);1262	if (buf) {1263		erst_info.buf = buf + sizeof(struct cper_pstore_record);1264		erst_info.bufsize = erst_erange.size -1265				    sizeof(struct cper_pstore_record);1266		rc = pstore_register(&erst_info);1267		if (rc) {1268			if (rc != -EPERM)1269				pr_info(1270				"Could not register with persistent store.\n");1271			erst_info.buf = NULL;1272			erst_info.bufsize = 0;1273			kfree(buf);1274		}1275	} else1276		pr_err(1277		"Failed to allocate %lld bytes for persistent store error log.\n",1278		erst_erange.size);1279 1280	/* Cleanup ERST Resources */1281	apei_resources_fini(&erst_resources);1282 1283	return 0;1284 1285err_release_erange:1286	release_mem_region(erst_erange.base, erst_erange.size);1287err_unmap_reg:1288	apei_exec_post_unmap_gars(&ctx);1289err_release:1290	apei_resources_release(&erst_resources);1291err_fini:1292	apei_resources_fini(&erst_resources);1293err_put_erst_tab:1294	acpi_put_table((struct acpi_table_header *)erst_tab);1295err:1296	erst_disable = 1;1297	return rc;1298}1299 1300device_initcall(erst_init);1301