cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wafer5823wdt.c (7070B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 *	ICP Wafer 5823 Single Board Computer WDT driver
      4 *	http://www.icpamerica.com/wafer_5823.php
      5 *	May also work on other similar models
      6 *
      7 *	(c) Copyright 2002 Justin Cormack <justin@street-vision.com>
      8 *
      9 *	Release 0.02
     10 *
     11 *	Based on advantechwdt.c which is based on wdt.c.
     12 *	Original copyright messages:
     13 *
     14 *	(c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
     15 *						All Rights Reserved.
     16 *
     17 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
     18 *	warranty for any of this software. This material is provided
     19 *	"AS-IS" and at no charge.
     20 *
     21 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
     22 *
     23 */
     24
     25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     26
     27#include <linux/module.h>
     28#include <linux/moduleparam.h>
     29#include <linux/miscdevice.h>
     30#include <linux/watchdog.h>
     31#include <linux/fs.h>
     32#include <linux/ioport.h>
     33#include <linux/notifier.h>
     34#include <linux/reboot.h>
     35#include <linux/init.h>
     36#include <linux/spinlock.h>
     37#include <linux/io.h>
     38#include <linux/uaccess.h>
     39
     40#define WATCHDOG_NAME "Wafer 5823 WDT"
     41#define PFX WATCHDOG_NAME ": "
     42#define WD_TIMO 60			/* 60 sec default timeout */
     43
     44static unsigned long wafwdt_is_open;
     45static char expect_close;
     46static DEFINE_SPINLOCK(wafwdt_lock);
     47
     48/*
     49 *	You must set these - there is no sane way to probe for this board.
     50 *
     51 *	To enable, write the timeout value in seconds (1 to 255) to I/O
     52 *	port WDT_START, then read the port to start the watchdog. To pat
     53 *	the dog, read port WDT_STOP to stop the timer, then read WDT_START
     54 *	to restart it again.
     55 */
     56
     57static int wdt_stop = 0x843;
     58static int wdt_start = 0x443;
     59
     60static int timeout = WD_TIMO;  /* in seconds */
     61module_param(timeout, int, 0);
     62MODULE_PARM_DESC(timeout,
     63		"Watchdog timeout in seconds. 1 <= timeout <= 255, default="
     64				__MODULE_STRING(WD_TIMO) ".");
     65
     66static bool nowayout = WATCHDOG_NOWAYOUT;
     67module_param(nowayout, bool, 0);
     68MODULE_PARM_DESC(nowayout,
     69		"Watchdog cannot be stopped once started (default="
     70				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     71
     72static void wafwdt_ping(void)
     73{
     74	/* pat watchdog */
     75	spin_lock(&wafwdt_lock);
     76	inb_p(wdt_stop);
     77	inb_p(wdt_start);
     78	spin_unlock(&wafwdt_lock);
     79}
     80
     81static void wafwdt_start(void)
     82{
     83	/* start up watchdog */
     84	outb_p(timeout, wdt_start);
     85	inb_p(wdt_start);
     86}
     87
     88static void wafwdt_stop(void)
     89{
     90	/* stop watchdog */
     91	inb_p(wdt_stop);
     92}
     93
     94static ssize_t wafwdt_write(struct file *file, const char __user *buf,
     95						size_t count, loff_t *ppos)
     96{
     97	/* See if we got the magic character 'V' and reload the timer */
     98	if (count) {
     99		if (!nowayout) {
    100			size_t i;
    101
    102			/* In case it was set long ago */
    103			expect_close = 0;
    104
    105			/* scan to see whether or not we got the magic
    106			   character */
    107			for (i = 0; i != count; i++) {
    108				char c;
    109				if (get_user(c, buf + i))
    110					return -EFAULT;
    111				if (c == 'V')
    112					expect_close = 42;
    113			}
    114		}
    115		/* Well, anyhow someone wrote to us, we should
    116		   return that favour */
    117		wafwdt_ping();
    118	}
    119	return count;
    120}
    121
    122static long wafwdt_ioctl(struct file *file, unsigned int cmd,
    123							unsigned long arg)
    124{
    125	int new_timeout;
    126	void __user *argp = (void __user *)arg;
    127	int __user *p = argp;
    128	static const struct watchdog_info ident = {
    129		.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
    130							WDIOF_MAGICCLOSE,
    131		.firmware_version = 1,
    132		.identity = "Wafer 5823 WDT",
    133	};
    134
    135	switch (cmd) {
    136	case WDIOC_GETSUPPORT:
    137		if (copy_to_user(argp, &ident, sizeof(ident)))
    138			return -EFAULT;
    139		break;
    140
    141	case WDIOC_GETSTATUS:
    142	case WDIOC_GETBOOTSTATUS:
    143		return put_user(0, p);
    144
    145	case WDIOC_SETOPTIONS:
    146	{
    147		int options, retval = -EINVAL;
    148
    149		if (get_user(options, p))
    150			return -EFAULT;
    151
    152		if (options & WDIOS_DISABLECARD) {
    153			wafwdt_stop();
    154			retval = 0;
    155		}
    156
    157		if (options & WDIOS_ENABLECARD) {
    158			wafwdt_start();
    159			retval = 0;
    160		}
    161
    162		return retval;
    163	}
    164
    165	case WDIOC_KEEPALIVE:
    166		wafwdt_ping();
    167		break;
    168
    169	case WDIOC_SETTIMEOUT:
    170		if (get_user(new_timeout, p))
    171			return -EFAULT;
    172		if ((new_timeout < 1) || (new_timeout > 255))
    173			return -EINVAL;
    174		timeout = new_timeout;
    175		wafwdt_stop();
    176		wafwdt_start();
    177		fallthrough;
    178	case WDIOC_GETTIMEOUT:
    179		return put_user(timeout, p);
    180
    181	default:
    182		return -ENOTTY;
    183	}
    184	return 0;
    185}
    186
    187static int wafwdt_open(struct inode *inode, struct file *file)
    188{
    189	if (test_and_set_bit(0, &wafwdt_is_open))
    190		return -EBUSY;
    191
    192	/*
    193	 *      Activate
    194	 */
    195	wafwdt_start();
    196	return stream_open(inode, file);
    197}
    198
    199static int wafwdt_close(struct inode *inode, struct file *file)
    200{
    201	if (expect_close == 42)
    202		wafwdt_stop();
    203	else {
    204		pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");
    205		wafwdt_ping();
    206	}
    207	clear_bit(0, &wafwdt_is_open);
    208	expect_close = 0;
    209	return 0;
    210}
    211
    212/*
    213 *	Notifier for system down
    214 */
    215
    216static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code,
    217								void *unused)
    218{
    219	if (code == SYS_DOWN || code == SYS_HALT)
    220		wafwdt_stop();
    221	return NOTIFY_DONE;
    222}
    223
    224/*
    225 *	Kernel Interfaces
    226 */
    227
    228static const struct file_operations wafwdt_fops = {
    229	.owner		= THIS_MODULE,
    230	.llseek		= no_llseek,
    231	.write		= wafwdt_write,
    232	.unlocked_ioctl	= wafwdt_ioctl,
    233	.compat_ioctl	= compat_ptr_ioctl,
    234	.open		= wafwdt_open,
    235	.release	= wafwdt_close,
    236};
    237
    238static struct miscdevice wafwdt_miscdev = {
    239	.minor	= WATCHDOG_MINOR,
    240	.name	= "watchdog",
    241	.fops	= &wafwdt_fops,
    242};
    243
    244/*
    245 *	The WDT needs to learn about soft shutdowns in order to
    246 *	turn the timebomb registers off.
    247 */
    248
    249static struct notifier_block wafwdt_notifier = {
    250	.notifier_call = wafwdt_notify_sys,
    251};
    252
    253static int __init wafwdt_init(void)
    254{
    255	int ret;
    256
    257	pr_info("WDT driver for Wafer 5823 single board computer initialising\n");
    258
    259	if (timeout < 1 || timeout > 255) {
    260		timeout = WD_TIMO;
    261		pr_info("timeout value must be 1 <= x <= 255, using %d\n",
    262			timeout);
    263	}
    264
    265	if (wdt_stop != wdt_start) {
    266		if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
    267			pr_err("I/O address 0x%04x already in use\n", wdt_stop);
    268			ret = -EIO;
    269			goto error;
    270		}
    271	}
    272
    273	if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
    274		pr_err("I/O address 0x%04x already in use\n", wdt_start);
    275		ret = -EIO;
    276		goto error2;
    277	}
    278
    279	ret = register_reboot_notifier(&wafwdt_notifier);
    280	if (ret != 0) {
    281		pr_err("cannot register reboot notifier (err=%d)\n", ret);
    282		goto error3;
    283	}
    284
    285	ret = misc_register(&wafwdt_miscdev);
    286	if (ret != 0) {
    287		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    288		       WATCHDOG_MINOR, ret);
    289		goto error4;
    290	}
    291
    292	pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
    293		timeout, nowayout);
    294
    295	return ret;
    296error4:
    297	unregister_reboot_notifier(&wafwdt_notifier);
    298error3:
    299	release_region(wdt_start, 1);
    300error2:
    301	if (wdt_stop != wdt_start)
    302		release_region(wdt_stop, 1);
    303error:
    304	return ret;
    305}
    306
    307static void __exit wafwdt_exit(void)
    308{
    309	misc_deregister(&wafwdt_miscdev);
    310	unregister_reboot_notifier(&wafwdt_notifier);
    311	if (wdt_stop != wdt_start)
    312		release_region(wdt_stop, 1);
    313	release_region(wdt_start, 1);
    314}
    315
    316module_init(wafwdt_init);
    317module_exit(wafwdt_exit);
    318
    319MODULE_AUTHOR("Justin Cormack");
    320MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
    321MODULE_LICENSE("GPL");
    322
    323/* end of wafer5823wdt.c */