18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * apei-base.c - ACPI Platform Error Interface (APEI) supporting
48c2ecf20Sopenharmony_ci * infrastructure
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * APEI allows to report errors (for example from the chipset) to the
78c2ecf20Sopenharmony_ci * the operating system. This improves NMI handling especially. In
88c2ecf20Sopenharmony_ci * addition it supports error serialization and error injection.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * For more information about APEI, please refer to ACPI Specification
118c2ecf20Sopenharmony_ci * version 4.0, chapter 17.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This file has Common functions used by more than one APEI table,
148c2ecf20Sopenharmony_ci * including framework of interpreter for ERST and EINJ; resource
158c2ecf20Sopenharmony_ci * management for APEI registers.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Copyright (C) 2009, Intel Corp.
188c2ecf20Sopenharmony_ci *	Author: Huang Ying <ying.huang@intel.com>
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/kernel.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/acpi.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/io.h>
278c2ecf20Sopenharmony_ci#include <linux/kref.h>
288c2ecf20Sopenharmony_ci#include <linux/rculist.h>
298c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
308c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
318c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "apei-internal.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define APEI_PFX "APEI: "
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * APEI ERST (Error Record Serialization Table) and EINJ (Error
398c2ecf20Sopenharmony_ci * INJection) interpreter framework.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define APEI_EXEC_PRESERVE_REGISTER	0x1
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_civoid apei_exec_ctx_init(struct apei_exec_context *ctx,
458c2ecf20Sopenharmony_ci			struct apei_exec_ins_type *ins_table,
468c2ecf20Sopenharmony_ci			u32 instructions,
478c2ecf20Sopenharmony_ci			struct acpi_whea_header *action_table,
488c2ecf20Sopenharmony_ci			u32 entries)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	ctx->ins_table = ins_table;
518c2ecf20Sopenharmony_ci	ctx->instructions = instructions;
528c2ecf20Sopenharmony_ci	ctx->action_table = action_table;
538c2ecf20Sopenharmony_ci	ctx->entries = entries;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_ctx_init);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ciint __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	int rc;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	rc = apei_read(val, &entry->register_region);
628c2ecf20Sopenharmony_ci	if (rc)
638c2ecf20Sopenharmony_ci		return rc;
648c2ecf20Sopenharmony_ci	*val >>= entry->register_region.bit_offset;
658c2ecf20Sopenharmony_ci	*val &= entry->mask;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciint apei_exec_read_register(struct apei_exec_context *ctx,
718c2ecf20Sopenharmony_ci			    struct acpi_whea_header *entry)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int rc;
748c2ecf20Sopenharmony_ci	u64 val = 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	rc = __apei_exec_read_register(entry, &val);
778c2ecf20Sopenharmony_ci	if (rc)
788c2ecf20Sopenharmony_ci		return rc;
798c2ecf20Sopenharmony_ci	ctx->value = val;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_read_register);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciint apei_exec_read_register_value(struct apei_exec_context *ctx,
868c2ecf20Sopenharmony_ci				  struct acpi_whea_header *entry)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int rc;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	rc = apei_exec_read_register(ctx, entry);
918c2ecf20Sopenharmony_ci	if (rc)
928c2ecf20Sopenharmony_ci		return rc;
938c2ecf20Sopenharmony_ci	ctx->value = (ctx->value == entry->value);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_read_register_value);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciint __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	int rc;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	val &= entry->mask;
1048c2ecf20Sopenharmony_ci	val <<= entry->register_region.bit_offset;
1058c2ecf20Sopenharmony_ci	if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
1068c2ecf20Sopenharmony_ci		u64 valr = 0;
1078c2ecf20Sopenharmony_ci		rc = apei_read(&valr, &entry->register_region);
1088c2ecf20Sopenharmony_ci		if (rc)
1098c2ecf20Sopenharmony_ci			return rc;
1108c2ecf20Sopenharmony_ci		valr &= ~(entry->mask << entry->register_region.bit_offset);
1118c2ecf20Sopenharmony_ci		val |= valr;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci	rc = apei_write(val, &entry->register_region);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return rc;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ciint apei_exec_write_register(struct apei_exec_context *ctx,
1198c2ecf20Sopenharmony_ci			     struct acpi_whea_header *entry)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	return __apei_exec_write_register(entry, ctx->value);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_write_register);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciint apei_exec_write_register_value(struct apei_exec_context *ctx,
1268c2ecf20Sopenharmony_ci				   struct acpi_whea_header *entry)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	int rc;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ctx->value = entry->value;
1318c2ecf20Sopenharmony_ci	rc = apei_exec_write_register(ctx, entry);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return rc;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_write_register_value);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciint apei_exec_noop(struct apei_exec_context *ctx,
1388c2ecf20Sopenharmony_ci		   struct acpi_whea_header *entry)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_noop);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/*
1458c2ecf20Sopenharmony_ci * Interpret the specified action. Go through whole action table,
1468c2ecf20Sopenharmony_ci * execute all instructions belong to the action.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_ciint __apei_exec_run(struct apei_exec_context *ctx, u8 action,
1498c2ecf20Sopenharmony_ci		    bool optional)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	int rc = -ENOENT;
1528c2ecf20Sopenharmony_ci	u32 i, ip;
1538c2ecf20Sopenharmony_ci	struct acpi_whea_header *entry;
1548c2ecf20Sopenharmony_ci	apei_exec_ins_func_t run;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ctx->ip = 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/*
1598c2ecf20Sopenharmony_ci	 * "ip" is the instruction pointer of current instruction,
1608c2ecf20Sopenharmony_ci	 * "ctx->ip" specifies the next instruction to executed,
1618c2ecf20Sopenharmony_ci	 * instruction "run" function may change the "ctx->ip" to
1628c2ecf20Sopenharmony_ci	 * implement "goto" semantics.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_cirewind:
1658c2ecf20Sopenharmony_ci	ip = 0;
1668c2ecf20Sopenharmony_ci	for (i = 0; i < ctx->entries; i++) {
1678c2ecf20Sopenharmony_ci		entry = &ctx->action_table[i];
1688c2ecf20Sopenharmony_ci		if (entry->action != action)
1698c2ecf20Sopenharmony_ci			continue;
1708c2ecf20Sopenharmony_ci		if (ip == ctx->ip) {
1718c2ecf20Sopenharmony_ci			if (entry->instruction >= ctx->instructions ||
1728c2ecf20Sopenharmony_ci			    !ctx->ins_table[entry->instruction].run) {
1738c2ecf20Sopenharmony_ci				pr_warn(FW_WARN APEI_PFX
1748c2ecf20Sopenharmony_ci					"Invalid action table, unknown instruction type: %d\n",
1758c2ecf20Sopenharmony_ci					entry->instruction);
1768c2ecf20Sopenharmony_ci				return -EINVAL;
1778c2ecf20Sopenharmony_ci			}
1788c2ecf20Sopenharmony_ci			run = ctx->ins_table[entry->instruction].run;
1798c2ecf20Sopenharmony_ci			rc = run(ctx, entry);
1808c2ecf20Sopenharmony_ci			if (rc < 0)
1818c2ecf20Sopenharmony_ci				return rc;
1828c2ecf20Sopenharmony_ci			else if (rc != APEI_EXEC_SET_IP)
1838c2ecf20Sopenharmony_ci				ctx->ip++;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci		ip++;
1868c2ecf20Sopenharmony_ci		if (ctx->ip < ip)
1878c2ecf20Sopenharmony_ci			goto rewind;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return !optional && rc < 0 ? rc : 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__apei_exec_run);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_citypedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
1958c2ecf20Sopenharmony_ci				      struct acpi_whea_header *entry,
1968c2ecf20Sopenharmony_ci				      void *data);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int apei_exec_for_each_entry(struct apei_exec_context *ctx,
1998c2ecf20Sopenharmony_ci				    apei_exec_entry_func_t func,
2008c2ecf20Sopenharmony_ci				    void *data,
2018c2ecf20Sopenharmony_ci				    int *end)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	u8 ins;
2048c2ecf20Sopenharmony_ci	int i, rc;
2058c2ecf20Sopenharmony_ci	struct acpi_whea_header *entry;
2068c2ecf20Sopenharmony_ci	struct apei_exec_ins_type *ins_table = ctx->ins_table;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	for (i = 0; i < ctx->entries; i++) {
2098c2ecf20Sopenharmony_ci		entry = ctx->action_table + i;
2108c2ecf20Sopenharmony_ci		ins = entry->instruction;
2118c2ecf20Sopenharmony_ci		if (end)
2128c2ecf20Sopenharmony_ci			*end = i;
2138c2ecf20Sopenharmony_ci		if (ins >= ctx->instructions || !ins_table[ins].run) {
2148c2ecf20Sopenharmony_ci			pr_warn(FW_WARN APEI_PFX
2158c2ecf20Sopenharmony_ci				"Invalid action table, unknown instruction type: %d\n",
2168c2ecf20Sopenharmony_ci				ins);
2178c2ecf20Sopenharmony_ci			return -EINVAL;
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci		rc = func(ctx, entry, data);
2208c2ecf20Sopenharmony_ci		if (rc)
2218c2ecf20Sopenharmony_ci			return rc;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int pre_map_gar_callback(struct apei_exec_context *ctx,
2288c2ecf20Sopenharmony_ci				struct acpi_whea_header *entry,
2298c2ecf20Sopenharmony_ci				void *data)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	u8 ins = entry->instruction;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
2348c2ecf20Sopenharmony_ci		return apei_map_generic_address(&entry->register_region);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/*
2408c2ecf20Sopenharmony_ci * Pre-map all GARs in action table to make it possible to access them
2418c2ecf20Sopenharmony_ci * in NMI handler.
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_ciint apei_exec_pre_map_gars(struct apei_exec_context *ctx)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int rc, end;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback,
2488c2ecf20Sopenharmony_ci				      NULL, &end);
2498c2ecf20Sopenharmony_ci	if (rc) {
2508c2ecf20Sopenharmony_ci		struct apei_exec_context ctx_unmap;
2518c2ecf20Sopenharmony_ci		memcpy(&ctx_unmap, ctx, sizeof(*ctx));
2528c2ecf20Sopenharmony_ci		ctx_unmap.entries = end;
2538c2ecf20Sopenharmony_ci		apei_exec_post_unmap_gars(&ctx_unmap);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return rc;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_pre_map_gars);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int post_unmap_gar_callback(struct apei_exec_context *ctx,
2618c2ecf20Sopenharmony_ci				   struct acpi_whea_header *entry,
2628c2ecf20Sopenharmony_ci				   void *data)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	u8 ins = entry->instruction;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
2678c2ecf20Sopenharmony_ci		apei_unmap_generic_address(&entry->register_region);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/* Post-unmap all GAR in action table. */
2738c2ecf20Sopenharmony_ciint apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
2768c2ecf20Sopenharmony_ci					NULL, NULL);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/*
2818c2ecf20Sopenharmony_ci * Resource management for GARs in APEI
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_cistruct apei_res {
2848c2ecf20Sopenharmony_ci	struct list_head list;
2858c2ecf20Sopenharmony_ci	unsigned long start;
2868c2ecf20Sopenharmony_ci	unsigned long end;
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/* Collect all resources requested, to avoid conflict */
2908c2ecf20Sopenharmony_cistruct apei_resources apei_resources_all = {
2918c2ecf20Sopenharmony_ci	.iomem = LIST_HEAD_INIT(apei_resources_all.iomem),
2928c2ecf20Sopenharmony_ci	.ioport = LIST_HEAD_INIT(apei_resources_all.ioport),
2938c2ecf20Sopenharmony_ci};
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int apei_res_add(struct list_head *res_list,
2968c2ecf20Sopenharmony_ci			unsigned long start, unsigned long size)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct apei_res *res, *resn, *res_ins = NULL;
2998c2ecf20Sopenharmony_ci	unsigned long end = start + size;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (end <= start)
3028c2ecf20Sopenharmony_ci		return 0;
3038c2ecf20Sopenharmony_cirepeat:
3048c2ecf20Sopenharmony_ci	list_for_each_entry_safe(res, resn, res_list, list) {
3058c2ecf20Sopenharmony_ci		if (res->start > end || res->end < start)
3068c2ecf20Sopenharmony_ci			continue;
3078c2ecf20Sopenharmony_ci		else if (end <= res->end && start >= res->start) {
3088c2ecf20Sopenharmony_ci			kfree(res_ins);
3098c2ecf20Sopenharmony_ci			return 0;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci		list_del(&res->list);
3128c2ecf20Sopenharmony_ci		res->start = start = min(res->start, start);
3138c2ecf20Sopenharmony_ci		res->end = end = max(res->end, end);
3148c2ecf20Sopenharmony_ci		kfree(res_ins);
3158c2ecf20Sopenharmony_ci		res_ins = res;
3168c2ecf20Sopenharmony_ci		goto repeat;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (res_ins)
3208c2ecf20Sopenharmony_ci		list_add(&res_ins->list, res_list);
3218c2ecf20Sopenharmony_ci	else {
3228c2ecf20Sopenharmony_ci		res_ins = kmalloc(sizeof(*res), GFP_KERNEL);
3238c2ecf20Sopenharmony_ci		if (!res_ins)
3248c2ecf20Sopenharmony_ci			return -ENOMEM;
3258c2ecf20Sopenharmony_ci		res_ins->start = start;
3268c2ecf20Sopenharmony_ci		res_ins->end = end;
3278c2ecf20Sopenharmony_ci		list_add(&res_ins->list, res_list);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int apei_res_sub(struct list_head *res_list1,
3348c2ecf20Sopenharmony_ci			struct list_head *res_list2)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct apei_res *res1, *resn1, *res2, *res;
3378c2ecf20Sopenharmony_ci	res1 = list_entry(res_list1->next, struct apei_res, list);
3388c2ecf20Sopenharmony_ci	resn1 = list_entry(res1->list.next, struct apei_res, list);
3398c2ecf20Sopenharmony_ci	while (&res1->list != res_list1) {
3408c2ecf20Sopenharmony_ci		list_for_each_entry(res2, res_list2, list) {
3418c2ecf20Sopenharmony_ci			if (res1->start >= res2->end ||
3428c2ecf20Sopenharmony_ci			    res1->end <= res2->start)
3438c2ecf20Sopenharmony_ci				continue;
3448c2ecf20Sopenharmony_ci			else if (res1->end <= res2->end &&
3458c2ecf20Sopenharmony_ci				 res1->start >= res2->start) {
3468c2ecf20Sopenharmony_ci				list_del(&res1->list);
3478c2ecf20Sopenharmony_ci				kfree(res1);
3488c2ecf20Sopenharmony_ci				break;
3498c2ecf20Sopenharmony_ci			} else if (res1->end > res2->end &&
3508c2ecf20Sopenharmony_ci				   res1->start < res2->start) {
3518c2ecf20Sopenharmony_ci				res = kmalloc(sizeof(*res), GFP_KERNEL);
3528c2ecf20Sopenharmony_ci				if (!res)
3538c2ecf20Sopenharmony_ci					return -ENOMEM;
3548c2ecf20Sopenharmony_ci				res->start = res2->end;
3558c2ecf20Sopenharmony_ci				res->end = res1->end;
3568c2ecf20Sopenharmony_ci				res1->end = res2->start;
3578c2ecf20Sopenharmony_ci				list_add(&res->list, &res1->list);
3588c2ecf20Sopenharmony_ci				resn1 = res;
3598c2ecf20Sopenharmony_ci			} else {
3608c2ecf20Sopenharmony_ci				if (res1->start < res2->start)
3618c2ecf20Sopenharmony_ci					res1->end = res2->start;
3628c2ecf20Sopenharmony_ci				else
3638c2ecf20Sopenharmony_ci					res1->start = res2->end;
3648c2ecf20Sopenharmony_ci			}
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci		res1 = resn1;
3678c2ecf20Sopenharmony_ci		resn1 = list_entry(resn1->list.next, struct apei_res, list);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return 0;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void apei_res_clean(struct list_head *res_list)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct apei_res *res, *resn;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	list_for_each_entry_safe(res, resn, res_list, list) {
3788c2ecf20Sopenharmony_ci		list_del(&res->list);
3798c2ecf20Sopenharmony_ci		kfree(res);
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_civoid apei_resources_fini(struct apei_resources *resources)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	apei_res_clean(&resources->iomem);
3868c2ecf20Sopenharmony_ci	apei_res_clean(&resources->ioport);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_fini);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int apei_resources_merge(struct apei_resources *resources1,
3918c2ecf20Sopenharmony_ci				struct apei_resources *resources2)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	int rc;
3948c2ecf20Sopenharmony_ci	struct apei_res *res;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources2->iomem, list) {
3978c2ecf20Sopenharmony_ci		rc = apei_res_add(&resources1->iomem, res->start,
3988c2ecf20Sopenharmony_ci				  res->end - res->start);
3998c2ecf20Sopenharmony_ci		if (rc)
4008c2ecf20Sopenharmony_ci			return rc;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources2->ioport, list) {
4038c2ecf20Sopenharmony_ci		rc = apei_res_add(&resources1->ioport, res->start,
4048c2ecf20Sopenharmony_ci				  res->end - res->start);
4058c2ecf20Sopenharmony_ci		if (rc)
4068c2ecf20Sopenharmony_ci			return rc;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ciint apei_resources_add(struct apei_resources *resources,
4138c2ecf20Sopenharmony_ci		       unsigned long start, unsigned long size,
4148c2ecf20Sopenharmony_ci		       bool iomem)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	if (iomem)
4178c2ecf20Sopenharmony_ci		return apei_res_add(&resources->iomem, start, size);
4188c2ecf20Sopenharmony_ci	else
4198c2ecf20Sopenharmony_ci		return apei_res_add(&resources->ioport, start, size);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_add);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci * EINJ has two groups of GARs (EINJ table entry and trigger table
4258c2ecf20Sopenharmony_ci * entry), so common resources are subtracted from the trigger table
4268c2ecf20Sopenharmony_ci * resources before the second requesting.
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_ciint apei_resources_sub(struct apei_resources *resources1,
4298c2ecf20Sopenharmony_ci		       struct apei_resources *resources2)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	int rc;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	rc = apei_res_sub(&resources1->iomem, &resources2->iomem);
4348c2ecf20Sopenharmony_ci	if (rc)
4358c2ecf20Sopenharmony_ci		return rc;
4368c2ecf20Sopenharmony_ci	return apei_res_sub(&resources1->ioport, &resources2->ioport);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_sub);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int apei_get_res_callback(__u64 start, __u64 size, void *data)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct apei_resources *resources = data;
4438c2ecf20Sopenharmony_ci	return apei_res_add(&resources->iomem, start, size);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int apei_get_nvs_resources(struct apei_resources *resources)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	return acpi_nvs_for_each_region(apei_get_res_callback, resources);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ciint (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
4528c2ecf20Sopenharmony_ci				     void *data), void *data);
4538c2ecf20Sopenharmony_cistatic int apei_get_arch_resources(struct apei_resources *resources)
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	return arch_apei_filter_addr(apei_get_res_callback, resources);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/*
4608c2ecf20Sopenharmony_ci * IO memory/port resource management mechanism is used to check
4618c2ecf20Sopenharmony_ci * whether memory/port area used by GARs conflicts with normal memory
4628c2ecf20Sopenharmony_ci * or IO memory/port of devices.
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_ciint apei_resources_request(struct apei_resources *resources,
4658c2ecf20Sopenharmony_ci			   const char *desc)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct apei_res *res, *res_bak = NULL;
4688c2ecf20Sopenharmony_ci	struct resource *r;
4698c2ecf20Sopenharmony_ci	struct apei_resources nvs_resources, arch_res;
4708c2ecf20Sopenharmony_ci	int rc;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	rc = apei_resources_sub(resources, &apei_resources_all);
4738c2ecf20Sopenharmony_ci	if (rc)
4748c2ecf20Sopenharmony_ci		return rc;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/*
4778c2ecf20Sopenharmony_ci	 * Some firmware uses ACPI NVS region, that has been marked as
4788c2ecf20Sopenharmony_ci	 * busy, so exclude it from APEI resources to avoid false
4798c2ecf20Sopenharmony_ci	 * conflict.
4808c2ecf20Sopenharmony_ci	 */
4818c2ecf20Sopenharmony_ci	apei_resources_init(&nvs_resources);
4828c2ecf20Sopenharmony_ci	rc = apei_get_nvs_resources(&nvs_resources);
4838c2ecf20Sopenharmony_ci	if (rc)
4848c2ecf20Sopenharmony_ci		goto nvs_res_fini;
4858c2ecf20Sopenharmony_ci	rc = apei_resources_sub(resources, &nvs_resources);
4868c2ecf20Sopenharmony_ci	if (rc)
4878c2ecf20Sopenharmony_ci		goto nvs_res_fini;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (arch_apei_filter_addr) {
4908c2ecf20Sopenharmony_ci		apei_resources_init(&arch_res);
4918c2ecf20Sopenharmony_ci		rc = apei_get_arch_resources(&arch_res);
4928c2ecf20Sopenharmony_ci		if (rc)
4938c2ecf20Sopenharmony_ci			goto arch_res_fini;
4948c2ecf20Sopenharmony_ci		rc = apei_resources_sub(resources, &arch_res);
4958c2ecf20Sopenharmony_ci		if (rc)
4968c2ecf20Sopenharmony_ci			goto arch_res_fini;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	rc = -EINVAL;
5008c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources->iomem, list) {
5018c2ecf20Sopenharmony_ci		r = request_mem_region(res->start, res->end - res->start,
5028c2ecf20Sopenharmony_ci				       desc);
5038c2ecf20Sopenharmony_ci		if (!r) {
5048c2ecf20Sopenharmony_ci			pr_err(APEI_PFX
5058c2ecf20Sopenharmony_ci		"Can not request [mem %#010llx-%#010llx] for %s registers\n",
5068c2ecf20Sopenharmony_ci			       (unsigned long long)res->start,
5078c2ecf20Sopenharmony_ci			       (unsigned long long)res->end - 1, desc);
5088c2ecf20Sopenharmony_ci			res_bak = res;
5098c2ecf20Sopenharmony_ci			goto err_unmap_iomem;
5108c2ecf20Sopenharmony_ci		}
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources->ioport, list) {
5148c2ecf20Sopenharmony_ci		r = request_region(res->start, res->end - res->start, desc);
5158c2ecf20Sopenharmony_ci		if (!r) {
5168c2ecf20Sopenharmony_ci			pr_err(APEI_PFX
5178c2ecf20Sopenharmony_ci		"Can not request [io  %#06llx-%#06llx] for %s registers\n",
5188c2ecf20Sopenharmony_ci			       (unsigned long long)res->start,
5198c2ecf20Sopenharmony_ci			       (unsigned long long)res->end - 1, desc);
5208c2ecf20Sopenharmony_ci			res_bak = res;
5218c2ecf20Sopenharmony_ci			goto err_unmap_ioport;
5228c2ecf20Sopenharmony_ci		}
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	rc = apei_resources_merge(&apei_resources_all, resources);
5268c2ecf20Sopenharmony_ci	if (rc) {
5278c2ecf20Sopenharmony_ci		pr_err(APEI_PFX "Fail to merge resources!\n");
5288c2ecf20Sopenharmony_ci		goto err_unmap_ioport;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	goto arch_res_fini;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cierr_unmap_ioport:
5348c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources->ioport, list) {
5358c2ecf20Sopenharmony_ci		if (res == res_bak)
5368c2ecf20Sopenharmony_ci			break;
5378c2ecf20Sopenharmony_ci		release_region(res->start, res->end - res->start);
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci	res_bak = NULL;
5408c2ecf20Sopenharmony_cierr_unmap_iomem:
5418c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources->iomem, list) {
5428c2ecf20Sopenharmony_ci		if (res == res_bak)
5438c2ecf20Sopenharmony_ci			break;
5448c2ecf20Sopenharmony_ci		release_mem_region(res->start, res->end - res->start);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ciarch_res_fini:
5478c2ecf20Sopenharmony_ci	if (arch_apei_filter_addr)
5488c2ecf20Sopenharmony_ci		apei_resources_fini(&arch_res);
5498c2ecf20Sopenharmony_cinvs_res_fini:
5508c2ecf20Sopenharmony_ci	apei_resources_fini(&nvs_resources);
5518c2ecf20Sopenharmony_ci	return rc;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_request);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_civoid apei_resources_release(struct apei_resources *resources)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	int rc;
5588c2ecf20Sopenharmony_ci	struct apei_res *res;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources->iomem, list)
5618c2ecf20Sopenharmony_ci		release_mem_region(res->start, res->end - res->start);
5628c2ecf20Sopenharmony_ci	list_for_each_entry(res, &resources->ioport, list)
5638c2ecf20Sopenharmony_ci		release_region(res->start, res->end - res->start);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	rc = apei_resources_sub(&apei_resources_all, resources);
5668c2ecf20Sopenharmony_ci	if (rc)
5678c2ecf20Sopenharmony_ci		pr_err(APEI_PFX "Fail to sub resources!\n");
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_resources_release);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
5728c2ecf20Sopenharmony_ci				u32 *access_bit_width)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	u32 bit_width, bit_offset, access_size_code, space_id;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	bit_width = reg->bit_width;
5778c2ecf20Sopenharmony_ci	bit_offset = reg->bit_offset;
5788c2ecf20Sopenharmony_ci	access_size_code = reg->access_width;
5798c2ecf20Sopenharmony_ci	space_id = reg->space_id;
5808c2ecf20Sopenharmony_ci	*paddr = get_unaligned(&reg->address);
5818c2ecf20Sopenharmony_ci	if (!*paddr) {
5828c2ecf20Sopenharmony_ci		pr_warn(FW_BUG APEI_PFX
5838c2ecf20Sopenharmony_ci			"Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
5848c2ecf20Sopenharmony_ci			*paddr, bit_width, bit_offset, access_size_code,
5858c2ecf20Sopenharmony_ci			space_id);
5868c2ecf20Sopenharmony_ci		return -EINVAL;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (access_size_code < 1 || access_size_code > 4) {
5908c2ecf20Sopenharmony_ci		pr_warn(FW_BUG APEI_PFX
5918c2ecf20Sopenharmony_ci			"Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
5928c2ecf20Sopenharmony_ci			*paddr, bit_width, bit_offset, access_size_code,
5938c2ecf20Sopenharmony_ci			space_id);
5948c2ecf20Sopenharmony_ci		return -EINVAL;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci	*access_bit_width = 1UL << (access_size_code + 2);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* Fixup common BIOS bug */
5998c2ecf20Sopenharmony_ci	if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 &&
6008c2ecf20Sopenharmony_ci	    *access_bit_width < 32)
6018c2ecf20Sopenharmony_ci		*access_bit_width = 32;
6028c2ecf20Sopenharmony_ci	else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 &&
6038c2ecf20Sopenharmony_ci	    *access_bit_width < 64)
6048c2ecf20Sopenharmony_ci		*access_bit_width = 64;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if ((bit_width + bit_offset) > *access_bit_width) {
6078c2ecf20Sopenharmony_ci		pr_warn(FW_BUG APEI_PFX
6088c2ecf20Sopenharmony_ci			"Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
6098c2ecf20Sopenharmony_ci			*paddr, bit_width, bit_offset, access_size_code,
6108c2ecf20Sopenharmony_ci			space_id);
6118c2ecf20Sopenharmony_ci		return -EINVAL;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
6158c2ecf20Sopenharmony_ci	    space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
6168c2ecf20Sopenharmony_ci		pr_warn(FW_BUG APEI_PFX
6178c2ecf20Sopenharmony_ci			"Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
6188c2ecf20Sopenharmony_ci			*paddr, bit_width, bit_offset, access_size_code,
6198c2ecf20Sopenharmony_ci			space_id);
6208c2ecf20Sopenharmony_ci		return -EINVAL;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	return 0;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ciint apei_map_generic_address(struct acpi_generic_address *reg)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	int rc;
6298c2ecf20Sopenharmony_ci	u32 access_bit_width;
6308c2ecf20Sopenharmony_ci	u64 address;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	rc = apei_check_gar(reg, &address, &access_bit_width);
6338c2ecf20Sopenharmony_ci	if (rc)
6348c2ecf20Sopenharmony_ci		return rc;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* IO space doesn't need mapping */
6378c2ecf20Sopenharmony_ci	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
6388c2ecf20Sopenharmony_ci		return 0;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (!acpi_os_map_generic_address(reg))
6418c2ecf20Sopenharmony_ci		return -ENXIO;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_map_generic_address);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/* read GAR in interrupt (including NMI) or process context */
6488c2ecf20Sopenharmony_ciint apei_read(u64 *val, struct acpi_generic_address *reg)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	int rc;
6518c2ecf20Sopenharmony_ci	u32 access_bit_width;
6528c2ecf20Sopenharmony_ci	u64 address;
6538c2ecf20Sopenharmony_ci	acpi_status status;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	rc = apei_check_gar(reg, &address, &access_bit_width);
6568c2ecf20Sopenharmony_ci	if (rc)
6578c2ecf20Sopenharmony_ci		return rc;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	*val = 0;
6608c2ecf20Sopenharmony_ci	switch(reg->space_id) {
6618c2ecf20Sopenharmony_ci	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
6628c2ecf20Sopenharmony_ci		status = acpi_os_read_memory((acpi_physical_address) address,
6638c2ecf20Sopenharmony_ci					       val, access_bit_width);
6648c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
6658c2ecf20Sopenharmony_ci			return -EIO;
6668c2ecf20Sopenharmony_ci		break;
6678c2ecf20Sopenharmony_ci	case ACPI_ADR_SPACE_SYSTEM_IO:
6688c2ecf20Sopenharmony_ci		status = acpi_os_read_port(address, (u32 *)val,
6698c2ecf20Sopenharmony_ci					   access_bit_width);
6708c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
6718c2ecf20Sopenharmony_ci			return -EIO;
6728c2ecf20Sopenharmony_ci		break;
6738c2ecf20Sopenharmony_ci	default:
6748c2ecf20Sopenharmony_ci		return -EINVAL;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	return 0;
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_read);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/* write GAR in interrupt (including NMI) or process context */
6828c2ecf20Sopenharmony_ciint apei_write(u64 val, struct acpi_generic_address *reg)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	int rc;
6858c2ecf20Sopenharmony_ci	u32 access_bit_width;
6868c2ecf20Sopenharmony_ci	u64 address;
6878c2ecf20Sopenharmony_ci	acpi_status status;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	rc = apei_check_gar(reg, &address, &access_bit_width);
6908c2ecf20Sopenharmony_ci	if (rc)
6918c2ecf20Sopenharmony_ci		return rc;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	switch (reg->space_id) {
6948c2ecf20Sopenharmony_ci	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
6958c2ecf20Sopenharmony_ci		status = acpi_os_write_memory((acpi_physical_address) address,
6968c2ecf20Sopenharmony_ci						val, access_bit_width);
6978c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
6988c2ecf20Sopenharmony_ci			return -EIO;
6998c2ecf20Sopenharmony_ci		break;
7008c2ecf20Sopenharmony_ci	case ACPI_ADR_SPACE_SYSTEM_IO:
7018c2ecf20Sopenharmony_ci		status = acpi_os_write_port(address, val, access_bit_width);
7028c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
7038c2ecf20Sopenharmony_ci			return -EIO;
7048c2ecf20Sopenharmony_ci		break;
7058c2ecf20Sopenharmony_ci	default:
7068c2ecf20Sopenharmony_ci		return -EINVAL;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	return 0;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_write);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int collect_res_callback(struct apei_exec_context *ctx,
7148c2ecf20Sopenharmony_ci				struct acpi_whea_header *entry,
7158c2ecf20Sopenharmony_ci				void *data)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct apei_resources *resources = data;
7188c2ecf20Sopenharmony_ci	struct acpi_generic_address *reg = &entry->register_region;
7198c2ecf20Sopenharmony_ci	u8 ins = entry->instruction;
7208c2ecf20Sopenharmony_ci	u32 access_bit_width;
7218c2ecf20Sopenharmony_ci	u64 paddr;
7228c2ecf20Sopenharmony_ci	int rc;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
7258c2ecf20Sopenharmony_ci		return 0;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	rc = apei_check_gar(reg, &paddr, &access_bit_width);
7288c2ecf20Sopenharmony_ci	if (rc)
7298c2ecf20Sopenharmony_ci		return rc;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	switch (reg->space_id) {
7328c2ecf20Sopenharmony_ci	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
7338c2ecf20Sopenharmony_ci		return apei_res_add(&resources->iomem, paddr,
7348c2ecf20Sopenharmony_ci				    access_bit_width / 8);
7358c2ecf20Sopenharmony_ci	case ACPI_ADR_SPACE_SYSTEM_IO:
7368c2ecf20Sopenharmony_ci		return apei_res_add(&resources->ioport, paddr,
7378c2ecf20Sopenharmony_ci				    access_bit_width / 8);
7388c2ecf20Sopenharmony_ci	default:
7398c2ecf20Sopenharmony_ci		return -EINVAL;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci/*
7448c2ecf20Sopenharmony_ci * Same register may be used by multiple instructions in GARs, so
7458c2ecf20Sopenharmony_ci * resources are collected before requesting.
7468c2ecf20Sopenharmony_ci */
7478c2ecf20Sopenharmony_ciint apei_exec_collect_resources(struct apei_exec_context *ctx,
7488c2ecf20Sopenharmony_ci				struct apei_resources *resources)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	return apei_exec_for_each_entry(ctx, collect_res_callback,
7518c2ecf20Sopenharmony_ci					resources, NULL);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_exec_collect_resources);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistruct dentry *apei_get_debugfs_dir(void)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	static struct dentry *dapei;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (!dapei)
7608c2ecf20Sopenharmony_ci		dapei = debugfs_create_dir("apei", NULL);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return dapei;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ciint __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr,
7678c2ecf20Sopenharmony_ci				  void *data)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	return 1;
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(arch_apei_enable_cmcff);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_civoid __weak arch_apei_report_mem_error(int sev,
7748c2ecf20Sopenharmony_ci				       struct cper_sec_mem_err *mem_err)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(arch_apei_report_mem_error);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ciint apei_osc_setup(void)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
7828c2ecf20Sopenharmony_ci	acpi_handle handle;
7838c2ecf20Sopenharmony_ci	u32 capbuf[3];
7848c2ecf20Sopenharmony_ci	struct acpi_osc_context context = {
7858c2ecf20Sopenharmony_ci		.uuid_str	= whea_uuid_str,
7868c2ecf20Sopenharmony_ci		.rev		= 1,
7878c2ecf20Sopenharmony_ci		.cap.length	= sizeof(capbuf),
7888c2ecf20Sopenharmony_ci		.cap.pointer	= capbuf,
7898c2ecf20Sopenharmony_ci	};
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
7928c2ecf20Sopenharmony_ci	capbuf[OSC_SUPPORT_DWORD] = 1;
7938c2ecf20Sopenharmony_ci	capbuf[OSC_CONTROL_DWORD] = 0;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
7968c2ecf20Sopenharmony_ci	    || ACPI_FAILURE(acpi_run_osc(handle, &context)))
7978c2ecf20Sopenharmony_ci		return -EIO;
7988c2ecf20Sopenharmony_ci	else {
7998c2ecf20Sopenharmony_ci		kfree(context.ret.pointer);
8008c2ecf20Sopenharmony_ci		return 0;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(apei_osc_setup);
804