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

sbc_fitpc2_wdt.c (5443B)


      1/*
      2 * Watchdog driver for SBC-FITPC2 board
      3 *
      4 * Author: Denis Turischev <denis@compulab.co.il>
      5 *
      6 * Adapted from the IXP2000 watchdog driver by Deepak Saxena.
      7 *
      8 * This file is licensed under  the terms of the GNU General Public
      9 * License version 2. This program is licensed "as is" without any
     10 * warranty of any kind, whether express or implied.
     11 */
     12
     13#define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
     14
     15#include <linux/module.h>
     16#include <linux/types.h>
     17#include <linux/miscdevice.h>
     18#include <linux/watchdog.h>
     19#include <linux/ioport.h>
     20#include <linux/delay.h>
     21#include <linux/fs.h>
     22#include <linux/init.h>
     23#include <linux/moduleparam.h>
     24#include <linux/dmi.h>
     25#include <linux/io.h>
     26#include <linux/uaccess.h>
     27
     28
     29static bool nowayout = WATCHDOG_NOWAYOUT;
     30static unsigned int margin = 60;	/* (secs) Default is 1 minute */
     31static unsigned long wdt_status;
     32static DEFINE_MUTEX(wdt_lock);
     33
     34#define WDT_IN_USE		0
     35#define WDT_OK_TO_CLOSE		1
     36
     37#define COMMAND_PORT		0x4c
     38#define DATA_PORT		0x48
     39
     40#define IFACE_ON_COMMAND	1
     41#define REBOOT_COMMAND		2
     42
     43#define WATCHDOG_NAME		"SBC-FITPC2 Watchdog"
     44
     45static void wdt_send_data(unsigned char command, unsigned char data)
     46{
     47	outb(data, DATA_PORT);
     48	msleep(200);
     49	outb(command, COMMAND_PORT);
     50	msleep(100);
     51}
     52
     53static void wdt_enable(void)
     54{
     55	mutex_lock(&wdt_lock);
     56	wdt_send_data(IFACE_ON_COMMAND, 1);
     57	wdt_send_data(REBOOT_COMMAND, margin);
     58	mutex_unlock(&wdt_lock);
     59}
     60
     61static void wdt_disable(void)
     62{
     63	mutex_lock(&wdt_lock);
     64	wdt_send_data(IFACE_ON_COMMAND, 0);
     65	wdt_send_data(REBOOT_COMMAND, 0);
     66	mutex_unlock(&wdt_lock);
     67}
     68
     69static int fitpc2_wdt_open(struct inode *inode, struct file *file)
     70{
     71	if (test_and_set_bit(WDT_IN_USE, &wdt_status))
     72		return -EBUSY;
     73
     74	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
     75
     76	wdt_enable();
     77
     78	return stream_open(inode, file);
     79}
     80
     81static ssize_t fitpc2_wdt_write(struct file *file, const char __user *data,
     82						size_t len, loff_t *ppos)
     83{
     84	size_t i;
     85
     86	if (!len)
     87		return 0;
     88
     89	if (nowayout) {
     90		len = 0;
     91		goto out;
     92	}
     93
     94	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
     95
     96	for (i = 0; i != len; i++) {
     97		char c;
     98
     99		if (get_user(c, data + i))
    100			return -EFAULT;
    101
    102		if (c == 'V')
    103			set_bit(WDT_OK_TO_CLOSE, &wdt_status);
    104	}
    105
    106out:
    107	wdt_enable();
    108
    109	return len;
    110}
    111
    112
    113static const struct watchdog_info ident = {
    114	.options	= WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
    115				WDIOF_KEEPALIVEPING,
    116	.identity	= WATCHDOG_NAME,
    117};
    118
    119
    120static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd,
    121							unsigned long arg)
    122{
    123	int ret = -ENOTTY;
    124	int time;
    125
    126	switch (cmd) {
    127	case WDIOC_GETSUPPORT:
    128		ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
    129				   sizeof(ident)) ? -EFAULT : 0;
    130		break;
    131
    132	case WDIOC_GETSTATUS:
    133		ret = put_user(0, (int __user *)arg);
    134		break;
    135
    136	case WDIOC_GETBOOTSTATUS:
    137		ret = put_user(0, (int __user *)arg);
    138		break;
    139
    140	case WDIOC_KEEPALIVE:
    141		wdt_enable();
    142		ret = 0;
    143		break;
    144
    145	case WDIOC_SETTIMEOUT:
    146		ret = get_user(time, (int __user *)arg);
    147		if (ret)
    148			break;
    149
    150		if (time < 31 || time > 255) {
    151			ret = -EINVAL;
    152			break;
    153		}
    154
    155		margin = time;
    156		wdt_enable();
    157		fallthrough;
    158
    159	case WDIOC_GETTIMEOUT:
    160		ret = put_user(margin, (int __user *)arg);
    161		break;
    162	}
    163
    164	return ret;
    165}
    166
    167static int fitpc2_wdt_release(struct inode *inode, struct file *file)
    168{
    169	if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
    170		wdt_disable();
    171		pr_info("Device disabled\n");
    172	} else {
    173		pr_warn("Device closed unexpectedly - timer will not stop\n");
    174		wdt_enable();
    175	}
    176
    177	clear_bit(WDT_IN_USE, &wdt_status);
    178	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
    179
    180	return 0;
    181}
    182
    183
    184static const struct file_operations fitpc2_wdt_fops = {
    185	.owner		= THIS_MODULE,
    186	.llseek		= no_llseek,
    187	.write		= fitpc2_wdt_write,
    188	.unlocked_ioctl	= fitpc2_wdt_ioctl,
    189	.compat_ioctl	= compat_ptr_ioctl,
    190	.open		= fitpc2_wdt_open,
    191	.release	= fitpc2_wdt_release,
    192};
    193
    194static struct miscdevice fitpc2_wdt_miscdev = {
    195	.minor		= WATCHDOG_MINOR,
    196	.name		= "watchdog",
    197	.fops		= &fitpc2_wdt_fops,
    198};
    199
    200static int __init fitpc2_wdt_init(void)
    201{
    202	int err;
    203	const char *brd_name;
    204
    205	brd_name = dmi_get_system_info(DMI_BOARD_NAME);
    206
    207	if (!brd_name || !strstr(brd_name, "SBC-FITPC2"))
    208		return -ENODEV;
    209
    210	pr_info("%s found\n", brd_name);
    211
    212	if (!request_region(COMMAND_PORT, 1, WATCHDOG_NAME)) {
    213		pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT);
    214		return -EIO;
    215	}
    216
    217	if (!request_region(DATA_PORT, 1, WATCHDOG_NAME)) {
    218		pr_err("I/O address 0x%04x already in use\n", DATA_PORT);
    219		err = -EIO;
    220		goto err_data_port;
    221	}
    222
    223	if (margin < 31 || margin > 255) {
    224		pr_err("margin must be in range 31 - 255 seconds, you tried to set %d\n",
    225		       margin);
    226		err = -EINVAL;
    227		goto err_margin;
    228	}
    229
    230	err = misc_register(&fitpc2_wdt_miscdev);
    231	if (err) {
    232		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    233		       WATCHDOG_MINOR, err);
    234		goto err_margin;
    235	}
    236
    237	return 0;
    238
    239err_margin:
    240	release_region(DATA_PORT, 1);
    241err_data_port:
    242	release_region(COMMAND_PORT, 1);
    243
    244	return err;
    245}
    246
    247static void __exit fitpc2_wdt_exit(void)
    248{
    249	misc_deregister(&fitpc2_wdt_miscdev);
    250	release_region(DATA_PORT, 1);
    251	release_region(COMMAND_PORT, 1);
    252}
    253
    254module_init(fitpc2_wdt_init);
    255module_exit(fitpc2_wdt_exit);
    256
    257MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
    258MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
    259
    260module_param(margin, int, 0);
    261MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
    262
    263module_param(nowayout, bool, 0);
    264MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
    265
    266MODULE_LICENSE("GPL");