162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Pvpanic Device Support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2013 Fujitsu.
662306a36Sopenharmony_ci *  Copyright (C) 2018 ZTE.
762306a36Sopenharmony_ci *  Copyright (C) 2021 Oracle.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/kexec.h>
1362306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/panic_notifier.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci#include <linux/cdev.h>
1962306a36Sopenharmony_ci#include <linux/list.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <uapi/misc/pvpanic.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "pvpanic.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciMODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>");
2662306a36Sopenharmony_ciMODULE_DESCRIPTION("pvpanic device driver");
2762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct list_head pvpanic_list;
3062306a36Sopenharmony_cistatic spinlock_t pvpanic_lock;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void
3362306a36Sopenharmony_cipvpanic_send_event(unsigned int event)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct pvpanic_instance *pi_cur;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (!spin_trylock(&pvpanic_lock))
3862306a36Sopenharmony_ci		return;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	list_for_each_entry(pi_cur, &pvpanic_list, list) {
4162306a36Sopenharmony_ci		if (event & pi_cur->capability & pi_cur->events)
4262306a36Sopenharmony_ci			iowrite8(event, pi_cur->base);
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci	spin_unlock(&pvpanic_lock);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int
4862306a36Sopenharmony_cipvpanic_panic_notify(struct notifier_block *nb, unsigned long code, void *unused)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	unsigned int event = PVPANIC_PANICKED;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (kexec_crash_loaded())
5362306a36Sopenharmony_ci		event = PVPANIC_CRASH_LOADED;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	pvpanic_send_event(event);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return NOTIFY_DONE;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * Call our notifier very early on panic, deferring the
6262306a36Sopenharmony_ci * action taken to the hypervisor.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic struct notifier_block pvpanic_panic_nb = {
6562306a36Sopenharmony_ci	.notifier_call = pvpanic_panic_notify,
6662306a36Sopenharmony_ci	.priority = INT_MAX,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void pvpanic_remove(void *param)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct pvpanic_instance *pi_cur, *pi_next;
7262306a36Sopenharmony_ci	struct pvpanic_instance *pi = param;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	spin_lock(&pvpanic_lock);
7562306a36Sopenharmony_ci	list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) {
7662306a36Sopenharmony_ci		if (pi_cur == pi) {
7762306a36Sopenharmony_ci			list_del(&pi_cur->list);
7862306a36Sopenharmony_ci			break;
7962306a36Sopenharmony_ci		}
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	spin_unlock(&pvpanic_lock);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciint devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	if (!pi || !pi->base)
8762306a36Sopenharmony_ci		return -EINVAL;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	spin_lock(&pvpanic_lock);
9062306a36Sopenharmony_ci	list_add(&pi->list, &pvpanic_list);
9162306a36Sopenharmony_ci	spin_unlock(&pvpanic_lock);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	dev_set_drvdata(dev, pi);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return devm_add_action_or_reset(dev, pvpanic_remove, pi);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_pvpanic_probe);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int pvpanic_init(void)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	INIT_LIST_HEAD(&pvpanic_list);
10262306a36Sopenharmony_ci	spin_lock_init(&pvpanic_lock);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list, &pvpanic_panic_nb);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_cimodule_init(pvpanic_init);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void pvpanic_exit(void)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	atomic_notifier_chain_unregister(&panic_notifier_list, &pvpanic_panic_nb);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_cimodule_exit(pvpanic_exit);
116