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

pika_wdt.c (6965B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * PIKA FPGA based Watchdog Timer
      4 *
      5 * Copyright (c) 2008 PIKA Technologies
      6 *   Sean MacLennan <smaclennan@pikatech.com>
      7 */
      8
      9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     10
     11#include <linux/init.h>
     12#include <linux/errno.h>
     13#include <linux/module.h>
     14#include <linux/moduleparam.h>
     15#include <linux/types.h>
     16#include <linux/kernel.h>
     17#include <linux/fs.h>
     18#include <linux/miscdevice.h>
     19#include <linux/watchdog.h>
     20#include <linux/reboot.h>
     21#include <linux/jiffies.h>
     22#include <linux/timer.h>
     23#include <linux/bitops.h>
     24#include <linux/uaccess.h>
     25#include <linux/io.h>
     26#include <linux/of_address.h>
     27#include <linux/of_platform.h>
     28
     29#define DRV_NAME "PIKA-WDT"
     30
     31/* Hardware timeout in seconds */
     32#define WDT_HW_TIMEOUT 2
     33
     34/* Timer heartbeat (500ms) */
     35#define WDT_TIMEOUT	(HZ/2)
     36
     37/* User land timeout */
     38#define WDT_HEARTBEAT 15
     39static int heartbeat = WDT_HEARTBEAT;
     40module_param(heartbeat, int, 0);
     41MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
     42	"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
     43
     44static bool nowayout = WATCHDOG_NOWAYOUT;
     45module_param(nowayout, bool, 0);
     46MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
     47	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     48
     49static struct {
     50	void __iomem *fpga;
     51	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
     52	unsigned long open;
     53	char expect_close;
     54	int bootstatus;
     55	struct timer_list timer;	/* The timer that pings the watchdog */
     56} pikawdt_private;
     57
     58static struct watchdog_info ident __ro_after_init = {
     59	.identity	= DRV_NAME,
     60	.options	= WDIOF_CARDRESET |
     61			  WDIOF_SETTIMEOUT |
     62			  WDIOF_KEEPALIVEPING |
     63			  WDIOF_MAGICCLOSE,
     64};
     65
     66/*
     67 * Reload the watchdog timer.  (ie, pat the watchdog)
     68 */
     69static inline void pikawdt_reset(void)
     70{
     71	/* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
     72	 * Bit 7,    WTCHDG_EN: When set to 1, the watchdog timer is enabled.
     73	 *           Once enabled, it cannot be disabled. The watchdog can be
     74	 *           kicked by performing any write access to the reset
     75	 *           control register (this register).
     76	 * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
     77	 *           seconds. Valid ranges are 1 to 15 seconds. The value can
     78	 *           be modified dynamically.
     79	 */
     80	unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
     81	/* enable with max timeout - 15 seconds */
     82	reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
     83	out_be32(pikawdt_private.fpga + 0x14, reset);
     84}
     85
     86/*
     87 * Timer tick
     88 */
     89static void pikawdt_ping(struct timer_list *unused)
     90{
     91	if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
     92			(!nowayout && !pikawdt_private.open)) {
     93		pikawdt_reset();
     94		mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
     95	} else
     96		pr_crit("I will reset your machine !\n");
     97}
     98
     99
    100static void pikawdt_keepalive(void)
    101{
    102	pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
    103}
    104
    105static void pikawdt_start(void)
    106{
    107	pikawdt_keepalive();
    108	mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
    109}
    110
    111/*
    112 * Watchdog device is opened, and watchdog starts running.
    113 */
    114static int pikawdt_open(struct inode *inode, struct file *file)
    115{
    116	/* /dev/watchdog can only be opened once */
    117	if (test_and_set_bit(0, &pikawdt_private.open))
    118		return -EBUSY;
    119
    120	pikawdt_start();
    121
    122	return stream_open(inode, file);
    123}
    124
    125/*
    126 * Close the watchdog device.
    127 */
    128static int pikawdt_release(struct inode *inode, struct file *file)
    129{
    130	/* stop internal ping */
    131	if (!pikawdt_private.expect_close)
    132		del_timer(&pikawdt_private.timer);
    133
    134	clear_bit(0, &pikawdt_private.open);
    135	pikawdt_private.expect_close = 0;
    136	return 0;
    137}
    138
    139/*
    140 * Pat the watchdog whenever device is written to.
    141 */
    142static ssize_t pikawdt_write(struct file *file, const char __user *data,
    143			     size_t len, loff_t *ppos)
    144{
    145	if (!len)
    146		return 0;
    147
    148	/* Scan for magic character */
    149	if (!nowayout) {
    150		size_t i;
    151
    152		pikawdt_private.expect_close = 0;
    153
    154		for (i = 0; i < len; i++) {
    155			char c;
    156			if (get_user(c, data + i))
    157				return -EFAULT;
    158			if (c == 'V') {
    159				pikawdt_private.expect_close = 42;
    160				break;
    161			}
    162		}
    163	}
    164
    165	pikawdt_keepalive();
    166
    167	return len;
    168}
    169
    170/*
    171 * Handle commands from user-space.
    172 */
    173static long pikawdt_ioctl(struct file *file,
    174		unsigned int cmd, unsigned long arg)
    175{
    176	void __user *argp = (void __user *)arg;
    177	int __user *p = argp;
    178	int new_value;
    179
    180	switch (cmd) {
    181	case WDIOC_GETSUPPORT:
    182		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
    183
    184	case WDIOC_GETSTATUS:
    185		return put_user(0, p);
    186
    187	case WDIOC_GETBOOTSTATUS:
    188		return put_user(pikawdt_private.bootstatus, p);
    189
    190	case WDIOC_KEEPALIVE:
    191		pikawdt_keepalive();
    192		return 0;
    193
    194	case WDIOC_SETTIMEOUT:
    195		if (get_user(new_value, p))
    196			return -EFAULT;
    197
    198		heartbeat = new_value;
    199		pikawdt_keepalive();
    200
    201		return put_user(new_value, p);  /* return current value */
    202
    203	case WDIOC_GETTIMEOUT:
    204		return put_user(heartbeat, p);
    205	}
    206	return -ENOTTY;
    207}
    208
    209
    210static const struct file_operations pikawdt_fops = {
    211	.owner		= THIS_MODULE,
    212	.llseek		= no_llseek,
    213	.open		= pikawdt_open,
    214	.release	= pikawdt_release,
    215	.write		= pikawdt_write,
    216	.unlocked_ioctl	= pikawdt_ioctl,
    217	.compat_ioctl	= compat_ptr_ioctl,
    218};
    219
    220static struct miscdevice pikawdt_miscdev = {
    221	.minor	= WATCHDOG_MINOR,
    222	.name	= "watchdog",
    223	.fops	= &pikawdt_fops,
    224};
    225
    226static int __init pikawdt_init(void)
    227{
    228	struct device_node *np;
    229	void __iomem *fpga;
    230	u32 post1;
    231	int ret;
    232
    233	np = of_find_compatible_node(NULL, NULL, "pika,fpga");
    234	if (np == NULL) {
    235		pr_err("Unable to find fpga\n");
    236		return -ENOENT;
    237	}
    238
    239	pikawdt_private.fpga = of_iomap(np, 0);
    240	of_node_put(np);
    241	if (pikawdt_private.fpga == NULL) {
    242		pr_err("Unable to map fpga\n");
    243		return -ENOMEM;
    244	}
    245
    246	ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
    247
    248	/* POST information is in the sd area. */
    249	np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
    250	if (np == NULL) {
    251		pr_err("Unable to find fpga-sd\n");
    252		ret = -ENOENT;
    253		goto out;
    254	}
    255
    256	fpga = of_iomap(np, 0);
    257	of_node_put(np);
    258	if (fpga == NULL) {
    259		pr_err("Unable to map fpga-sd\n");
    260		ret = -ENOMEM;
    261		goto out;
    262	}
    263
    264	/* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
    265	 * Bit 31,   WDOG: Set to 1 when the last reset was caused by a watchdog
    266	 *           timeout.
    267	 */
    268	post1 = in_be32(fpga + 0x40);
    269	if (post1 & 0x80000000)
    270		pikawdt_private.bootstatus = WDIOF_CARDRESET;
    271
    272	iounmap(fpga);
    273
    274	timer_setup(&pikawdt_private.timer, pikawdt_ping, 0);
    275
    276	ret = misc_register(&pikawdt_miscdev);
    277	if (ret) {
    278		pr_err("Unable to register miscdev\n");
    279		goto out;
    280	}
    281
    282	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
    283		heartbeat, nowayout);
    284	return 0;
    285
    286out:
    287	iounmap(pikawdt_private.fpga);
    288	return ret;
    289}
    290
    291static void __exit pikawdt_exit(void)
    292{
    293	misc_deregister(&pikawdt_miscdev);
    294
    295	iounmap(pikawdt_private.fpga);
    296}
    297
    298module_init(pikawdt_init);
    299module_exit(pikawdt_exit);
    300
    301MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
    302MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
    303MODULE_LICENSE("GPL");