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

machzwd.c (9905B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  MachZ ZF-Logic Watchdog Timer driver for Linux
      4 *
      5 *  The author does NOT admit liability nor provide warranty for
      6 *  any of this software. This material is provided "AS-IS" in
      7 *  the hope that it may be useful for others.
      8 *
      9 *  Author: Fernando Fuganti <fuganti@conectiva.com.br>
     10 *
     11 *  Based on sbc60xxwdt.c by Jakob Oestergaard
     12 *
     13 *  We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the
     14 *  following periods:
     15 *      wd#1 - 2 seconds;
     16 *      wd#2 - 7.2 ms;
     17 *  After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
     18 *  a system RESET and it starts wd#2 that unconditionally will RESET
     19 *  the system when the counter reaches zero.
     20 *
     21 *  14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
     22 *      Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
     23 */
     24
     25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     26
     27#include <linux/module.h>
     28#include <linux/moduleparam.h>
     29#include <linux/types.h>
     30#include <linux/timer.h>
     31#include <linux/jiffies.h>
     32#include <linux/miscdevice.h>
     33#include <linux/watchdog.h>
     34#include <linux/fs.h>
     35#include <linux/ioport.h>
     36#include <linux/notifier.h>
     37#include <linux/reboot.h>
     38#include <linux/init.h>
     39#include <linux/io.h>
     40#include <linux/uaccess.h>
     41
     42
     43/* ports */
     44#define ZF_IOBASE	0x218
     45#define INDEX		0x218
     46#define DATA_B		0x219
     47#define DATA_W		0x21A
     48#define DATA_D		0x21A
     49
     50/* indexes */			/* size */
     51#define ZFL_VERSION	0x02	/* 16   */
     52#define CONTROL		0x10	/* 16   */
     53#define STATUS		0x12	/* 8    */
     54#define COUNTER_1	0x0C	/* 16   */
     55#define COUNTER_2	0x0E	/* 8    */
     56#define PULSE_LEN	0x0F	/* 8    */
     57
     58/* controls */
     59#define ENABLE_WD1	0x0001
     60#define ENABLE_WD2	0x0002
     61#define RESET_WD1	0x0010
     62#define RESET_WD2	0x0020
     63#define GEN_SCI		0x0100
     64#define GEN_NMI		0x0200
     65#define GEN_SMI		0x0400
     66#define GEN_RESET	0x0800
     67
     68
     69/* utilities */
     70
     71#define WD1	0
     72#define WD2	1
     73
     74#define zf_writew(port, data)  { outb(port, INDEX); outw(data, DATA_W); }
     75#define zf_writeb(port, data)  { outb(port, INDEX); outb(data, DATA_B); }
     76#define zf_get_ZFL_version()   zf_readw(ZFL_VERSION)
     77
     78
     79static unsigned short zf_readw(unsigned char port)
     80{
     81	outb(port, INDEX);
     82	return inw(DATA_W);
     83}
     84
     85
     86MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
     87MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
     88MODULE_LICENSE("GPL");
     89
     90static bool nowayout = WATCHDOG_NOWAYOUT;
     91module_param(nowayout, bool, 0);
     92MODULE_PARM_DESC(nowayout,
     93		"Watchdog cannot be stopped once started (default="
     94				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     95
     96#define PFX "machzwd"
     97
     98static const struct watchdog_info zf_info = {
     99	.options		= WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    100	.firmware_version	= 1,
    101	.identity		= "ZF-Logic watchdog",
    102};
    103
    104
    105/*
    106 * action refers to action taken when watchdog resets
    107 * 0 = GEN_RESET
    108 * 1 = GEN_SMI
    109 * 2 = GEN_NMI
    110 * 3 = GEN_SCI
    111 * defaults to GEN_RESET (0)
    112 */
    113static int action;
    114module_param(action, int, 0);
    115MODULE_PARM_DESC(action, "after watchdog resets, generate: "
    116				"0 = RESET(*)  1 = SMI  2 = NMI  3 = SCI");
    117
    118static void zf_ping(struct timer_list *unused);
    119
    120static int zf_action = GEN_RESET;
    121static unsigned long zf_is_open;
    122static char zf_expect_close;
    123static DEFINE_SPINLOCK(zf_port_lock);
    124static DEFINE_TIMER(zf_timer, zf_ping);
    125static unsigned long next_heartbeat;
    126
    127
    128/* timeout for user land heart beat (10 seconds) */
    129#define ZF_USER_TIMEO (HZ*10)
    130
    131/* timeout for hardware watchdog (~500ms) */
    132#define ZF_HW_TIMEO (HZ/2)
    133
    134/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */
    135#define ZF_CTIMEOUT 0xffff
    136
    137#ifndef ZF_DEBUG
    138#define dprintk(format, args...)
    139#else
    140#define dprintk(format, args...)					\
    141	pr_debug(":%s:%d: " format, __func__, __LINE__ , ## args)
    142#endif
    143
    144
    145static inline void zf_set_status(unsigned char new)
    146{
    147	zf_writeb(STATUS, new);
    148}
    149
    150
    151/* CONTROL register functions */
    152
    153static inline unsigned short zf_get_control(void)
    154{
    155	return zf_readw(CONTROL);
    156}
    157
    158static inline void zf_set_control(unsigned short new)
    159{
    160	zf_writew(CONTROL, new);
    161}
    162
    163
    164/* WD#? counter functions */
    165/*
    166 *	Just set counter value
    167 */
    168
    169static inline void zf_set_timer(unsigned short new, unsigned char n)
    170{
    171	switch (n) {
    172	case WD1:
    173		zf_writew(COUNTER_1, new);
    174		fallthrough;
    175	case WD2:
    176		zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
    177		fallthrough;
    178	default:
    179		return;
    180	}
    181}
    182
    183/*
    184 * stop hardware timer
    185 */
    186static void zf_timer_off(void)
    187{
    188	unsigned int ctrl_reg = 0;
    189	unsigned long flags;
    190
    191	/* stop internal ping */
    192	del_timer_sync(&zf_timer);
    193
    194	spin_lock_irqsave(&zf_port_lock, flags);
    195	/* stop watchdog timer */
    196	ctrl_reg = zf_get_control();
    197	ctrl_reg |= (ENABLE_WD1|ENABLE_WD2);	/* disable wd1 and wd2 */
    198	ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
    199	zf_set_control(ctrl_reg);
    200	spin_unlock_irqrestore(&zf_port_lock, flags);
    201
    202	pr_info("Watchdog timer is now disabled\n");
    203}
    204
    205
    206/*
    207 * start hardware timer
    208 */
    209static void zf_timer_on(void)
    210{
    211	unsigned int ctrl_reg = 0;
    212	unsigned long flags;
    213
    214	spin_lock_irqsave(&zf_port_lock, flags);
    215
    216	zf_writeb(PULSE_LEN, 0xff);
    217
    218	zf_set_timer(ZF_CTIMEOUT, WD1);
    219
    220	/* user land ping */
    221	next_heartbeat = jiffies + ZF_USER_TIMEO;
    222
    223	/* start the timer for internal ping */
    224	mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
    225
    226	/* start watchdog timer */
    227	ctrl_reg = zf_get_control();
    228	ctrl_reg |= (ENABLE_WD1|zf_action);
    229	zf_set_control(ctrl_reg);
    230	spin_unlock_irqrestore(&zf_port_lock, flags);
    231
    232	pr_info("Watchdog timer is now enabled\n");
    233}
    234
    235
    236static void zf_ping(struct timer_list *unused)
    237{
    238	unsigned int ctrl_reg = 0;
    239	unsigned long flags;
    240
    241	zf_writeb(COUNTER_2, 0xff);
    242
    243	if (time_before(jiffies, next_heartbeat)) {
    244		dprintk("time_before: %ld\n", next_heartbeat - jiffies);
    245		/*
    246		 * reset event is activated by transition from 0 to 1 on
    247		 * RESET_WD1 bit and we assume that it is already zero...
    248		 */
    249
    250		spin_lock_irqsave(&zf_port_lock, flags);
    251		ctrl_reg = zf_get_control();
    252		ctrl_reg |= RESET_WD1;
    253		zf_set_control(ctrl_reg);
    254
    255		/* ...and nothing changes until here */
    256		ctrl_reg &= ~(RESET_WD1);
    257		zf_set_control(ctrl_reg);
    258		spin_unlock_irqrestore(&zf_port_lock, flags);
    259
    260		mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
    261	} else
    262		pr_crit("I will reset your machine\n");
    263}
    264
    265static ssize_t zf_write(struct file *file, const char __user *buf, size_t count,
    266								loff_t *ppos)
    267{
    268	/* See if we got the magic character */
    269	if (count) {
    270		/*
    271		 * no need to check for close confirmation
    272		 * no way to disable watchdog ;)
    273		 */
    274		if (!nowayout) {
    275			size_t ofs;
    276			/*
    277			 * note: just in case someone wrote the magic character
    278			 * five months ago...
    279			 */
    280			zf_expect_close = 0;
    281
    282			/* now scan */
    283			for (ofs = 0; ofs != count; ofs++) {
    284				char c;
    285				if (get_user(c, buf + ofs))
    286					return -EFAULT;
    287				if (c == 'V') {
    288					zf_expect_close = 42;
    289					dprintk("zf_expect_close = 42\n");
    290				}
    291			}
    292		}
    293
    294		/*
    295		 * Well, anyhow someone wrote to us,
    296		 * we should return that favour
    297		 */
    298		next_heartbeat = jiffies + ZF_USER_TIMEO;
    299		dprintk("user ping at %ld\n", jiffies);
    300	}
    301	return count;
    302}
    303
    304static long zf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    305{
    306	void __user *argp = (void __user *)arg;
    307	int __user *p = argp;
    308	switch (cmd) {
    309	case WDIOC_GETSUPPORT:
    310		if (copy_to_user(argp, &zf_info, sizeof(zf_info)))
    311			return -EFAULT;
    312		break;
    313	case WDIOC_GETSTATUS:
    314	case WDIOC_GETBOOTSTATUS:
    315		return put_user(0, p);
    316	case WDIOC_KEEPALIVE:
    317		zf_ping(NULL);
    318		break;
    319	default:
    320		return -ENOTTY;
    321	}
    322	return 0;
    323}
    324
    325static int zf_open(struct inode *inode, struct file *file)
    326{
    327	if (test_and_set_bit(0, &zf_is_open))
    328		return -EBUSY;
    329	if (nowayout)
    330		__module_get(THIS_MODULE);
    331	zf_timer_on();
    332	return stream_open(inode, file);
    333}
    334
    335static int zf_close(struct inode *inode, struct file *file)
    336{
    337	if (zf_expect_close == 42)
    338		zf_timer_off();
    339	else {
    340		del_timer(&zf_timer);
    341		pr_err("device file closed unexpectedly. Will not stop the WDT!\n");
    342	}
    343	clear_bit(0, &zf_is_open);
    344	zf_expect_close = 0;
    345	return 0;
    346}
    347
    348/*
    349 * Notifier for system down
    350 */
    351
    352static int zf_notify_sys(struct notifier_block *this, unsigned long code,
    353								void *unused)
    354{
    355	if (code == SYS_DOWN || code == SYS_HALT)
    356		zf_timer_off();
    357	return NOTIFY_DONE;
    358}
    359
    360static const struct file_operations zf_fops = {
    361	.owner		= THIS_MODULE,
    362	.llseek		= no_llseek,
    363	.write		= zf_write,
    364	.unlocked_ioctl = zf_ioctl,
    365	.compat_ioctl	= compat_ptr_ioctl,
    366	.open		= zf_open,
    367	.release	= zf_close,
    368};
    369
    370static struct miscdevice zf_miscdev = {
    371	.minor = WATCHDOG_MINOR,
    372	.name = "watchdog",
    373	.fops = &zf_fops,
    374};
    375
    376
    377/*
    378 * The device needs to learn about soft shutdowns in order to
    379 * turn the timebomb registers off.
    380 */
    381static struct notifier_block zf_notifier = {
    382	.notifier_call = zf_notify_sys,
    383};
    384
    385static void __init zf_show_action(int act)
    386{
    387	static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" };
    388
    389	pr_info("Watchdog using action = %s\n", str[act]);
    390}
    391
    392static int __init zf_init(void)
    393{
    394	int ret;
    395
    396	pr_info("MachZ ZF-Logic Watchdog driver initializing\n");
    397
    398	ret = zf_get_ZFL_version();
    399	if (!ret || ret == 0xffff) {
    400		pr_warn("no ZF-Logic found\n");
    401		return -ENODEV;
    402	}
    403
    404	if (action <= 3 && action >= 0)
    405		zf_action = zf_action >> action;
    406	else
    407		action = 0;
    408
    409	zf_show_action(action);
    410
    411	if (!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")) {
    412		pr_err("cannot reserve I/O ports at %d\n", ZF_IOBASE);
    413		ret = -EBUSY;
    414		goto no_region;
    415	}
    416
    417	ret = register_reboot_notifier(&zf_notifier);
    418	if (ret) {
    419		pr_err("can't register reboot notifier (err=%d)\n", ret);
    420		goto no_reboot;
    421	}
    422
    423	ret = misc_register(&zf_miscdev);
    424	if (ret) {
    425		pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR);
    426		goto no_misc;
    427	}
    428
    429	zf_set_status(0);
    430	zf_set_control(0);
    431
    432	return 0;
    433
    434no_misc:
    435	unregister_reboot_notifier(&zf_notifier);
    436no_reboot:
    437	release_region(ZF_IOBASE, 3);
    438no_region:
    439	return ret;
    440}
    441
    442
    443static void __exit zf_exit(void)
    444{
    445	zf_timer_off();
    446
    447	misc_deregister(&zf_miscdev);
    448	unregister_reboot_notifier(&zf_notifier);
    449	release_region(ZF_IOBASE, 3);
    450}
    451
    452module_init(zf_init);
    453module_exit(zf_exit);