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

ib700wdt.c (8333B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 *	IB700 Single Board Computer WDT driver
      4 *
      5 *	(c) Copyright 2001 Charles Howes <chowes@vsol.net>
      6 *
      7 *	Based on advantechwdt.c which is based on acquirewdt.c which
      8 *	is based on wdt.c.
      9 *
     10 *	(c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
     11 *
     12 *	Based on acquirewdt.c which is based on wdt.c.
     13 *	Original copyright messages:
     14 *
     15 *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
     16 *						All Rights Reserved.
     17 *
     18 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
     19 *	warranty for any of this software. This material is provided
     20 *	"AS-IS" and at no charge.
     21 *
     22 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
     23 *
     24 *	14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
     25 *	     Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
     26 *	     Added timeout module option to override default
     27 *
     28 */
     29
     30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     31
     32#include <linux/module.h>
     33#include <linux/types.h>
     34#include <linux/miscdevice.h>
     35#include <linux/watchdog.h>
     36#include <linux/ioport.h>
     37#include <linux/fs.h>
     38#include <linux/init.h>
     39#include <linux/spinlock.h>
     40#include <linux/moduleparam.h>
     41#include <linux/platform_device.h>
     42#include <linux/io.h>
     43#include <linux/uaccess.h>
     44
     45
     46static struct platform_device *ibwdt_platform_device;
     47static unsigned long ibwdt_is_open;
     48static DEFINE_SPINLOCK(ibwdt_lock);
     49static char expect_close;
     50
     51/* Module information */
     52#define DRV_NAME "ib700wdt"
     53
     54/*
     55 *
     56 * Watchdog Timer Configuration
     57 *
     58 * The function of the watchdog timer is to reset the system
     59 * automatically and is defined at I/O port 0443H.  To enable the
     60 * watchdog timer and allow the system to reset, write I/O port 0443H.
     61 * To disable the timer, write I/O port 0441H for the system to stop the
     62 * watchdog function.  The timer has a tolerance of 20% for its
     63 * intervals.
     64 *
     65 * The following describes how the timer should be programmed.
     66 *
     67 * Enabling Watchdog:
     68 * MOV AX,000FH (Choose the values from 0 to F)
     69 * MOV DX,0443H
     70 * OUT DX,AX
     71 *
     72 * Disabling Watchdog:
     73 * MOV AX,000FH (Any value is fine.)
     74 * MOV DX,0441H
     75 * OUT DX,AX
     76 *
     77 * Watchdog timer control table:
     78 * Level   Value  Time/sec | Level Value Time/sec
     79 *   1       F       0     |   9     7      16
     80 *   2       E       2     |   10    6      18
     81 *   3       D       4     |   11    5      20
     82 *   4       C       6     |   12    4      22
     83 *   5       B       8     |   13    3      24
     84 *   6       A       10    |   14    2      26
     85 *   7       9       12    |   15    1      28
     86 *   8       8       14    |   16    0      30
     87 *
     88 */
     89
     90#define WDT_STOP 0x441
     91#define WDT_START 0x443
     92
     93/* Default timeout */
     94#define WATCHDOG_TIMEOUT 30		/* 30 seconds +/- 20% */
     95static int timeout = WATCHDOG_TIMEOUT;	/* in seconds */
     96module_param(timeout, int, 0);
     97MODULE_PARM_DESC(timeout,
     98	"Watchdog timeout in seconds. 0<= timeout <=30, default="
     99		__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
    100
    101static bool nowayout = WATCHDOG_NOWAYOUT;
    102module_param(nowayout, bool, 0);
    103MODULE_PARM_DESC(nowayout,
    104		"Watchdog cannot be stopped once started (default="
    105				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
    106
    107
    108/*
    109 *	Watchdog Operations
    110 */
    111
    112static void ibwdt_ping(void)
    113{
    114	int wd_margin = 15 - ((timeout + 1) / 2);
    115
    116	spin_lock(&ibwdt_lock);
    117
    118	/* Write a watchdog value */
    119	outb_p(wd_margin, WDT_START);
    120
    121	spin_unlock(&ibwdt_lock);
    122}
    123
    124static void ibwdt_disable(void)
    125{
    126	spin_lock(&ibwdt_lock);
    127	outb_p(0, WDT_STOP);
    128	spin_unlock(&ibwdt_lock);
    129}
    130
    131static int ibwdt_set_heartbeat(int t)
    132{
    133	if (t < 0 || t > 30)
    134		return -EINVAL;
    135
    136	timeout = t;
    137	return 0;
    138}
    139
    140/*
    141 *	/dev/watchdog handling
    142 */
    143
    144static ssize_t ibwdt_write(struct file *file, const char __user *buf,
    145						size_t count, loff_t *ppos)
    146{
    147	if (count) {
    148		if (!nowayout) {
    149			size_t i;
    150
    151			/* In case it was set long ago */
    152			expect_close = 0;
    153
    154			for (i = 0; i != count; i++) {
    155				char c;
    156				if (get_user(c, buf + i))
    157					return -EFAULT;
    158				if (c == 'V')
    159					expect_close = 42;
    160			}
    161		}
    162		ibwdt_ping();
    163	}
    164	return count;
    165}
    166
    167static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    168{
    169	int new_margin;
    170	void __user *argp = (void __user *)arg;
    171	int __user *p = argp;
    172
    173	static const struct watchdog_info ident = {
    174		.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
    175							| WDIOF_MAGICCLOSE,
    176		.firmware_version = 1,
    177		.identity = "IB700 WDT",
    178	};
    179
    180	switch (cmd) {
    181	case WDIOC_GETSUPPORT:
    182		if (copy_to_user(argp, &ident, sizeof(ident)))
    183			return -EFAULT;
    184		break;
    185
    186	case WDIOC_GETSTATUS:
    187	case WDIOC_GETBOOTSTATUS:
    188		return put_user(0, p);
    189
    190	case WDIOC_SETOPTIONS:
    191	{
    192		int options, retval = -EINVAL;
    193
    194		if (get_user(options, p))
    195			return -EFAULT;
    196
    197		if (options & WDIOS_DISABLECARD) {
    198			ibwdt_disable();
    199			retval = 0;
    200		}
    201		if (options & WDIOS_ENABLECARD) {
    202			ibwdt_ping();
    203			retval = 0;
    204		}
    205		return retval;
    206	}
    207	case WDIOC_KEEPALIVE:
    208		ibwdt_ping();
    209		break;
    210
    211	case WDIOC_SETTIMEOUT:
    212		if (get_user(new_margin, p))
    213			return -EFAULT;
    214		if (ibwdt_set_heartbeat(new_margin))
    215			return -EINVAL;
    216		ibwdt_ping();
    217		fallthrough;
    218
    219	case WDIOC_GETTIMEOUT:
    220		return put_user(timeout, p);
    221
    222	default:
    223		return -ENOTTY;
    224	}
    225	return 0;
    226}
    227
    228static int ibwdt_open(struct inode *inode, struct file *file)
    229{
    230	if (test_and_set_bit(0, &ibwdt_is_open))
    231		return -EBUSY;
    232	if (nowayout)
    233		__module_get(THIS_MODULE);
    234
    235	/* Activate */
    236	ibwdt_ping();
    237	return stream_open(inode, file);
    238}
    239
    240static int ibwdt_close(struct inode *inode, struct file *file)
    241{
    242	if (expect_close == 42) {
    243		ibwdt_disable();
    244	} else {
    245		pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");
    246		ibwdt_ping();
    247	}
    248	clear_bit(0, &ibwdt_is_open);
    249	expect_close = 0;
    250	return 0;
    251}
    252
    253/*
    254 *	Kernel Interfaces
    255 */
    256
    257static const struct file_operations ibwdt_fops = {
    258	.owner		= THIS_MODULE,
    259	.llseek		= no_llseek,
    260	.write		= ibwdt_write,
    261	.unlocked_ioctl	= ibwdt_ioctl,
    262	.compat_ioctl	= compat_ptr_ioctl,
    263	.open		= ibwdt_open,
    264	.release	= ibwdt_close,
    265};
    266
    267static struct miscdevice ibwdt_miscdev = {
    268	.minor = WATCHDOG_MINOR,
    269	.name = "watchdog",
    270	.fops = &ibwdt_fops,
    271};
    272
    273/*
    274 *	Init & exit routines
    275 */
    276
    277static int __init ibwdt_probe(struct platform_device *dev)
    278{
    279	int res;
    280
    281#if WDT_START != WDT_STOP
    282	if (!request_region(WDT_STOP, 1, "IB700 WDT")) {
    283		pr_err("STOP method I/O %X is not available\n", WDT_STOP);
    284		res = -EIO;
    285		goto out_nostopreg;
    286	}
    287#endif
    288
    289	if (!request_region(WDT_START, 1, "IB700 WDT")) {
    290		pr_err("START method I/O %X is not available\n", WDT_START);
    291		res = -EIO;
    292		goto out_nostartreg;
    293	}
    294
    295	/* Check that the heartbeat value is within it's range ;
    296	 * if not reset to the default */
    297	if (ibwdt_set_heartbeat(timeout)) {
    298		ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
    299		pr_info("timeout value must be 0<=x<=30, using %d\n", timeout);
    300	}
    301
    302	res = misc_register(&ibwdt_miscdev);
    303	if (res) {
    304		pr_err("failed to register misc device\n");
    305		goto out_nomisc;
    306	}
    307	return 0;
    308
    309out_nomisc:
    310	release_region(WDT_START, 1);
    311out_nostartreg:
    312#if WDT_START != WDT_STOP
    313	release_region(WDT_STOP, 1);
    314#endif
    315out_nostopreg:
    316	return res;
    317}
    318
    319static int ibwdt_remove(struct platform_device *dev)
    320{
    321	misc_deregister(&ibwdt_miscdev);
    322	release_region(WDT_START, 1);
    323#if WDT_START != WDT_STOP
    324	release_region(WDT_STOP, 1);
    325#endif
    326	return 0;
    327}
    328
    329static void ibwdt_shutdown(struct platform_device *dev)
    330{
    331	/* Turn the WDT off if we have a soft shutdown */
    332	ibwdt_disable();
    333}
    334
    335static struct platform_driver ibwdt_driver = {
    336	.remove		= ibwdt_remove,
    337	.shutdown	= ibwdt_shutdown,
    338	.driver		= {
    339		.name	= DRV_NAME,
    340	},
    341};
    342
    343static int __init ibwdt_init(void)
    344{
    345	int err;
    346
    347	pr_info("WDT driver for IB700 single board computer initialising\n");
    348
    349	ibwdt_platform_device = platform_device_register_simple(DRV_NAME,
    350								-1, NULL, 0);
    351	if (IS_ERR(ibwdt_platform_device))
    352		return PTR_ERR(ibwdt_platform_device);
    353
    354	err = platform_driver_probe(&ibwdt_driver, ibwdt_probe);
    355	if (err)
    356		goto unreg_platform_device;
    357
    358	return 0;
    359
    360unreg_platform_device:
    361	platform_device_unregister(ibwdt_platform_device);
    362	return err;
    363}
    364
    365static void __exit ibwdt_exit(void)
    366{
    367	platform_device_unregister(ibwdt_platform_device);
    368	platform_driver_unregister(&ibwdt_driver);
    369	pr_info("Watchdog Module Unloaded\n");
    370}
    371
    372module_init(ibwdt_init);
    373module_exit(ibwdt_exit);
    374
    375MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
    376MODULE_DESCRIPTION("IB700 SBC watchdog driver");
    377MODULE_LICENSE("GPL");
    378
    379/* end of ib700wdt.c */