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

wdt.c (15656B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 *	Industrial Computer Source WDT501 driver
      4 *
      5 *	(c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
      6 *						All Rights Reserved.
      7 *
      8 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
      9 *	warranty for any of this software. This material is provided
     10 *	"AS-IS" and at no charge.
     11 *
     12 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
     13 *
     14 *	Release 0.10.
     15 *
     16 *	Fixes
     17 *		Dave Gregorich	:	Modularisation and minor bugs
     18 *		Alan Cox	:	Added the watchdog ioctl() stuff
     19 *		Alan Cox	:	Fixed the reboot problem (as noted by
     20 *					Matt Crocker).
     21 *		Alan Cox	:	Added wdt= boot option
     22 *		Alan Cox	:	Cleaned up copy/user stuff
     23 *		Tim Hockin	:	Added insmod parameters, comment
     24 *					cleanup, parameterized timeout
     25 *		Tigran Aivazian	:	Restructured wdt_init() to handle
     26 *					failures
     27 *		Joel Becker	:	Added WDIOC_GET/SETTIMEOUT
     28 *		Matt Domsch	:	Added nowayout module option
     29 */
     30
     31#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     32
     33#include <linux/interrupt.h>
     34#include <linux/module.h>
     35#include <linux/moduleparam.h>
     36#include <linux/types.h>
     37#include <linux/miscdevice.h>
     38#include <linux/watchdog.h>
     39#include <linux/fs.h>
     40#include <linux/ioport.h>
     41#include <linux/notifier.h>
     42#include <linux/reboot.h>
     43#include <linux/init.h>
     44#include <linux/io.h>
     45#include <linux/uaccess.h>
     46
     47#include "wd501p.h"
     48
     49static unsigned long wdt_is_open;
     50static char expect_close;
     51
     52/*
     53 *	Module parameters
     54 */
     55
     56#define WD_TIMO 60			/* Default heartbeat = 60 seconds */
     57
     58static int heartbeat = WD_TIMO;
     59static int wd_heartbeat;
     60module_param(heartbeat, int, 0);
     61MODULE_PARM_DESC(heartbeat,
     62	"Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
     63				__MODULE_STRING(WD_TIMO) ")");
     64
     65static bool nowayout = WATCHDOG_NOWAYOUT;
     66module_param(nowayout, bool, 0);
     67MODULE_PARM_DESC(nowayout,
     68	"Watchdog cannot be stopped once started (default="
     69				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     70
     71/* You must set these - there is no sane way to probe for this board. */
     72static int io = 0x240;
     73static int irq = 11;
     74
     75static DEFINE_SPINLOCK(wdt_lock);
     76
     77module_param_hw(io, int, ioport, 0);
     78MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
     79module_param_hw(irq, int, irq, 0);
     80MODULE_PARM_DESC(irq, "WDT irq (default=11)");
     81
     82/* Support for the Fan Tachometer on the WDT501-P */
     83static int tachometer;
     84module_param(tachometer, int, 0);
     85MODULE_PARM_DESC(tachometer,
     86		"WDT501-P Fan Tachometer support (0=disable, default=0)");
     87
     88static int type = 500;
     89module_param(type, int, 0);
     90MODULE_PARM_DESC(type,
     91		"WDT501-P Card type (500 or 501, default=500)");
     92
     93/*
     94 *	Programming support
     95 */
     96
     97static void wdt_ctr_mode(int ctr, int mode)
     98{
     99	ctr <<= 6;
    100	ctr |= 0x30;
    101	ctr |= (mode << 1);
    102	outb_p(ctr, WDT_CR);
    103}
    104
    105static void wdt_ctr_load(int ctr, int val)
    106{
    107	outb_p(val&0xFF, WDT_COUNT0+ctr);
    108	outb_p(val>>8, WDT_COUNT0+ctr);
    109}
    110
    111/**
    112 *	wdt_start:
    113 *
    114 *	Start the watchdog driver.
    115 */
    116
    117static int wdt_start(void)
    118{
    119	unsigned long flags;
    120	spin_lock_irqsave(&wdt_lock, flags);
    121	inb_p(WDT_DC);			/* Disable watchdog */
    122	wdt_ctr_mode(0, 3);		/* Program CTR0 for Mode 3:
    123						Square Wave Generator */
    124	wdt_ctr_mode(1, 2);		/* Program CTR1 for Mode 2:
    125						Rate Generator */
    126	wdt_ctr_mode(2, 0);		/* Program CTR2 for Mode 0:
    127						Pulse on Terminal Count */
    128	wdt_ctr_load(0, 8948);		/* Count at 100Hz */
    129	wdt_ctr_load(1, wd_heartbeat);	/* Heartbeat */
    130	wdt_ctr_load(2, 65535);		/* Length of reset pulse */
    131	outb_p(0, WDT_DC);		/* Enable watchdog */
    132	spin_unlock_irqrestore(&wdt_lock, flags);
    133	return 0;
    134}
    135
    136/**
    137 *	wdt_stop:
    138 *
    139 *	Stop the watchdog driver.
    140 */
    141
    142static int wdt_stop(void)
    143{
    144	unsigned long flags;
    145	spin_lock_irqsave(&wdt_lock, flags);
    146	/* Turn the card off */
    147	inb_p(WDT_DC);			/* Disable watchdog */
    148	wdt_ctr_load(2, 0);		/* 0 length reset pulses now */
    149	spin_unlock_irqrestore(&wdt_lock, flags);
    150	return 0;
    151}
    152
    153/**
    154 *	wdt_ping:
    155 *
    156 *	Reload counter one with the watchdog heartbeat. We don't bother
    157 *	reloading the cascade counter.
    158 */
    159
    160static void wdt_ping(void)
    161{
    162	unsigned long flags;
    163	spin_lock_irqsave(&wdt_lock, flags);
    164	/* Write a watchdog value */
    165	inb_p(WDT_DC);			/* Disable watchdog */
    166	wdt_ctr_mode(1, 2);		/* Re-Program CTR1 for Mode 2:
    167							Rate Generator */
    168	wdt_ctr_load(1, wd_heartbeat);	/* Heartbeat */
    169	outb_p(0, WDT_DC);		/* Enable watchdog */
    170	spin_unlock_irqrestore(&wdt_lock, flags);
    171}
    172
    173/**
    174 *	wdt_set_heartbeat:
    175 *	@t:		the new heartbeat value that needs to be set.
    176 *
    177 *	Set a new heartbeat value for the watchdog device. If the heartbeat
    178 *	value is incorrect we keep the old value and return -EINVAL. If
    179 *	successful we return 0.
    180 */
    181
    182static int wdt_set_heartbeat(int t)
    183{
    184	if (t < 1 || t > 65535)
    185		return -EINVAL;
    186
    187	heartbeat = t;
    188	wd_heartbeat = t * 100;
    189	return 0;
    190}
    191
    192/**
    193 *	wdt_get_status:
    194 *
    195 *	Extract the status information from a WDT watchdog device. There are
    196 *	several board variants so we have to know which bits are valid. Some
    197 *	bits default to one and some to zero in order to be maximally painful.
    198 *
    199 *	we then map the bits onto the status ioctl flags.
    200 */
    201
    202static int wdt_get_status(void)
    203{
    204	unsigned char new_status;
    205	int status = 0;
    206	unsigned long flags;
    207
    208	spin_lock_irqsave(&wdt_lock, flags);
    209	new_status = inb_p(WDT_SR);
    210	spin_unlock_irqrestore(&wdt_lock, flags);
    211
    212	if (new_status & WDC_SR_ISOI0)
    213		status |= WDIOF_EXTERN1;
    214	if (new_status & WDC_SR_ISII1)
    215		status |= WDIOF_EXTERN2;
    216	if (type == 501) {
    217		if (!(new_status & WDC_SR_TGOOD))
    218			status |= WDIOF_OVERHEAT;
    219		if (!(new_status & WDC_SR_PSUOVER))
    220			status |= WDIOF_POWEROVER;
    221		if (!(new_status & WDC_SR_PSUUNDR))
    222			status |= WDIOF_POWERUNDER;
    223		if (tachometer) {
    224			if (!(new_status & WDC_SR_FANGOOD))
    225				status |= WDIOF_FANFAULT;
    226		}
    227	}
    228	return status;
    229}
    230
    231/**
    232 *	wdt_get_temperature:
    233 *
    234 *	Reports the temperature in degrees Fahrenheit. The API is in
    235 *	farenheit. It was designed by an imperial measurement luddite.
    236 */
    237
    238static int wdt_get_temperature(void)
    239{
    240	unsigned short c;
    241	unsigned long flags;
    242
    243	spin_lock_irqsave(&wdt_lock, flags);
    244	c = inb_p(WDT_RT);
    245	spin_unlock_irqrestore(&wdt_lock, flags);
    246	return (c * 11 / 15) + 7;
    247}
    248
    249static void wdt_decode_501(int status)
    250{
    251	if (!(status & WDC_SR_TGOOD))
    252		pr_crit("Overheat alarm (%d)\n", inb_p(WDT_RT));
    253	if (!(status & WDC_SR_PSUOVER))
    254		pr_crit("PSU over voltage\n");
    255	if (!(status & WDC_SR_PSUUNDR))
    256		pr_crit("PSU under voltage\n");
    257}
    258
    259/**
    260 *	wdt_interrupt:
    261 *	@irq:		Interrupt number
    262 *	@dev_id:	Unused as we don't allow multiple devices.
    263 *
    264 *	Handle an interrupt from the board. These are raised when the status
    265 *	map changes in what the board considers an interesting way. That means
    266 *	a failure condition occurring.
    267 */
    268
    269static irqreturn_t wdt_interrupt(int irq, void *dev_id)
    270{
    271	/*
    272	 *	Read the status register see what is up and
    273	 *	then printk it.
    274	 */
    275	unsigned char status;
    276
    277	spin_lock(&wdt_lock);
    278	status = inb_p(WDT_SR);
    279
    280	pr_crit("WDT status %d\n", status);
    281
    282	if (type == 501) {
    283		wdt_decode_501(status);
    284		if (tachometer) {
    285			if (!(status & WDC_SR_FANGOOD))
    286				pr_crit("Possible fan fault\n");
    287		}
    288	}
    289	if (!(status & WDC_SR_WCCR)) {
    290#ifdef SOFTWARE_REBOOT
    291#ifdef ONLY_TESTING
    292		pr_crit("Would Reboot\n");
    293#else
    294		pr_crit("Initiating system reboot\n");
    295		emergency_restart();
    296#endif
    297#else
    298		pr_crit("Reset in 5ms\n");
    299#endif
    300	}
    301	spin_unlock(&wdt_lock);
    302	return IRQ_HANDLED;
    303}
    304
    305
    306/**
    307 *	wdt_write:
    308 *	@file: file handle to the watchdog
    309 *	@buf: buffer to write (unused as data does not matter here
    310 *	@count: count of bytes
    311 *	@ppos: pointer to the position to write. No seeks allowed
    312 *
    313 *	A write to a watchdog device is defined as a keepalive signal. Any
    314 *	write of data will do, as we we don't define content meaning.
    315 */
    316
    317static ssize_t wdt_write(struct file *file, const char __user *buf,
    318						size_t count, loff_t *ppos)
    319{
    320	if (count) {
    321		if (!nowayout) {
    322			size_t i;
    323
    324			/* In case it was set long ago */
    325			expect_close = 0;
    326
    327			for (i = 0; i != count; i++) {
    328				char c;
    329				if (get_user(c, buf + i))
    330					return -EFAULT;
    331				if (c == 'V')
    332					expect_close = 42;
    333			}
    334		}
    335		wdt_ping();
    336	}
    337	return count;
    338}
    339
    340/**
    341 *	wdt_ioctl:
    342 *	@file: file handle to the device
    343 *	@cmd: watchdog command
    344 *	@arg: argument pointer
    345 *
    346 *	The watchdog API defines a common set of functions for all watchdogs
    347 *	according to their available features. We only actually usefully support
    348 *	querying capabilities and current status.
    349 */
    350
    351static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    352{
    353	void __user *argp = (void __user *)arg;
    354	int __user *p = argp;
    355	int new_heartbeat;
    356	int status;
    357
    358	struct watchdog_info ident = {
    359		.options =		WDIOF_SETTIMEOUT|
    360					WDIOF_MAGICCLOSE|
    361					WDIOF_KEEPALIVEPING,
    362		.firmware_version =	1,
    363		.identity =		"WDT500/501",
    364	};
    365
    366	/* Add options according to the card we have */
    367	ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
    368	if (type == 501) {
    369		ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
    370							WDIOF_POWEROVER);
    371		if (tachometer)
    372			ident.options |= WDIOF_FANFAULT;
    373	}
    374
    375	switch (cmd) {
    376	case WDIOC_GETSUPPORT:
    377		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
    378	case WDIOC_GETSTATUS:
    379		status = wdt_get_status();
    380		return put_user(status, p);
    381	case WDIOC_GETBOOTSTATUS:
    382		return put_user(0, p);
    383	case WDIOC_KEEPALIVE:
    384		wdt_ping();
    385		return 0;
    386	case WDIOC_SETTIMEOUT:
    387		if (get_user(new_heartbeat, p))
    388			return -EFAULT;
    389		if (wdt_set_heartbeat(new_heartbeat))
    390			return -EINVAL;
    391		wdt_ping();
    392		fallthrough;
    393	case WDIOC_GETTIMEOUT:
    394		return put_user(heartbeat, p);
    395	default:
    396		return -ENOTTY;
    397	}
    398}
    399
    400/**
    401 *	wdt_open:
    402 *	@inode: inode of device
    403 *	@file: file handle to device
    404 *
    405 *	The watchdog device has been opened. The watchdog device is single
    406 *	open and on opening we load the counters. Counter zero is a 100Hz
    407 *	cascade, into counter 1 which downcounts to reboot. When the counter
    408 *	triggers counter 2 downcounts the length of the reset pulse which
    409 *	set set to be as long as possible.
    410 */
    411
    412static int wdt_open(struct inode *inode, struct file *file)
    413{
    414	if (test_and_set_bit(0, &wdt_is_open))
    415		return -EBUSY;
    416	/*
    417	 *	Activate
    418	 */
    419	wdt_start();
    420	return stream_open(inode, file);
    421}
    422
    423/**
    424 *	wdt_release:
    425 *	@inode: inode to board
    426 *	@file: file handle to board
    427 *
    428 *	The watchdog has a configurable API. There is a religious dispute
    429 *	between people who want their watchdog to be able to shut down and
    430 *	those who want to be sure if the watchdog manager dies the machine
    431 *	reboots. In the former case we disable the counters, in the latter
    432 *	case you have to open it again very soon.
    433 */
    434
    435static int wdt_release(struct inode *inode, struct file *file)
    436{
    437	if (expect_close == 42) {
    438		wdt_stop();
    439		clear_bit(0, &wdt_is_open);
    440	} else {
    441		pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");
    442		wdt_ping();
    443	}
    444	expect_close = 0;
    445	return 0;
    446}
    447
    448/**
    449 *	wdt_temp_read:
    450 *	@file: file handle to the watchdog board
    451 *	@buf: buffer to write 1 byte into
    452 *	@count: length of buffer
    453 *	@ptr: offset (no seek allowed)
    454 *
    455 *	Temp_read reports the temperature in degrees Fahrenheit. The API is in
    456 *	farenheit. It was designed by an imperial measurement luddite.
    457 */
    458
    459static ssize_t wdt_temp_read(struct file *file, char __user *buf,
    460						size_t count, loff_t *ptr)
    461{
    462	int temperature = wdt_get_temperature();
    463
    464	if (copy_to_user(buf, &temperature, 1))
    465		return -EFAULT;
    466
    467	return 1;
    468}
    469
    470/**
    471 *	wdt_temp_open:
    472 *	@inode: inode of device
    473 *	@file: file handle to device
    474 *
    475 *	The temperature device has been opened.
    476 */
    477
    478static int wdt_temp_open(struct inode *inode, struct file *file)
    479{
    480	return stream_open(inode, file);
    481}
    482
    483/**
    484 *	wdt_temp_release:
    485 *	@inode: inode to board
    486 *	@file: file handle to board
    487 *
    488 *	The temperature device has been closed.
    489 */
    490
    491static int wdt_temp_release(struct inode *inode, struct file *file)
    492{
    493	return 0;
    494}
    495
    496/**
    497 *	wdt_notify_sys:
    498 *	@this: our notifier block
    499 *	@code: the event being reported
    500 *	@unused: unused
    501 *
    502 *	Our notifier is called on system shutdowns. We want to turn the card
    503 *	off at reboot otherwise the machine will reboot again during memory
    504 *	test or worse yet during the following fsck. This would suck, in fact
    505 *	trust me - if it happens it does suck.
    506 */
    507
    508static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
    509	void *unused)
    510{
    511	if (code == SYS_DOWN || code == SYS_HALT)
    512		wdt_stop();
    513	return NOTIFY_DONE;
    514}
    515
    516/*
    517 *	Kernel Interfaces
    518 */
    519
    520
    521static const struct file_operations wdt_fops = {
    522	.owner		= THIS_MODULE,
    523	.llseek		= no_llseek,
    524	.write		= wdt_write,
    525	.unlocked_ioctl	= wdt_ioctl,
    526	.compat_ioctl	= compat_ptr_ioctl,
    527	.open		= wdt_open,
    528	.release	= wdt_release,
    529};
    530
    531static struct miscdevice wdt_miscdev = {
    532	.minor	= WATCHDOG_MINOR,
    533	.name	= "watchdog",
    534	.fops	= &wdt_fops,
    535};
    536
    537static const struct file_operations wdt_temp_fops = {
    538	.owner		= THIS_MODULE,
    539	.llseek		= no_llseek,
    540	.read		= wdt_temp_read,
    541	.open		= wdt_temp_open,
    542	.release	= wdt_temp_release,
    543};
    544
    545static struct miscdevice temp_miscdev = {
    546	.minor	= TEMP_MINOR,
    547	.name	= "temperature",
    548	.fops	= &wdt_temp_fops,
    549};
    550
    551/*
    552 *	The WDT card needs to learn about soft shutdowns in order to
    553 *	turn the timebomb registers off.
    554 */
    555
    556static struct notifier_block wdt_notifier = {
    557	.notifier_call = wdt_notify_sys,
    558};
    559
    560/**
    561 *	wdt_exit:
    562 *
    563 *	Unload the watchdog. You cannot do this with any file handles open.
    564 *	If your watchdog is set to continue ticking on close and you unload
    565 *	it, well it keeps ticking. We won't get the interrupt but the board
    566 *	will not touch PC memory so all is fine. You just have to load a new
    567 *	module in 60 seconds or reboot.
    568 */
    569
    570static void __exit wdt_exit(void)
    571{
    572	misc_deregister(&wdt_miscdev);
    573	if (type == 501)
    574		misc_deregister(&temp_miscdev);
    575	unregister_reboot_notifier(&wdt_notifier);
    576	free_irq(irq, NULL);
    577	release_region(io, 8);
    578}
    579
    580/**
    581 *	wdt_init:
    582 *
    583 *	Set up the WDT watchdog board. All we have to do is grab the
    584 *	resources we require and bitch if anyone beat us to them.
    585 *	The open() function will actually kick the board off.
    586 */
    587
    588static int __init wdt_init(void)
    589{
    590	int ret;
    591
    592	if (type != 500 && type != 501) {
    593		pr_err("unknown card type '%d'\n", type);
    594		return -ENODEV;
    595	}
    596
    597	/* Check that the heartbeat value is within it's range;
    598	   if not reset to the default */
    599	if (wdt_set_heartbeat(heartbeat)) {
    600		wdt_set_heartbeat(WD_TIMO);
    601		pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
    602			WD_TIMO);
    603	}
    604
    605	if (!request_region(io, 8, "wdt501p")) {
    606		pr_err("I/O address 0x%04x already in use\n", io);
    607		ret = -EBUSY;
    608		goto out;
    609	}
    610
    611	ret = request_irq(irq, wdt_interrupt, 0, "wdt501p", NULL);
    612	if (ret) {
    613		pr_err("IRQ %d is not free\n", irq);
    614		goto outreg;
    615	}
    616
    617	ret = register_reboot_notifier(&wdt_notifier);
    618	if (ret) {
    619		pr_err("cannot register reboot notifier (err=%d)\n", ret);
    620		goto outirq;
    621	}
    622
    623	if (type == 501) {
    624		ret = misc_register(&temp_miscdev);
    625		if (ret) {
    626			pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    627			       TEMP_MINOR, ret);
    628			goto outrbt;
    629		}
    630	}
    631
    632	ret = misc_register(&wdt_miscdev);
    633	if (ret) {
    634		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    635		       WATCHDOG_MINOR, ret);
    636		goto outmisc;
    637	}
    638
    639	pr_info("WDT500/501-P driver 0.10 at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
    640		io, irq, heartbeat, nowayout);
    641	if (type == 501)
    642		pr_info("Fan Tachometer is %s\n",
    643			tachometer ? "Enabled" : "Disabled");
    644	return 0;
    645
    646outmisc:
    647	if (type == 501)
    648		misc_deregister(&temp_miscdev);
    649outrbt:
    650	unregister_reboot_notifier(&wdt_notifier);
    651outirq:
    652	free_irq(irq, NULL);
    653outreg:
    654	release_region(io, 8);
    655out:
    656	return ret;
    657}
    658
    659module_init(wdt_init);
    660module_exit(wdt_exit);
    661
    662MODULE_AUTHOR("Alan Cox");
    663MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
    664MODULE_LICENSE("GPL");