18c2ecf20Sopenharmony_ci/* UML hardware watchdog, shamelessly stolen from:
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci *	SoftDog	0.05:	A Software Watchdog Device
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
68c2ecf20Sopenharmony_ci *				http://www.redhat.com
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *	This program is free software; you can redistribute it and/or
98c2ecf20Sopenharmony_ci *	modify it under the terms of the GNU General Public License
108c2ecf20Sopenharmony_ci *	as published by the Free Software Foundation; either version
118c2ecf20Sopenharmony_ci *	2 of the License, or (at your option) any later version.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
148c2ecf20Sopenharmony_ci *	warranty for any of this software. This material is provided
158c2ecf20Sopenharmony_ci *	"AS-IS" and at no charge.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *	Software only watchdog driver. Unlike its big brother the WDT501P
208c2ecf20Sopenharmony_ci *	driver this won't always recover a failed machine.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci *  03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
238c2ecf20Sopenharmony_ci *	Modularised.
248c2ecf20Sopenharmony_ci *	Added soft_margin; use upon insmod to change the timer delay.
258c2ecf20Sopenharmony_ci *	NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
268c2ecf20Sopenharmony_ci *	    minors.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci *  19980911 Alan Cox
298c2ecf20Sopenharmony_ci *	Made SMP safe for 2.3.x
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci *  20011127 Joel Becker (jlbec@evilplan.org>
328c2ecf20Sopenharmony_ci *	Added soft_noboot; Allows testing the softdog trigger without
338c2ecf20Sopenharmony_ci *	requiring a recompile.
348c2ecf20Sopenharmony_ci *	Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <linux/module.h>
388c2ecf20Sopenharmony_ci#include <linux/types.h>
398c2ecf20Sopenharmony_ci#include <linux/kernel.h>
408c2ecf20Sopenharmony_ci#include <linux/fs.h>
418c2ecf20Sopenharmony_ci#include <linux/mm.h>
428c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
438c2ecf20Sopenharmony_ci#include <linux/watchdog.h>
448c2ecf20Sopenharmony_ci#include <linux/reboot.h>
458c2ecf20Sopenharmony_ci#include <linux/mutex.h>
468c2ecf20Sopenharmony_ci#include <linux/init.h>
478c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
488c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
498c2ecf20Sopenharmony_ci#include "mconsole.h"
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(harddog_mutex);
548c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(lock);
558c2ecf20Sopenharmony_cistatic int timer_alive;
568c2ecf20Sopenharmony_cistatic int harddog_in_fd = -1;
578c2ecf20Sopenharmony_cistatic int harddog_out_fd = -1;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci *	Allow only one person to hold it open
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciextern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int harddog_open(struct inode *inode, struct file *file)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int err = -EBUSY;
688c2ecf20Sopenharmony_ci	char *sock = NULL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	mutex_lock(&harddog_mutex);
718c2ecf20Sopenharmony_ci	spin_lock(&lock);
728c2ecf20Sopenharmony_ci	if(timer_alive)
738c2ecf20Sopenharmony_ci		goto err;
748c2ecf20Sopenharmony_ci#ifdef CONFIG_WATCHDOG_NOWAYOUT
758c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
768c2ecf20Sopenharmony_ci#endif
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#ifdef CONFIG_MCONSOLE
798c2ecf20Sopenharmony_ci	sock = mconsole_notify_socket();
808c2ecf20Sopenharmony_ci#endif
818c2ecf20Sopenharmony_ci	err = start_watchdog(&harddog_in_fd, &harddog_out_fd, sock);
828c2ecf20Sopenharmony_ci	if(err)
838c2ecf20Sopenharmony_ci		goto err;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	timer_alive = 1;
868c2ecf20Sopenharmony_ci	spin_unlock(&lock);
878c2ecf20Sopenharmony_ci	mutex_unlock(&harddog_mutex);
888c2ecf20Sopenharmony_ci	return stream_open(inode, file);
898c2ecf20Sopenharmony_cierr:
908c2ecf20Sopenharmony_ci	spin_unlock(&lock);
918c2ecf20Sopenharmony_ci	mutex_unlock(&harddog_mutex);
928c2ecf20Sopenharmony_ci	return err;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciextern void stop_watchdog(int in_fd, int out_fd);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int harddog_release(struct inode *inode, struct file *file)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	/*
1008c2ecf20Sopenharmony_ci	 *	Shut off the timer.
1018c2ecf20Sopenharmony_ci	 */
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	spin_lock(&lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	stop_watchdog(harddog_in_fd, harddog_out_fd);
1068c2ecf20Sopenharmony_ci	harddog_in_fd = -1;
1078c2ecf20Sopenharmony_ci	harddog_out_fd = -1;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	timer_alive=0;
1108c2ecf20Sopenharmony_ci	spin_unlock(&lock);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciextern int ping_watchdog(int fd);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic ssize_t harddog_write(struct file *file, const char __user *data, size_t len,
1188c2ecf20Sopenharmony_ci			     loff_t *ppos)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	/*
1218c2ecf20Sopenharmony_ci	 *	Refresh the timer.
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	if(len)
1248c2ecf20Sopenharmony_ci		return ping_watchdog(harddog_out_fd);
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int harddog_ioctl_unlocked(struct file *file,
1298c2ecf20Sopenharmony_ci				  unsigned int cmd, unsigned long arg)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	void __user *argp= (void __user *)arg;
1328c2ecf20Sopenharmony_ci	static struct watchdog_info ident = {
1338c2ecf20Sopenharmony_ci		WDIOC_SETTIMEOUT,
1348c2ecf20Sopenharmony_ci		0,
1358c2ecf20Sopenharmony_ci		"UML Hardware Watchdog"
1368c2ecf20Sopenharmony_ci	};
1378c2ecf20Sopenharmony_ci	switch (cmd) {
1388c2ecf20Sopenharmony_ci		default:
1398c2ecf20Sopenharmony_ci			return -ENOTTY;
1408c2ecf20Sopenharmony_ci		case WDIOC_GETSUPPORT:
1418c2ecf20Sopenharmony_ci			if(copy_to_user(argp, &ident, sizeof(ident)))
1428c2ecf20Sopenharmony_ci				return -EFAULT;
1438c2ecf20Sopenharmony_ci			return 0;
1448c2ecf20Sopenharmony_ci		case WDIOC_GETSTATUS:
1458c2ecf20Sopenharmony_ci		case WDIOC_GETBOOTSTATUS:
1468c2ecf20Sopenharmony_ci			return put_user(0,(int __user *)argp);
1478c2ecf20Sopenharmony_ci		case WDIOC_KEEPALIVE:
1488c2ecf20Sopenharmony_ci			return ping_watchdog(harddog_out_fd);
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic long harddog_ioctl(struct file *file,
1538c2ecf20Sopenharmony_ci			  unsigned int cmd, unsigned long arg)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	long ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	mutex_lock(&harddog_mutex);
1588c2ecf20Sopenharmony_ci	ret = harddog_ioctl_unlocked(file, cmd, arg);
1598c2ecf20Sopenharmony_ci	mutex_unlock(&harddog_mutex);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return ret;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic const struct file_operations harddog_fops = {
1658c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1668c2ecf20Sopenharmony_ci	.write		= harddog_write,
1678c2ecf20Sopenharmony_ci	.unlocked_ioctl	= harddog_ioctl,
1688c2ecf20Sopenharmony_ci	.compat_ioctl	= compat_ptr_ioctl,
1698c2ecf20Sopenharmony_ci	.open		= harddog_open,
1708c2ecf20Sopenharmony_ci	.release	= harddog_release,
1718c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic struct miscdevice harddog_miscdev = {
1758c2ecf20Sopenharmony_ci	.minor		= WATCHDOG_MINOR,
1768c2ecf20Sopenharmony_ci	.name		= "watchdog",
1778c2ecf20Sopenharmony_ci	.fops		= &harddog_fops,
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_cimodule_misc_device(harddog_miscdev);
180