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

sun4v_wdt.c (4390B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	sun4v watchdog timer
      4 *	(c) Copyright 2016 Oracle Corporation
      5 *
      6 *	Implement a simple watchdog driver using the built-in sun4v hypervisor
      7 *	watchdog support. If time expires, the hypervisor stops or bounces
      8 *	the guest domain.
      9 */
     10
     11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     12
     13#include <linux/errno.h>
     14#include <linux/init.h>
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/moduleparam.h>
     18#include <linux/watchdog.h>
     19#include <asm/hypervisor.h>
     20#include <asm/mdesc.h>
     21
     22#define WDT_TIMEOUT			60
     23#define WDT_MAX_TIMEOUT			31536000
     24#define WDT_MIN_TIMEOUT			1
     25#define WDT_DEFAULT_RESOLUTION_MS	1000	/* 1 second */
     26
     27static unsigned int timeout;
     28module_param(timeout, uint, 0);
     29MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
     30	__MODULE_STRING(WDT_TIMEOUT) ")");
     31
     32static bool nowayout = WATCHDOG_NOWAYOUT;
     33module_param(nowayout, bool, S_IRUGO);
     34MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
     35	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     36
     37static int sun4v_wdt_stop(struct watchdog_device *wdd)
     38{
     39	sun4v_mach_set_watchdog(0, NULL);
     40
     41	return 0;
     42}
     43
     44static int sun4v_wdt_ping(struct watchdog_device *wdd)
     45{
     46	int hverr;
     47
     48	/*
     49	 * HV watchdog timer will round up the timeout
     50	 * passed in to the nearest multiple of the
     51	 * watchdog resolution in milliseconds.
     52	 */
     53	hverr = sun4v_mach_set_watchdog(wdd->timeout * 1000, NULL);
     54	if (hverr == HV_EINVAL)
     55		return -EINVAL;
     56
     57	return 0;
     58}
     59
     60static int sun4v_wdt_set_timeout(struct watchdog_device *wdd,
     61				 unsigned int timeout)
     62{
     63	wdd->timeout = timeout;
     64
     65	return 0;
     66}
     67
     68static const struct watchdog_info sun4v_wdt_ident = {
     69	.options =	WDIOF_SETTIMEOUT |
     70			WDIOF_MAGICCLOSE |
     71			WDIOF_KEEPALIVEPING,
     72	.identity =	"sun4v hypervisor watchdog",
     73	.firmware_version = 0,
     74};
     75
     76static const struct watchdog_ops sun4v_wdt_ops = {
     77	.owner =	THIS_MODULE,
     78	.start =	sun4v_wdt_ping,
     79	.stop =		sun4v_wdt_stop,
     80	.ping =		sun4v_wdt_ping,
     81	.set_timeout =	sun4v_wdt_set_timeout,
     82};
     83
     84static struct watchdog_device wdd = {
     85	.info = &sun4v_wdt_ident,
     86	.ops = &sun4v_wdt_ops,
     87	.min_timeout = WDT_MIN_TIMEOUT,
     88	.max_timeout = WDT_MAX_TIMEOUT,
     89	.timeout = WDT_TIMEOUT,
     90};
     91
     92static int __init sun4v_wdt_init(void)
     93{
     94	struct mdesc_handle *handle;
     95	u64 node;
     96	const u64 *value;
     97	int err = 0;
     98	unsigned long major = 1, minor = 1;
     99
    100	/*
    101	 * There are 2 properties that can be set from the control
    102	 * domain for the watchdog.
    103	 * watchdog-resolution
    104	 * watchdog-max-timeout
    105	 *
    106	 * We can expect a handle to be returned otherwise something
    107	 * serious is wrong. Correct to return -ENODEV here.
    108	 */
    109
    110	handle = mdesc_grab();
    111	if (!handle)
    112		return -ENODEV;
    113
    114	node = mdesc_node_by_name(handle, MDESC_NODE_NULL, "platform");
    115	err = -ENODEV;
    116	if (node == MDESC_NODE_NULL)
    117		goto out_release;
    118
    119	/*
    120	 * This is a safe way to validate if we are on the right
    121	 * platform.
    122	 */
    123	if (sun4v_hvapi_register(HV_GRP_CORE, major, &minor))
    124		goto out_hv_unreg;
    125
    126	/* Allow value of watchdog-resolution up to 1s (default) */
    127	value = mdesc_get_property(handle, node, "watchdog-resolution", NULL);
    128	err = -EINVAL;
    129	if (value) {
    130		if (*value == 0 ||
    131		    *value > WDT_DEFAULT_RESOLUTION_MS)
    132			goto out_hv_unreg;
    133	}
    134
    135	value = mdesc_get_property(handle, node, "watchdog-max-timeout", NULL);
    136	if (value) {
    137		/*
    138		 * If the property value (in ms) is smaller than
    139		 * min_timeout, return -EINVAL.
    140		 */
    141		if (*value < wdd.min_timeout * 1000)
    142			goto out_hv_unreg;
    143
    144		/*
    145		 * If the property value is smaller than
    146		 * default max_timeout  then set watchdog max_timeout to
    147		 * the value of the property in seconds.
    148		 */
    149		if (*value < wdd.max_timeout * 1000)
    150			wdd.max_timeout = *value  / 1000;
    151	}
    152
    153	watchdog_init_timeout(&wdd, timeout, NULL);
    154
    155	watchdog_set_nowayout(&wdd, nowayout);
    156
    157	err = watchdog_register_device(&wdd);
    158	if (err)
    159		goto out_hv_unreg;
    160
    161	pr_info("initialized (timeout=%ds, nowayout=%d)\n",
    162		 wdd.timeout, nowayout);
    163
    164	mdesc_release(handle);
    165
    166	return 0;
    167
    168out_hv_unreg:
    169	sun4v_hvapi_unregister(HV_GRP_CORE);
    170
    171out_release:
    172	mdesc_release(handle);
    173	return err;
    174}
    175
    176static void __exit sun4v_wdt_exit(void)
    177{
    178	sun4v_hvapi_unregister(HV_GRP_CORE);
    179	watchdog_unregister_device(&wdd);
    180}
    181
    182module_init(sun4v_wdt_init);
    183module_exit(sun4v_wdt_exit);
    184
    185MODULE_AUTHOR("Wim Coekaerts <wim.coekaerts@oracle.com>");
    186MODULE_DESCRIPTION("sun4v watchdog driver");
    187MODULE_LICENSE("GPL");