18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2017 Arm Ltd.
38c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "sdei: " fmt
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <acpi/ghes.h>
68c2ecf20Sopenharmony_ci#include <linux/acpi.h>
78c2ecf20Sopenharmony_ci#include <linux/arm_sdei.h>
88c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h>
98c2ecf20Sopenharmony_ci#include <linux/atomic.h>
108c2ecf20Sopenharmony_ci#include <linux/bitops.h>
118c2ecf20Sopenharmony_ci#include <linux/compiler.h>
128c2ecf20Sopenharmony_ci#include <linux/cpuhotplug.h>
138c2ecf20Sopenharmony_ci#include <linux/cpu.h>
148c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/kprobes.h>
198c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <linux/mutex.h>
228c2ecf20Sopenharmony_ci#include <linux/notifier.h>
238c2ecf20Sopenharmony_ci#include <linux/of.h>
248c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
258c2ecf20Sopenharmony_ci#include <linux/percpu.h>
268c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
278c2ecf20Sopenharmony_ci#include <linux/pm.h>
288c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
298c2ecf20Sopenharmony_ci#include <linux/preempt.h>
308c2ecf20Sopenharmony_ci#include <linux/reboot.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/smp.h>
338c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
348c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * The call to use to reach the firmware.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic asmlinkage void (*sdei_firmware_call)(unsigned long function_id,
408c2ecf20Sopenharmony_ci		      unsigned long arg0, unsigned long arg1,
418c2ecf20Sopenharmony_ci		      unsigned long arg2, unsigned long arg3,
428c2ecf20Sopenharmony_ci		      unsigned long arg4, struct arm_smccc_res *res);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* entry point from firmware to arch asm code */
458c2ecf20Sopenharmony_cistatic unsigned long sdei_entry_point;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int sdei_hp_state;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct sdei_event {
508c2ecf20Sopenharmony_ci	/* These three are protected by the sdei_list_lock */
518c2ecf20Sopenharmony_ci	struct list_head	list;
528c2ecf20Sopenharmony_ci	bool			reregister;
538c2ecf20Sopenharmony_ci	bool			reenable;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	u32			event_num;
568c2ecf20Sopenharmony_ci	u8			type;
578c2ecf20Sopenharmony_ci	u8			priority;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* This pointer is handed to firmware as the event argument. */
608c2ecf20Sopenharmony_ci	union {
618c2ecf20Sopenharmony_ci		/* Shared events */
628c2ecf20Sopenharmony_ci		struct sdei_registered_event *registered;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		/* CPU private events */
658c2ecf20Sopenharmony_ci		struct sdei_registered_event __percpu *private_registered;
668c2ecf20Sopenharmony_ci	};
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* Take the mutex for any API call or modification. Take the mutex first. */
708c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sdei_events_lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* and then hold this when modifying the list */
738c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sdei_list_lock);
748c2ecf20Sopenharmony_cistatic LIST_HEAD(sdei_list);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Private events are registered/enabled via IPI passing one of these */
778c2ecf20Sopenharmony_cistruct sdei_crosscall_args {
788c2ecf20Sopenharmony_ci	struct sdei_event *event;
798c2ecf20Sopenharmony_ci	atomic_t errors;
808c2ecf20Sopenharmony_ci	int first_error;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define CROSSCALL_INIT(arg, event)		\
848c2ecf20Sopenharmony_ci	do {					\
858c2ecf20Sopenharmony_ci		arg.event = event;		\
868c2ecf20Sopenharmony_ci		arg.first_error = 0;		\
878c2ecf20Sopenharmony_ci		atomic_set(&arg.errors, 0);	\
888c2ecf20Sopenharmony_ci	} while (0)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic inline int sdei_do_local_call(smp_call_func_t fn,
918c2ecf20Sopenharmony_ci				     struct sdei_event *event)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct sdei_crosscall_args arg;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	CROSSCALL_INIT(arg, event);
968c2ecf20Sopenharmony_ci	fn(&arg);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return arg.first_error;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic inline int sdei_do_cross_call(smp_call_func_t fn,
1028c2ecf20Sopenharmony_ci				     struct sdei_event *event)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct sdei_crosscall_args arg;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	CROSSCALL_INIT(arg, event);
1078c2ecf20Sopenharmony_ci	on_each_cpu(fn, &arg, true);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return arg.first_error;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic inline void
1138c2ecf20Sopenharmony_cisdei_cross_call_return(struct sdei_crosscall_args *arg, int err)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	if (err && (atomic_inc_return(&arg->errors) == 1))
1168c2ecf20Sopenharmony_ci		arg->first_error = err;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int sdei_to_linux_errno(unsigned long sdei_err)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	switch (sdei_err) {
1228c2ecf20Sopenharmony_ci	case SDEI_NOT_SUPPORTED:
1238c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1248c2ecf20Sopenharmony_ci	case SDEI_INVALID_PARAMETERS:
1258c2ecf20Sopenharmony_ci		return -EINVAL;
1268c2ecf20Sopenharmony_ci	case SDEI_DENIED:
1278c2ecf20Sopenharmony_ci		return -EPERM;
1288c2ecf20Sopenharmony_ci	case SDEI_PENDING:
1298c2ecf20Sopenharmony_ci		return -EINPROGRESS;
1308c2ecf20Sopenharmony_ci	case SDEI_OUT_OF_RESOURCE:
1318c2ecf20Sopenharmony_ci		return -ENOMEM;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int invoke_sdei_fn(unsigned long function_id, unsigned long arg0,
1388c2ecf20Sopenharmony_ci			  unsigned long arg1, unsigned long arg2,
1398c2ecf20Sopenharmony_ci			  unsigned long arg3, unsigned long arg4,
1408c2ecf20Sopenharmony_ci			  u64 *result)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int err;
1438c2ecf20Sopenharmony_ci	struct arm_smccc_res res;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (sdei_firmware_call) {
1468c2ecf20Sopenharmony_ci		sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4,
1478c2ecf20Sopenharmony_ci				   &res);
1488c2ecf20Sopenharmony_ci		err = sdei_to_linux_errno(res.a0);
1498c2ecf20Sopenharmony_ci	} else {
1508c2ecf20Sopenharmony_ci		/*
1518c2ecf20Sopenharmony_ci		 * !sdei_firmware_call means we failed to probe or called
1528c2ecf20Sopenharmony_ci		 * sdei_mark_interface_broken(). -EIO is not an error returned
1538c2ecf20Sopenharmony_ci		 * by sdei_to_linux_errno() and is used to suppress messages
1548c2ecf20Sopenharmony_ci		 * from this driver.
1558c2ecf20Sopenharmony_ci		 */
1568c2ecf20Sopenharmony_ci		err = -EIO;
1578c2ecf20Sopenharmony_ci		res.a0 = SDEI_NOT_SUPPORTED;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (result)
1618c2ecf20Sopenharmony_ci		*result = res.a0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return err;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(invoke_sdei_fn);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic struct sdei_event *sdei_event_find(u32 event_num)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct sdei_event *e, *found = NULL;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdei_events_lock);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
1748c2ecf20Sopenharmony_ci	list_for_each_entry(e, &sdei_list, list) {
1758c2ecf20Sopenharmony_ci		if (e->event_num == event_num) {
1768c2ecf20Sopenharmony_ci			found = e;
1778c2ecf20Sopenharmony_ci			break;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return found;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ciint sdei_api_event_context(u32 query, u64 *result)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_CONTEXT, query, 0, 0, 0, 0,
1888c2ecf20Sopenharmony_ci			      result);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(sdei_api_event_context);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info, 0,
1958c2ecf20Sopenharmony_ci			      0, 0, result);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic struct sdei_event *sdei_event_create(u32 event_num,
1998c2ecf20Sopenharmony_ci					    sdei_event_callback *cb,
2008c2ecf20Sopenharmony_ci					    void *cb_arg)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int err;
2038c2ecf20Sopenharmony_ci	u64 result;
2048c2ecf20Sopenharmony_ci	struct sdei_event *event;
2058c2ecf20Sopenharmony_ci	struct sdei_registered_event *reg;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdei_events_lock);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	event = kzalloc(sizeof(*event), GFP_KERNEL);
2108c2ecf20Sopenharmony_ci	if (!event) {
2118c2ecf20Sopenharmony_ci		err = -ENOMEM;
2128c2ecf20Sopenharmony_ci		goto fail;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&event->list);
2168c2ecf20Sopenharmony_ci	event->event_num = event_num;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
2198c2ecf20Sopenharmony_ci				      &result);
2208c2ecf20Sopenharmony_ci	if (err)
2218c2ecf20Sopenharmony_ci		goto fail;
2228c2ecf20Sopenharmony_ci	event->priority = result;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_TYPE,
2258c2ecf20Sopenharmony_ci				      &result);
2268c2ecf20Sopenharmony_ci	if (err)
2278c2ecf20Sopenharmony_ci		goto fail;
2288c2ecf20Sopenharmony_ci	event->type = result;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (event->type == SDEI_EVENT_TYPE_SHARED) {
2318c2ecf20Sopenharmony_ci		reg = kzalloc(sizeof(*reg), GFP_KERNEL);
2328c2ecf20Sopenharmony_ci		if (!reg) {
2338c2ecf20Sopenharmony_ci			err = -ENOMEM;
2348c2ecf20Sopenharmony_ci			goto fail;
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		reg->event_num = event->event_num;
2388c2ecf20Sopenharmony_ci		reg->priority = event->priority;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		reg->callback = cb;
2418c2ecf20Sopenharmony_ci		reg->callback_arg = cb_arg;
2428c2ecf20Sopenharmony_ci		event->registered = reg;
2438c2ecf20Sopenharmony_ci	} else {
2448c2ecf20Sopenharmony_ci		int cpu;
2458c2ecf20Sopenharmony_ci		struct sdei_registered_event __percpu *regs;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		regs = alloc_percpu(struct sdei_registered_event);
2488c2ecf20Sopenharmony_ci		if (!regs) {
2498c2ecf20Sopenharmony_ci			err = -ENOMEM;
2508c2ecf20Sopenharmony_ci			goto fail;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		for_each_possible_cpu(cpu) {
2548c2ecf20Sopenharmony_ci			reg = per_cpu_ptr(regs, cpu);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci			reg->event_num = event->event_num;
2578c2ecf20Sopenharmony_ci			reg->priority = event->priority;
2588c2ecf20Sopenharmony_ci			reg->callback = cb;
2598c2ecf20Sopenharmony_ci			reg->callback_arg = cb_arg;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		event->private_registered = regs;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
2668c2ecf20Sopenharmony_ci	list_add(&event->list, &sdei_list);
2678c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return event;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cifail:
2728c2ecf20Sopenharmony_ci	kfree(event);
2738c2ecf20Sopenharmony_ci	return ERR_PTR(err);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void sdei_event_destroy_llocked(struct sdei_event *event)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdei_events_lock);
2798c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdei_list_lock);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	list_del(&event->list);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (event->type == SDEI_EVENT_TYPE_SHARED)
2848c2ecf20Sopenharmony_ci		kfree(event->registered);
2858c2ecf20Sopenharmony_ci	else
2868c2ecf20Sopenharmony_ci		free_percpu(event->private_registered);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	kfree(event);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void sdei_event_destroy(struct sdei_event *event)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
2948c2ecf20Sopenharmony_ci	sdei_event_destroy_llocked(event);
2958c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int sdei_api_get_version(u64 *version)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_VERSION, 0, 0, 0, 0, 0, version);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ciint sdei_mask_local_cpu(void)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	int err;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0, NULL);
3088c2ecf20Sopenharmony_ci	if (err && err != -EIO) {
3098c2ecf20Sopenharmony_ci		pr_warn_once("failed to mask CPU[%u]: %d\n",
3108c2ecf20Sopenharmony_ci			      smp_processor_id(), err);
3118c2ecf20Sopenharmony_ci		return err;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void _ipi_mask_cpu(void *ignored)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	WARN_ON_ONCE(preemptible());
3208c2ecf20Sopenharmony_ci	sdei_mask_local_cpu();
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ciint sdei_unmask_local_cpu(void)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	int err;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_UNMASK, 0, 0, 0, 0, 0, NULL);
3288c2ecf20Sopenharmony_ci	if (err && err != -EIO) {
3298c2ecf20Sopenharmony_ci		pr_warn_once("failed to unmask CPU[%u]: %d\n",
3308c2ecf20Sopenharmony_ci			     smp_processor_id(), err);
3318c2ecf20Sopenharmony_ci		return err;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void _ipi_unmask_cpu(void *ignored)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	WARN_ON_ONCE(preemptible());
3408c2ecf20Sopenharmony_ci	sdei_unmask_local_cpu();
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void _ipi_private_reset(void *ignored)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	int err;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	WARN_ON_ONCE(preemptible());
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PRIVATE_RESET, 0, 0, 0, 0, 0,
3508c2ecf20Sopenharmony_ci			     NULL);
3518c2ecf20Sopenharmony_ci	if (err && err != -EIO)
3528c2ecf20Sopenharmony_ci		pr_warn_once("failed to reset CPU[%u]: %d\n",
3538c2ecf20Sopenharmony_ci			     smp_processor_id(), err);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic int sdei_api_shared_reset(void)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_SHARED_RESET, 0, 0, 0, 0, 0,
3598c2ecf20Sopenharmony_ci			      NULL);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic void sdei_mark_interface_broken(void)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	pr_err("disabling SDEI firmware interface\n");
3658c2ecf20Sopenharmony_ci	on_each_cpu(&_ipi_mask_cpu, NULL, true);
3668c2ecf20Sopenharmony_ci	sdei_firmware_call = NULL;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int sdei_platform_reset(void)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	int err;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	on_each_cpu(&_ipi_private_reset, NULL, true);
3748c2ecf20Sopenharmony_ci	err = sdei_api_shared_reset();
3758c2ecf20Sopenharmony_ci	if (err) {
3768c2ecf20Sopenharmony_ci		pr_err("Failed to reset platform: %d\n", err);
3778c2ecf20Sopenharmony_ci		sdei_mark_interface_broken();
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return err;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int sdei_api_event_enable(u32 event_num)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_ENABLE, event_num, 0, 0, 0,
3868c2ecf20Sopenharmony_ci			      0, NULL);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/* Called directly by the hotplug callbacks */
3908c2ecf20Sopenharmony_cistatic void _local_event_enable(void *data)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int err;
3938c2ecf20Sopenharmony_ci	struct sdei_crosscall_args *arg = data;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	err = sdei_api_event_enable(arg->event->event_num);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	sdei_cross_call_return(arg, err);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ciint sdei_event_enable(u32 event_num)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	int err = -EINVAL;
4038c2ecf20Sopenharmony_ci	struct sdei_event *event;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	mutex_lock(&sdei_events_lock);
4068c2ecf20Sopenharmony_ci	event = sdei_event_find(event_num);
4078c2ecf20Sopenharmony_ci	if (!event) {
4088c2ecf20Sopenharmony_ci		mutex_unlock(&sdei_events_lock);
4098c2ecf20Sopenharmony_ci		return -ENOENT;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	cpus_read_lock();
4148c2ecf20Sopenharmony_ci	if (event->type == SDEI_EVENT_TYPE_SHARED)
4158c2ecf20Sopenharmony_ci		err = sdei_api_event_enable(event->event_num);
4168c2ecf20Sopenharmony_ci	else
4178c2ecf20Sopenharmony_ci		err = sdei_do_cross_call(_local_event_enable, event);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (!err) {
4208c2ecf20Sopenharmony_ci		spin_lock(&sdei_list_lock);
4218c2ecf20Sopenharmony_ci		event->reenable = true;
4228c2ecf20Sopenharmony_ci		spin_unlock(&sdei_list_lock);
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci	cpus_read_unlock();
4258c2ecf20Sopenharmony_ci	mutex_unlock(&sdei_events_lock);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return err;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int sdei_api_event_disable(u32 event_num)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_DISABLE, event_num, 0, 0,
4338c2ecf20Sopenharmony_ci			      0, 0, NULL);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic void _ipi_event_disable(void *data)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	int err;
4398c2ecf20Sopenharmony_ci	struct sdei_crosscall_args *arg = data;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	err = sdei_api_event_disable(arg->event->event_num);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	sdei_cross_call_return(arg, err);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciint sdei_event_disable(u32 event_num)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	int err = -EINVAL;
4498c2ecf20Sopenharmony_ci	struct sdei_event *event;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	mutex_lock(&sdei_events_lock);
4528c2ecf20Sopenharmony_ci	event = sdei_event_find(event_num);
4538c2ecf20Sopenharmony_ci	if (!event) {
4548c2ecf20Sopenharmony_ci		mutex_unlock(&sdei_events_lock);
4558c2ecf20Sopenharmony_ci		return -ENOENT;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
4598c2ecf20Sopenharmony_ci	event->reenable = false;
4608c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (event->type == SDEI_EVENT_TYPE_SHARED)
4638c2ecf20Sopenharmony_ci		err = sdei_api_event_disable(event->event_num);
4648c2ecf20Sopenharmony_ci	else
4658c2ecf20Sopenharmony_ci		err = sdei_do_cross_call(_ipi_event_disable, event);
4668c2ecf20Sopenharmony_ci	mutex_unlock(&sdei_events_lock);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return err;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic int sdei_api_event_unregister(u32 event_num)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_UNREGISTER, event_num, 0,
4748c2ecf20Sopenharmony_ci			      0, 0, 0, NULL);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/* Called directly by the hotplug callbacks */
4788c2ecf20Sopenharmony_cistatic void _local_event_unregister(void *data)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	int err;
4818c2ecf20Sopenharmony_ci	struct sdei_crosscall_args *arg = data;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	err = sdei_api_event_unregister(arg->event->event_num);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	sdei_cross_call_return(arg, err);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ciint sdei_event_unregister(u32 event_num)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	int err;
4918c2ecf20Sopenharmony_ci	struct sdei_event *event;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	WARN_ON(in_nmi());
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	mutex_lock(&sdei_events_lock);
4968c2ecf20Sopenharmony_ci	event = sdei_event_find(event_num);
4978c2ecf20Sopenharmony_ci	if (!event) {
4988c2ecf20Sopenharmony_ci		pr_warn("Event %u not registered\n", event_num);
4998c2ecf20Sopenharmony_ci		err = -ENOENT;
5008c2ecf20Sopenharmony_ci		goto unlock;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
5048c2ecf20Sopenharmony_ci	event->reregister = false;
5058c2ecf20Sopenharmony_ci	event->reenable = false;
5068c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (event->type == SDEI_EVENT_TYPE_SHARED)
5098c2ecf20Sopenharmony_ci		err = sdei_api_event_unregister(event->event_num);
5108c2ecf20Sopenharmony_ci	else
5118c2ecf20Sopenharmony_ci		err = sdei_do_cross_call(_local_event_unregister, event);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (err)
5148c2ecf20Sopenharmony_ci		goto unlock;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	sdei_event_destroy(event);
5178c2ecf20Sopenharmony_ciunlock:
5188c2ecf20Sopenharmony_ci	mutex_unlock(&sdei_events_lock);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return err;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci/*
5248c2ecf20Sopenharmony_ci * unregister events, but don't destroy them as they are re-registered by
5258c2ecf20Sopenharmony_ci * sdei_reregister_shared().
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_cistatic int sdei_unregister_shared(void)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	int err = 0;
5308c2ecf20Sopenharmony_ci	struct sdei_event *event;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	mutex_lock(&sdei_events_lock);
5338c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
5348c2ecf20Sopenharmony_ci	list_for_each_entry(event, &sdei_list, list) {
5358c2ecf20Sopenharmony_ci		if (event->type != SDEI_EVENT_TYPE_SHARED)
5368c2ecf20Sopenharmony_ci			continue;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci		err = sdei_api_event_unregister(event->event_num);
5398c2ecf20Sopenharmony_ci		if (err)
5408c2ecf20Sopenharmony_ci			break;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
5438c2ecf20Sopenharmony_ci	mutex_unlock(&sdei_events_lock);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return err;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic int sdei_api_event_register(u32 event_num, unsigned long entry_point,
5498c2ecf20Sopenharmony_ci				   void *arg, u64 flags, u64 affinity)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_REGISTER, event_num,
5528c2ecf20Sopenharmony_ci			      (unsigned long)entry_point, (unsigned long)arg,
5538c2ecf20Sopenharmony_ci			      flags, affinity, NULL);
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/* Called directly by the hotplug callbacks */
5578c2ecf20Sopenharmony_cistatic void _local_event_register(void *data)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	int err;
5608c2ecf20Sopenharmony_ci	struct sdei_registered_event *reg;
5618c2ecf20Sopenharmony_ci	struct sdei_crosscall_args *arg = data;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
5648c2ecf20Sopenharmony_ci	err = sdei_api_event_register(arg->event->event_num, sdei_entry_point,
5658c2ecf20Sopenharmony_ci				      reg, 0, 0);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	sdei_cross_call_return(arg, err);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ciint sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	int err;
5738c2ecf20Sopenharmony_ci	struct sdei_event *event;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	WARN_ON(in_nmi());
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	mutex_lock(&sdei_events_lock);
5788c2ecf20Sopenharmony_ci	if (sdei_event_find(event_num)) {
5798c2ecf20Sopenharmony_ci		pr_warn("Event %u already registered\n", event_num);
5808c2ecf20Sopenharmony_ci		err = -EBUSY;
5818c2ecf20Sopenharmony_ci		goto unlock;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	event = sdei_event_create(event_num, cb, arg);
5858c2ecf20Sopenharmony_ci	if (IS_ERR(event)) {
5868c2ecf20Sopenharmony_ci		err = PTR_ERR(event);
5878c2ecf20Sopenharmony_ci		pr_warn("Failed to create event %u: %d\n", event_num, err);
5888c2ecf20Sopenharmony_ci		goto unlock;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	cpus_read_lock();
5928c2ecf20Sopenharmony_ci	if (event->type == SDEI_EVENT_TYPE_SHARED) {
5938c2ecf20Sopenharmony_ci		err = sdei_api_event_register(event->event_num,
5948c2ecf20Sopenharmony_ci					      sdei_entry_point,
5958c2ecf20Sopenharmony_ci					      event->registered,
5968c2ecf20Sopenharmony_ci					      SDEI_EVENT_REGISTER_RM_ANY, 0);
5978c2ecf20Sopenharmony_ci	} else {
5988c2ecf20Sopenharmony_ci		err = sdei_do_cross_call(_local_event_register, event);
5998c2ecf20Sopenharmony_ci		if (err)
6008c2ecf20Sopenharmony_ci			sdei_do_cross_call(_local_event_unregister, event);
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	if (err) {
6048c2ecf20Sopenharmony_ci		sdei_event_destroy(event);
6058c2ecf20Sopenharmony_ci		pr_warn("Failed to register event %u: %d\n", event_num, err);
6068c2ecf20Sopenharmony_ci		goto cpu_unlock;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
6108c2ecf20Sopenharmony_ci	event->reregister = true;
6118c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
6128c2ecf20Sopenharmony_cicpu_unlock:
6138c2ecf20Sopenharmony_ci	cpus_read_unlock();
6148c2ecf20Sopenharmony_ciunlock:
6158c2ecf20Sopenharmony_ci	mutex_unlock(&sdei_events_lock);
6168c2ecf20Sopenharmony_ci	return err;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int sdei_reregister_shared(void)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	int err = 0;
6228c2ecf20Sopenharmony_ci	struct sdei_event *event;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	mutex_lock(&sdei_events_lock);
6258c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
6268c2ecf20Sopenharmony_ci	list_for_each_entry(event, &sdei_list, list) {
6278c2ecf20Sopenharmony_ci		if (event->type != SDEI_EVENT_TYPE_SHARED)
6288c2ecf20Sopenharmony_ci			continue;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		if (event->reregister) {
6318c2ecf20Sopenharmony_ci			err = sdei_api_event_register(event->event_num,
6328c2ecf20Sopenharmony_ci					sdei_entry_point, event->registered,
6338c2ecf20Sopenharmony_ci					SDEI_EVENT_REGISTER_RM_ANY, 0);
6348c2ecf20Sopenharmony_ci			if (err) {
6358c2ecf20Sopenharmony_ci				pr_err("Failed to re-register event %u\n",
6368c2ecf20Sopenharmony_ci				       event->event_num);
6378c2ecf20Sopenharmony_ci				sdei_event_destroy_llocked(event);
6388c2ecf20Sopenharmony_ci				break;
6398c2ecf20Sopenharmony_ci			}
6408c2ecf20Sopenharmony_ci		}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		if (event->reenable) {
6438c2ecf20Sopenharmony_ci			err = sdei_api_event_enable(event->event_num);
6448c2ecf20Sopenharmony_ci			if (err) {
6458c2ecf20Sopenharmony_ci				pr_err("Failed to re-enable event %u\n",
6468c2ecf20Sopenharmony_ci				       event->event_num);
6478c2ecf20Sopenharmony_ci				break;
6488c2ecf20Sopenharmony_ci			}
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
6528c2ecf20Sopenharmony_ci	mutex_unlock(&sdei_events_lock);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	return err;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic int sdei_cpuhp_down(unsigned int cpu)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct sdei_event *event;
6608c2ecf20Sopenharmony_ci	int err;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* un-register private events */
6638c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
6648c2ecf20Sopenharmony_ci	list_for_each_entry(event, &sdei_list, list) {
6658c2ecf20Sopenharmony_ci		if (event->type == SDEI_EVENT_TYPE_SHARED)
6668c2ecf20Sopenharmony_ci			continue;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		err = sdei_do_local_call(_local_event_unregister, event);
6698c2ecf20Sopenharmony_ci		if (err) {
6708c2ecf20Sopenharmony_ci			pr_err("Failed to unregister event %u: %d\n",
6718c2ecf20Sopenharmony_ci			       event->event_num, err);
6728c2ecf20Sopenharmony_ci		}
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return sdei_mask_local_cpu();
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic int sdei_cpuhp_up(unsigned int cpu)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	struct sdei_event *event;
6828c2ecf20Sopenharmony_ci	int err;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	/* re-register/enable private events */
6858c2ecf20Sopenharmony_ci	spin_lock(&sdei_list_lock);
6868c2ecf20Sopenharmony_ci	list_for_each_entry(event, &sdei_list, list) {
6878c2ecf20Sopenharmony_ci		if (event->type == SDEI_EVENT_TYPE_SHARED)
6888c2ecf20Sopenharmony_ci			continue;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		if (event->reregister) {
6918c2ecf20Sopenharmony_ci			err = sdei_do_local_call(_local_event_register, event);
6928c2ecf20Sopenharmony_ci			if (err) {
6938c2ecf20Sopenharmony_ci				pr_err("Failed to re-register event %u: %d\n",
6948c2ecf20Sopenharmony_ci				       event->event_num, err);
6958c2ecf20Sopenharmony_ci			}
6968c2ecf20Sopenharmony_ci		}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		if (event->reenable) {
6998c2ecf20Sopenharmony_ci			err = sdei_do_local_call(_local_event_enable, event);
7008c2ecf20Sopenharmony_ci			if (err) {
7018c2ecf20Sopenharmony_ci				pr_err("Failed to re-enable event %u: %d\n",
7028c2ecf20Sopenharmony_ci				       event->event_num, err);
7038c2ecf20Sopenharmony_ci			}
7048c2ecf20Sopenharmony_ci		}
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	spin_unlock(&sdei_list_lock);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	return sdei_unmask_local_cpu();
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci/* When entering idle, mask/unmask events for this cpu */
7128c2ecf20Sopenharmony_cistatic int sdei_pm_notifier(struct notifier_block *nb, unsigned long action,
7138c2ecf20Sopenharmony_ci			    void *data)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	int rv;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	WARN_ON_ONCE(preemptible());
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	switch (action) {
7208c2ecf20Sopenharmony_ci	case CPU_PM_ENTER:
7218c2ecf20Sopenharmony_ci		rv = sdei_mask_local_cpu();
7228c2ecf20Sopenharmony_ci		break;
7238c2ecf20Sopenharmony_ci	case CPU_PM_EXIT:
7248c2ecf20Sopenharmony_ci	case CPU_PM_ENTER_FAILED:
7258c2ecf20Sopenharmony_ci		rv = sdei_unmask_local_cpu();
7268c2ecf20Sopenharmony_ci		break;
7278c2ecf20Sopenharmony_ci	default:
7288c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (rv)
7328c2ecf20Sopenharmony_ci		return notifier_from_errno(rv);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return NOTIFY_OK;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic struct notifier_block sdei_pm_nb = {
7388c2ecf20Sopenharmony_ci	.notifier_call = sdei_pm_notifier,
7398c2ecf20Sopenharmony_ci};
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_cistatic int sdei_device_suspend(struct device *dev)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	on_each_cpu(_ipi_mask_cpu, NULL, true);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	return 0;
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic int sdei_device_resume(struct device *dev)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	on_each_cpu(_ipi_unmask_cpu, NULL, true);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return 0;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci/*
7568c2ecf20Sopenharmony_ci * We need all events to be reregistered when we resume from hibernate.
7578c2ecf20Sopenharmony_ci *
7588c2ecf20Sopenharmony_ci * The sequence is freeze->thaw. Reboot. freeze->restore. We unregister
7598c2ecf20Sopenharmony_ci * events during freeze, then re-register and re-enable them during thaw
7608c2ecf20Sopenharmony_ci * and restore.
7618c2ecf20Sopenharmony_ci */
7628c2ecf20Sopenharmony_cistatic int sdei_device_freeze(struct device *dev)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	int err;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* unregister private events */
7678c2ecf20Sopenharmony_ci	cpuhp_remove_state(sdei_entry_point);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	err = sdei_unregister_shared();
7708c2ecf20Sopenharmony_ci	if (err)
7718c2ecf20Sopenharmony_ci		return err;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return 0;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic int sdei_device_thaw(struct device *dev)
7778c2ecf20Sopenharmony_ci{
7788c2ecf20Sopenharmony_ci	int err;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* re-register shared events */
7818c2ecf20Sopenharmony_ci	err = sdei_reregister_shared();
7828c2ecf20Sopenharmony_ci	if (err) {
7838c2ecf20Sopenharmony_ci		pr_warn("Failed to re-register shared events...\n");
7848c2ecf20Sopenharmony_ci		sdei_mark_interface_broken();
7858c2ecf20Sopenharmony_ci		return err;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "SDEI",
7898c2ecf20Sopenharmony_ci				&sdei_cpuhp_up, &sdei_cpuhp_down);
7908c2ecf20Sopenharmony_ci	if (err < 0) {
7918c2ecf20Sopenharmony_ci		pr_warn("Failed to re-register CPU hotplug notifier...\n");
7928c2ecf20Sopenharmony_ci		return err;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	sdei_hp_state = err;
7968c2ecf20Sopenharmony_ci	return 0;
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic int sdei_device_restore(struct device *dev)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	int err;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	err = sdei_platform_reset();
8048c2ecf20Sopenharmony_ci	if (err)
8058c2ecf20Sopenharmony_ci		return err;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	return sdei_device_thaw(dev);
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sdei_pm_ops = {
8118c2ecf20Sopenharmony_ci	.suspend = sdei_device_suspend,
8128c2ecf20Sopenharmony_ci	.resume = sdei_device_resume,
8138c2ecf20Sopenharmony_ci	.freeze = sdei_device_freeze,
8148c2ecf20Sopenharmony_ci	.thaw = sdei_device_thaw,
8158c2ecf20Sopenharmony_ci	.restore = sdei_device_restore,
8168c2ecf20Sopenharmony_ci};
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci/*
8198c2ecf20Sopenharmony_ci * Mask all CPUs and unregister all events on panic, reboot or kexec.
8208c2ecf20Sopenharmony_ci */
8218c2ecf20Sopenharmony_cistatic int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action,
8228c2ecf20Sopenharmony_ci				void *data)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	/*
8258c2ecf20Sopenharmony_ci	 * We are going to reset the interface, after this there is no point
8268c2ecf20Sopenharmony_ci	 * doing work when we take CPUs offline.
8278c2ecf20Sopenharmony_ci	 */
8288c2ecf20Sopenharmony_ci	cpuhp_remove_state(sdei_hp_state);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	sdei_platform_reset();
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return NOTIFY_OK;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic struct notifier_block sdei_reboot_nb = {
8368c2ecf20Sopenharmony_ci	.notifier_call = sdei_reboot_notifier,
8378c2ecf20Sopenharmony_ci};
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cistatic void sdei_smccc_smc(unsigned long function_id,
8408c2ecf20Sopenharmony_ci			   unsigned long arg0, unsigned long arg1,
8418c2ecf20Sopenharmony_ci			   unsigned long arg2, unsigned long arg3,
8428c2ecf20Sopenharmony_ci			   unsigned long arg4, struct arm_smccc_res *res)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(sdei_smccc_smc);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic void sdei_smccc_hvc(unsigned long function_id,
8498c2ecf20Sopenharmony_ci			   unsigned long arg0, unsigned long arg1,
8508c2ecf20Sopenharmony_ci			   unsigned long arg2, unsigned long arg3,
8518c2ecf20Sopenharmony_ci			   unsigned long arg4, struct arm_smccc_res *res)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(sdei_smccc_hvc);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ciint sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
8588c2ecf20Sopenharmony_ci		       sdei_event_callback *critical_cb)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	int err;
8618c2ecf20Sopenharmony_ci	u64 result;
8628c2ecf20Sopenharmony_ci	u32 event_num;
8638c2ecf20Sopenharmony_ci	sdei_event_callback *cb;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
8668c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	event_num = ghes->generic->notify.vector;
8698c2ecf20Sopenharmony_ci	if (event_num == 0) {
8708c2ecf20Sopenharmony_ci		/*
8718c2ecf20Sopenharmony_ci		 * Event 0 is reserved by the specification for
8728c2ecf20Sopenharmony_ci		 * SDEI_EVENT_SIGNAL.
8738c2ecf20Sopenharmony_ci		 */
8748c2ecf20Sopenharmony_ci		return -EINVAL;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
8788c2ecf20Sopenharmony_ci				      &result);
8798c2ecf20Sopenharmony_ci	if (err)
8808c2ecf20Sopenharmony_ci		return err;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (result == SDEI_EVENT_PRIORITY_CRITICAL)
8838c2ecf20Sopenharmony_ci		cb = critical_cb;
8848c2ecf20Sopenharmony_ci	else
8858c2ecf20Sopenharmony_ci		cb = normal_cb;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	err = sdei_event_register(event_num, cb, ghes);
8888c2ecf20Sopenharmony_ci	if (!err)
8898c2ecf20Sopenharmony_ci		err = sdei_event_enable(event_num);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	return err;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ciint sdei_unregister_ghes(struct ghes *ghes)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	int i;
8978c2ecf20Sopenharmony_ci	int err;
8988c2ecf20Sopenharmony_ci	u32 event_num = ghes->generic->notify.vector;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	might_sleep();
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
9038c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/*
9068c2ecf20Sopenharmony_ci	 * The event may be running on another CPU. Disable it
9078c2ecf20Sopenharmony_ci	 * to stop new events, then try to unregister a few times.
9088c2ecf20Sopenharmony_ci	 */
9098c2ecf20Sopenharmony_ci	err = sdei_event_disable(event_num);
9108c2ecf20Sopenharmony_ci	if (err)
9118c2ecf20Sopenharmony_ci		return err;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
9148c2ecf20Sopenharmony_ci		err = sdei_event_unregister(event_num);
9158c2ecf20Sopenharmony_ci		if (err != -EINPROGRESS)
9168c2ecf20Sopenharmony_ci			break;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		schedule();
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	return err;
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_cistatic int sdei_get_conduit(struct platform_device *pdev)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	const char *method;
9278c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	sdei_firmware_call = NULL;
9308c2ecf20Sopenharmony_ci	if (np) {
9318c2ecf20Sopenharmony_ci		if (of_property_read_string(np, "method", &method)) {
9328c2ecf20Sopenharmony_ci			pr_warn("missing \"method\" property\n");
9338c2ecf20Sopenharmony_ci			return SMCCC_CONDUIT_NONE;
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		if (!strcmp("hvc", method)) {
9378c2ecf20Sopenharmony_ci			sdei_firmware_call = &sdei_smccc_hvc;
9388c2ecf20Sopenharmony_ci			return SMCCC_CONDUIT_HVC;
9398c2ecf20Sopenharmony_ci		} else if (!strcmp("smc", method)) {
9408c2ecf20Sopenharmony_ci			sdei_firmware_call = &sdei_smccc_smc;
9418c2ecf20Sopenharmony_ci			return SMCCC_CONDUIT_SMC;
9428c2ecf20Sopenharmony_ci		}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		pr_warn("invalid \"method\" property: %s\n", method);
9458c2ecf20Sopenharmony_ci	} else if (!acpi_disabled) {
9468c2ecf20Sopenharmony_ci		if (acpi_psci_use_hvc()) {
9478c2ecf20Sopenharmony_ci			sdei_firmware_call = &sdei_smccc_hvc;
9488c2ecf20Sopenharmony_ci			return SMCCC_CONDUIT_HVC;
9498c2ecf20Sopenharmony_ci		} else {
9508c2ecf20Sopenharmony_ci			sdei_firmware_call = &sdei_smccc_smc;
9518c2ecf20Sopenharmony_ci			return SMCCC_CONDUIT_SMC;
9528c2ecf20Sopenharmony_ci		}
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	return SMCCC_CONDUIT_NONE;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic int sdei_probe(struct platform_device *pdev)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	int err;
9618c2ecf20Sopenharmony_ci	u64 ver = 0;
9628c2ecf20Sopenharmony_ci	int conduit;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	conduit = sdei_get_conduit(pdev);
9658c2ecf20Sopenharmony_ci	if (!sdei_firmware_call)
9668c2ecf20Sopenharmony_ci		return 0;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	err = sdei_api_get_version(&ver);
9698c2ecf20Sopenharmony_ci	if (err) {
9708c2ecf20Sopenharmony_ci		pr_err("Failed to get SDEI version: %d\n", err);
9718c2ecf20Sopenharmony_ci		sdei_mark_interface_broken();
9728c2ecf20Sopenharmony_ci		return err;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n",
9768c2ecf20Sopenharmony_ci		(int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver),
9778c2ecf20Sopenharmony_ci		(int)SDEI_VERSION_VENDOR(ver));
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (SDEI_VERSION_MAJOR(ver) != 1) {
9808c2ecf20Sopenharmony_ci		pr_warn("Conflicting SDEI version detected.\n");
9818c2ecf20Sopenharmony_ci		sdei_mark_interface_broken();
9828c2ecf20Sopenharmony_ci		return -EINVAL;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	err = sdei_platform_reset();
9868c2ecf20Sopenharmony_ci	if (err)
9878c2ecf20Sopenharmony_ci		return err;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	sdei_entry_point = sdei_arch_get_entry_point(conduit);
9908c2ecf20Sopenharmony_ci	if (!sdei_entry_point) {
9918c2ecf20Sopenharmony_ci		/* Not supported due to hardware or boot configuration */
9928c2ecf20Sopenharmony_ci		sdei_mark_interface_broken();
9938c2ecf20Sopenharmony_ci		return 0;
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	err = cpu_pm_register_notifier(&sdei_pm_nb);
9978c2ecf20Sopenharmony_ci	if (err) {
9988c2ecf20Sopenharmony_ci		pr_warn("Failed to register CPU PM notifier...\n");
9998c2ecf20Sopenharmony_ci		goto error;
10008c2ecf20Sopenharmony_ci	}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	err = register_reboot_notifier(&sdei_reboot_nb);
10038c2ecf20Sopenharmony_ci	if (err) {
10048c2ecf20Sopenharmony_ci		pr_warn("Failed to register reboot notifier...\n");
10058c2ecf20Sopenharmony_ci		goto remove_cpupm;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "SDEI",
10098c2ecf20Sopenharmony_ci				&sdei_cpuhp_up, &sdei_cpuhp_down);
10108c2ecf20Sopenharmony_ci	if (err < 0) {
10118c2ecf20Sopenharmony_ci		pr_warn("Failed to register CPU hotplug notifier...\n");
10128c2ecf20Sopenharmony_ci		goto remove_reboot;
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	sdei_hp_state = err;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	return 0;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ciremove_reboot:
10208c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&sdei_reboot_nb);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ciremove_cpupm:
10238c2ecf20Sopenharmony_ci	cpu_pm_unregister_notifier(&sdei_pm_nb);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cierror:
10268c2ecf20Sopenharmony_ci	sdei_mark_interface_broken();
10278c2ecf20Sopenharmony_ci	return err;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic const struct of_device_id sdei_of_match[] = {
10318c2ecf20Sopenharmony_ci	{ .compatible = "arm,sdei-1.0" },
10328c2ecf20Sopenharmony_ci	{}
10338c2ecf20Sopenharmony_ci};
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cistatic struct platform_driver sdei_driver = {
10368c2ecf20Sopenharmony_ci	.driver		= {
10378c2ecf20Sopenharmony_ci		.name			= "sdei",
10388c2ecf20Sopenharmony_ci		.pm			= &sdei_pm_ops,
10398c2ecf20Sopenharmony_ci		.of_match_table		= sdei_of_match,
10408c2ecf20Sopenharmony_ci	},
10418c2ecf20Sopenharmony_ci	.probe		= sdei_probe,
10428c2ecf20Sopenharmony_ci};
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic bool __init sdei_present_acpi(void)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	acpi_status status;
10478c2ecf20Sopenharmony_ci	struct acpi_table_header *sdei_table_header;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	if (acpi_disabled)
10508c2ecf20Sopenharmony_ci		return false;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	status = acpi_get_table(ACPI_SIG_SDEI, 0, &sdei_table_header);
10538c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
10548c2ecf20Sopenharmony_ci		const char *msg = acpi_format_exception(status);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci		pr_info("Failed to get ACPI:SDEI table, %s\n", msg);
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
10598c2ecf20Sopenharmony_ci		return false;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	acpi_put_table(sdei_table_header);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	return true;
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_civoid __init sdei_init(void)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	struct platform_device *pdev;
10698c2ecf20Sopenharmony_ci	int ret;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	ret = platform_driver_register(&sdei_driver);
10728c2ecf20Sopenharmony_ci	if (ret || !sdei_present_acpi())
10738c2ecf20Sopenharmony_ci		return;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	pdev = platform_device_register_simple(sdei_driver.driver.name,
10768c2ecf20Sopenharmony_ci					       0, NULL, 0);
10778c2ecf20Sopenharmony_ci	if (IS_ERR(pdev)) {
10788c2ecf20Sopenharmony_ci		ret = PTR_ERR(pdev);
10798c2ecf20Sopenharmony_ci		platform_driver_unregister(&sdei_driver);
10808c2ecf20Sopenharmony_ci		pr_info("Failed to register ACPI:SDEI platform device %d\n",
10818c2ecf20Sopenharmony_ci			ret);
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ciint sdei_event_handler(struct pt_regs *regs,
10868c2ecf20Sopenharmony_ci		       struct sdei_registered_event *arg)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	int err;
10898c2ecf20Sopenharmony_ci	mm_segment_t orig_addr_limit;
10908c2ecf20Sopenharmony_ci	u32 event_num = arg->event_num;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	/*
10938c2ecf20Sopenharmony_ci	 * Save restore 'fs'.
10948c2ecf20Sopenharmony_ci	 * The architecture's entry code save/restores 'fs' when taking an
10958c2ecf20Sopenharmony_ci	 * exception from the kernel. This ensures addr_limit isn't inherited
10968c2ecf20Sopenharmony_ci	 * if you interrupted something that allowed the uaccess routines to
10978c2ecf20Sopenharmony_ci	 * access kernel memory.
10988c2ecf20Sopenharmony_ci	 * Do the same here because this doesn't come via the same entry code.
10998c2ecf20Sopenharmony_ci	*/
11008c2ecf20Sopenharmony_ci	orig_addr_limit = force_uaccess_begin();
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	err = arg->callback(event_num, regs, arg->callback_arg);
11038c2ecf20Sopenharmony_ci	if (err)
11048c2ecf20Sopenharmony_ci		pr_err_ratelimited("event %u on CPU %u failed with error: %d\n",
11058c2ecf20Sopenharmony_ci				   event_num, smp_processor_id(), err);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	force_uaccess_end(orig_addr_limit);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	return err;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(sdei_event_handler);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_civoid sdei_handler_abort(void)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	/*
11168c2ecf20Sopenharmony_ci	 * If the crash happened in an SDEI event handler then we need to
11178c2ecf20Sopenharmony_ci	 * finish the handler with the firmware so that we can have working
11188c2ecf20Sopenharmony_ci	 * interrupts in the crash kernel.
11198c2ecf20Sopenharmony_ci	 */
11208c2ecf20Sopenharmony_ci	if (__this_cpu_read(sdei_active_critical_event)) {
11218c2ecf20Sopenharmony_ci	        pr_warn("still in SDEI critical event context, attempting to finish handler.\n");
11228c2ecf20Sopenharmony_ci	        __sdei_handler_abort();
11238c2ecf20Sopenharmony_ci	        __this_cpu_write(sdei_active_critical_event, NULL);
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci	if (__this_cpu_read(sdei_active_normal_event)) {
11268c2ecf20Sopenharmony_ci	        pr_warn("still in SDEI normal event context, attempting to finish handler.\n");
11278c2ecf20Sopenharmony_ci	        __sdei_handler_abort();
11288c2ecf20Sopenharmony_ci	        __this_cpu_write(sdei_active_normal_event, NULL);
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci}
1131