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

cpu5wdt.c (6133B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * sma cpu5 watchdog driver
      4 *
      5 * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/module.h>
     11#include <linux/moduleparam.h>
     12#include <linux/types.h>
     13#include <linux/errno.h>
     14#include <linux/miscdevice.h>
     15#include <linux/fs.h>
     16#include <linux/ioport.h>
     17#include <linux/timer.h>
     18#include <linux/completion.h>
     19#include <linux/jiffies.h>
     20#include <linux/io.h>
     21#include <linux/uaccess.h>
     22#include <linux/watchdog.h>
     23
     24/* adjustable parameters */
     25
     26static int verbose;
     27static int port = 0x91;
     28static int ticks = 10000;
     29static DEFINE_SPINLOCK(cpu5wdt_lock);
     30
     31#define PFX			"cpu5wdt: "
     32
     33#define CPU5WDT_EXTENT          0x0A
     34
     35#define CPU5WDT_STATUS_REG      0x00
     36#define CPU5WDT_TIME_A_REG      0x02
     37#define CPU5WDT_TIME_B_REG      0x03
     38#define CPU5WDT_MODE_REG        0x04
     39#define CPU5WDT_TRIGGER_REG     0x07
     40#define CPU5WDT_ENABLE_REG      0x08
     41#define CPU5WDT_RESET_REG       0x09
     42
     43#define CPU5WDT_INTERVAL	(HZ/10+1)
     44
     45/* some device data */
     46
     47static struct {
     48	struct completion stop;
     49	int running;
     50	struct timer_list timer;
     51	int queue;
     52	int default_ticks;
     53	unsigned long inuse;
     54} cpu5wdt_device;
     55
     56/* generic helper functions */
     57
     58static void cpu5wdt_trigger(struct timer_list *unused)
     59{
     60	if (verbose > 2)
     61		pr_debug("trigger at %i ticks\n", ticks);
     62
     63	if (cpu5wdt_device.running)
     64		ticks--;
     65
     66	spin_lock(&cpu5wdt_lock);
     67	/* keep watchdog alive */
     68	outb(1, port + CPU5WDT_TRIGGER_REG);
     69
     70	/* requeue?? */
     71	if (cpu5wdt_device.queue && ticks)
     72		mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
     73	else {
     74		/* ticks doesn't matter anyway */
     75		complete(&cpu5wdt_device.stop);
     76	}
     77	spin_unlock(&cpu5wdt_lock);
     78
     79}
     80
     81static void cpu5wdt_reset(void)
     82{
     83	ticks = cpu5wdt_device.default_ticks;
     84
     85	if (verbose)
     86		pr_debug("reset (%i ticks)\n", (int) ticks);
     87
     88}
     89
     90static void cpu5wdt_start(void)
     91{
     92	unsigned long flags;
     93
     94	spin_lock_irqsave(&cpu5wdt_lock, flags);
     95	if (!cpu5wdt_device.queue) {
     96		cpu5wdt_device.queue = 1;
     97		outb(0, port + CPU5WDT_TIME_A_REG);
     98		outb(0, port + CPU5WDT_TIME_B_REG);
     99		outb(1, port + CPU5WDT_MODE_REG);
    100		outb(0, port + CPU5WDT_RESET_REG);
    101		outb(0, port + CPU5WDT_ENABLE_REG);
    102		mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
    103	}
    104	/* if process dies, counter is not decremented */
    105	cpu5wdt_device.running++;
    106	spin_unlock_irqrestore(&cpu5wdt_lock, flags);
    107}
    108
    109static int cpu5wdt_stop(void)
    110{
    111	unsigned long flags;
    112
    113	spin_lock_irqsave(&cpu5wdt_lock, flags);
    114	if (cpu5wdt_device.running)
    115		cpu5wdt_device.running = 0;
    116	ticks = cpu5wdt_device.default_ticks;
    117	spin_unlock_irqrestore(&cpu5wdt_lock, flags);
    118	if (verbose)
    119		pr_crit("stop not possible\n");
    120	return -EIO;
    121}
    122
    123/* filesystem operations */
    124
    125static int cpu5wdt_open(struct inode *inode, struct file *file)
    126{
    127	if (test_and_set_bit(0, &cpu5wdt_device.inuse))
    128		return -EBUSY;
    129	return stream_open(inode, file);
    130}
    131
    132static int cpu5wdt_release(struct inode *inode, struct file *file)
    133{
    134	clear_bit(0, &cpu5wdt_device.inuse);
    135	return 0;
    136}
    137
    138static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
    139						unsigned long arg)
    140{
    141	void __user *argp = (void __user *)arg;
    142	int __user *p = argp;
    143	unsigned int value;
    144	static const struct watchdog_info ident = {
    145		.options = WDIOF_CARDRESET,
    146		.identity = "CPU5 WDT",
    147	};
    148
    149	switch (cmd) {
    150	case WDIOC_GETSUPPORT:
    151		if (copy_to_user(argp, &ident, sizeof(ident)))
    152			return -EFAULT;
    153		break;
    154	case WDIOC_GETSTATUS:
    155		value = inb(port + CPU5WDT_STATUS_REG);
    156		value = (value >> 2) & 1;
    157		return put_user(value, p);
    158	case WDIOC_GETBOOTSTATUS:
    159		return put_user(0, p);
    160	case WDIOC_SETOPTIONS:
    161		if (get_user(value, p))
    162			return -EFAULT;
    163		if (value & WDIOS_ENABLECARD)
    164			cpu5wdt_start();
    165		if (value & WDIOS_DISABLECARD)
    166			cpu5wdt_stop();
    167		break;
    168	case WDIOC_KEEPALIVE:
    169		cpu5wdt_reset();
    170		break;
    171	default:
    172		return -ENOTTY;
    173	}
    174	return 0;
    175}
    176
    177static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
    178						size_t count, loff_t *ppos)
    179{
    180	if (!count)
    181		return -EIO;
    182	cpu5wdt_reset();
    183	return count;
    184}
    185
    186static const struct file_operations cpu5wdt_fops = {
    187	.owner		= THIS_MODULE,
    188	.llseek		= no_llseek,
    189	.unlocked_ioctl	= cpu5wdt_ioctl,
    190	.compat_ioctl	= compat_ptr_ioctl,
    191	.open		= cpu5wdt_open,
    192	.write		= cpu5wdt_write,
    193	.release	= cpu5wdt_release,
    194};
    195
    196static struct miscdevice cpu5wdt_misc = {
    197	.minor	= WATCHDOG_MINOR,
    198	.name	= "watchdog",
    199	.fops	= &cpu5wdt_fops,
    200};
    201
    202/* init/exit function */
    203
    204static int cpu5wdt_init(void)
    205{
    206	unsigned int val;
    207	int err;
    208
    209	if (verbose)
    210		pr_debug("port=0x%x, verbose=%i\n", port, verbose);
    211
    212	init_completion(&cpu5wdt_device.stop);
    213	cpu5wdt_device.queue = 0;
    214	timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
    215	cpu5wdt_device.default_ticks = ticks;
    216
    217	if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
    218		pr_err("request_region failed\n");
    219		err = -EBUSY;
    220		goto no_port;
    221	}
    222
    223	/* watchdog reboot? */
    224	val = inb(port + CPU5WDT_STATUS_REG);
    225	val = (val >> 2) & 1;
    226	if (!val)
    227		pr_info("sorry, was my fault\n");
    228
    229	err = misc_register(&cpu5wdt_misc);
    230	if (err < 0) {
    231		pr_err("misc_register failed\n");
    232		goto no_misc;
    233	}
    234
    235
    236	pr_info("init success\n");
    237	return 0;
    238
    239no_misc:
    240	release_region(port, CPU5WDT_EXTENT);
    241no_port:
    242	return err;
    243}
    244
    245static int cpu5wdt_init_module(void)
    246{
    247	return cpu5wdt_init();
    248}
    249
    250static void cpu5wdt_exit(void)
    251{
    252	if (cpu5wdt_device.queue) {
    253		cpu5wdt_device.queue = 0;
    254		wait_for_completion(&cpu5wdt_device.stop);
    255		del_timer(&cpu5wdt_device.timer);
    256	}
    257
    258	misc_deregister(&cpu5wdt_misc);
    259
    260	release_region(port, CPU5WDT_EXTENT);
    261
    262}
    263
    264static void cpu5wdt_exit_module(void)
    265{
    266	cpu5wdt_exit();
    267}
    268
    269/* module entry points */
    270
    271module_init(cpu5wdt_init_module);
    272module_exit(cpu5wdt_exit_module);
    273
    274MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
    275MODULE_DESCRIPTION("sma cpu5 watchdog driver");
    276MODULE_LICENSE("GPL");
    277
    278module_param_hw(port, int, ioport, 0);
    279MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
    280
    281module_param(verbose, int, 0);
    282MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
    283
    284module_param(ticks, int, 0);
    285MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");