2021-03-24 16:49:14 +02:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Pvpanic Device Support
*
* Copyright ( C ) 2013 Fujitsu .
* Copyright ( C ) 2018 ZTE .
* Copyright ( C ) 2021 Oracle .
*/
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/kexec.h>
# include <linux/mod_devicetable.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2021-06-30 18:54:59 -07:00
# include <linux/panic_notifier.h>
2021-03-24 16:49:14 +02:00
# include <linux/types.h>
2021-03-24 16:49:15 +02:00
# include <linux/cdev.h>
# include <linux/list.h>
2021-03-24 16:49:14 +02:00
# include <uapi/misc/pvpanic.h>
# include "pvpanic.h"
MODULE_AUTHOR ( " Mihai Carabas <mihai.carabas@oracle.com> " ) ;
2021-08-29 15:43:54 +03:00
MODULE_DESCRIPTION ( " pvpanic device driver " ) ;
2021-03-24 16:49:14 +02:00
MODULE_LICENSE ( " GPL " ) ;
2021-03-31 20:17:06 +08:00
static struct list_head pvpanic_list ;
static spinlock_t pvpanic_lock ;
2021-03-24 16:49:14 +02:00
static void
pvpanic_send_event ( unsigned int event )
{
2021-03-24 16:49:15 +02:00
struct pvpanic_instance * pi_cur ;
2022-04-27 19:48:59 -03:00
if ( ! spin_trylock ( & pvpanic_lock ) )
return ;
2021-03-24 16:49:15 +02:00
list_for_each_entry ( pi_cur , & pvpanic_list , list ) {
if ( event & pi_cur - > capability & pi_cur - > events )
iowrite8 ( event , pi_cur - > base ) ;
}
spin_unlock ( & pvpanic_lock ) ;
2021-03-24 16:49:14 +02:00
}
static int
2021-08-29 15:43:54 +03:00
pvpanic_panic_notify ( struct notifier_block * nb , unsigned long code , void * unused )
2021-03-24 16:49:14 +02:00
{
unsigned int event = PVPANIC_PANICKED ;
if ( kexec_crash_loaded ( ) )
event = PVPANIC_CRASH_LOADED ;
pvpanic_send_event ( event ) ;
return NOTIFY_DONE ;
}
2022-04-27 19:48:59 -03:00
/*
* Call our notifier very early on panic , deferring the
* action taken to the hypervisor .
*/
2021-03-24 16:49:14 +02:00
static struct notifier_block pvpanic_panic_nb = {
. notifier_call = pvpanic_panic_notify ,
2022-04-27 19:48:59 -03:00
. priority = INT_MAX ,
2021-03-24 16:49:14 +02:00
} ;
2021-05-22 08:55:20 +02:00
static void pvpanic_remove ( void * param )
2021-03-24 16:49:14 +02:00
{
2021-03-24 16:49:15 +02:00
struct pvpanic_instance * pi_cur , * pi_next ;
2021-05-22 08:55:20 +02:00
struct pvpanic_instance * pi = param ;
2021-03-24 16:49:15 +02:00
spin_lock ( & pvpanic_lock ) ;
list_for_each_entry_safe ( pi_cur , pi_next , & pvpanic_list , list ) {
if ( pi_cur = = pi ) {
list_del ( & pi_cur - > list ) ;
break ;
}
}
spin_unlock ( & pvpanic_lock ) ;
2021-03-24 16:49:14 +02:00
}
2021-05-22 08:55:20 +02:00
int devm_pvpanic_probe ( struct device * dev , struct pvpanic_instance * pi )
{
if ( ! pi | | ! pi - > base )
return - EINVAL ;
spin_lock ( & pvpanic_lock ) ;
list_add ( & pi - > list , & pvpanic_list ) ;
spin_unlock ( & pvpanic_lock ) ;
2021-08-19 18:12:26 +03:00
dev_set_drvdata ( dev , pi ) ;
2021-05-22 08:55:20 +02:00
return devm_add_action_or_reset ( dev , pvpanic_remove , pi ) ;
}
EXPORT_SYMBOL_GPL ( devm_pvpanic_probe ) ;
2021-03-24 16:49:14 +02:00
2021-03-24 16:49:15 +02:00
static int pvpanic_init ( void )
2021-03-24 16:49:14 +02:00
{
2021-03-24 16:49:15 +02:00
INIT_LIST_HEAD ( & pvpanic_list ) ;
spin_lock_init ( & pvpanic_lock ) ;
2021-08-29 15:43:54 +03:00
atomic_notifier_chain_register ( & panic_notifier_list , & pvpanic_panic_nb ) ;
2021-03-24 16:49:15 +02:00
return 0 ;
2021-03-24 16:49:14 +02:00
}
2021-08-29 15:43:52 +03:00
module_init ( pvpanic_init ) ;
2021-03-24 16:49:15 +02:00
static void pvpanic_exit ( void )
{
2021-08-29 15:43:54 +03:00
atomic_notifier_chain_unregister ( & panic_notifier_list , & pvpanic_panic_nb ) ;
2021-03-24 16:49:15 +02:00
}
module_exit ( pvpanic_exit ) ;