1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Watchdog driver for z/VM and LPAR using the diag 288 interface. 4 * 5 * Under z/VM, expiration of the watchdog will send a "system restart" command 6 * to CP. 7 * 8 * The command can be altered using the module parameter "cmd". This is 9 * not recommended because it's only supported on z/VM but not whith LPAR. 10 * 11 * On LPAR, the watchdog will always trigger a system restart. the module 12 * paramter cmd is meaningless here. 13 * 14 * 15 * Copyright IBM Corp. 2004, 2013 16 * Author(s): Arnd Bergmann (arndb@de.ibm.com) 17 * Philipp Hachtmann (phacht@de.ibm.com) 18 * 19 */ 20 21#define KMSG_COMPONENT "diag288_wdt" 22#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 23 24#include <linux/init.h> 25#include <linux/kernel.h> 26#include <linux/module.h> 27#include <linux/moduleparam.h> 28#include <linux/slab.h> 29#include <linux/watchdog.h> 30#include <asm/ebcdic.h> 31#include <asm/diag.h> 32#include <linux/io.h> 33 34#define MAX_CMDLEN 240 35#define DEFAULT_CMD "SYSTEM RESTART" 36 37#define MIN_INTERVAL 15 /* Minimal time supported by diag88 */ 38#define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */ 39 40#define WDT_DEFAULT_TIMEOUT 30 41 42/* Function codes - init, change, cancel */ 43#define WDT_FUNC_INIT 0 44#define WDT_FUNC_CHANGE 1 45#define WDT_FUNC_CANCEL 2 46#define WDT_FUNC_CONCEAL 0x80000000 47 48/* Action codes for LPAR watchdog */ 49#define LPARWDT_RESTART 0 50 51static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD; 52static bool conceal_on; 53static bool nowayout_info = WATCHDOG_NOWAYOUT; 54 55MODULE_LICENSE("GPL"); 56MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); 57MODULE_AUTHOR("Philipp Hachtmann <phacht@de.ibm.com>"); 58 59MODULE_DESCRIPTION("System z diag288 Watchdog Timer"); 60 61module_param_string(cmd, wdt_cmd, MAX_CMDLEN, 0644); 62MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers (z/VM only)"); 63 64module_param_named(conceal, conceal_on, bool, 0644); 65MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is active (z/VM only)"); 66 67module_param_named(nowayout, nowayout_info, bool, 0444); 68MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)"); 69 70MODULE_ALIAS("vmwatchdog"); 71 72static char *cmd_buf; 73 74static int diag288(unsigned int func, unsigned int timeout, 75 unsigned long action, unsigned int len) 76{ 77 union register_pair r1 = { .even = func, .odd = timeout, }; 78 union register_pair r3 = { .even = action, .odd = len, }; 79 int err; 80 81 diag_stat_inc(DIAG_STAT_X288); 82 83 err = -EINVAL; 84 asm volatile( 85 " diag %[r1],%[r3],0x288\n" 86 "0: la %[err],0\n" 87 "1:\n" 88 EX_TABLE(0b, 1b) 89 : [err] "+d" (err) 90 : [r1] "d" (r1.pair), [r3] "d" (r3.pair) 91 : "cc", "memory"); 92 return err; 93} 94 95static int diag288_str(unsigned int func, unsigned int timeout, char *cmd) 96{ 97 ssize_t len; 98 99 len = strscpy(cmd_buf, cmd, MAX_CMDLEN); 100 if (len < 0) 101 return len; 102 ASCEBC(cmd_buf, MAX_CMDLEN); 103 EBC_TOUPPER(cmd_buf, MAX_CMDLEN); 104 105 return diag288(func, timeout, virt_to_phys(cmd_buf), len); 106} 107 108static int wdt_start(struct watchdog_device *dev) 109{ 110 int ret; 111 unsigned int func; 112 113 if (MACHINE_IS_VM) { 114 func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) 115 : WDT_FUNC_INIT; 116 ret = diag288_str(func, dev->timeout, wdt_cmd); 117 WARN_ON(ret != 0); 118 } else { 119 ret = diag288(WDT_FUNC_INIT, dev->timeout, LPARWDT_RESTART, 0); 120 } 121 122 if (ret) { 123 pr_err("The watchdog cannot be activated\n"); 124 return ret; 125 } 126 return 0; 127} 128 129static int wdt_stop(struct watchdog_device *dev) 130{ 131 return diag288(WDT_FUNC_CANCEL, 0, 0, 0); 132} 133 134static int wdt_ping(struct watchdog_device *dev) 135{ 136 int ret; 137 unsigned int func; 138 139 if (MACHINE_IS_VM) { 140 /* 141 * It seems to be ok to z/VM to use the init function to 142 * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must 143 * be used when the watchdog is running. 144 */ 145 func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) 146 : WDT_FUNC_INIT; 147 148 ret = diag288_str(func, dev->timeout, wdt_cmd); 149 WARN_ON(ret != 0); 150 } else { 151 ret = diag288(WDT_FUNC_CHANGE, dev->timeout, 0, 0); 152 } 153 154 if (ret) 155 pr_err("The watchdog timer cannot be started or reset\n"); 156 return ret; 157} 158 159static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to) 160{ 161 dev->timeout = new_to; 162 return wdt_ping(dev); 163} 164 165static const struct watchdog_ops wdt_ops = { 166 .owner = THIS_MODULE, 167 .start = wdt_start, 168 .stop = wdt_stop, 169 .ping = wdt_ping, 170 .set_timeout = wdt_set_timeout, 171}; 172 173static const struct watchdog_info wdt_info = { 174 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 175 .firmware_version = 0, 176 .identity = "z Watchdog", 177}; 178 179static struct watchdog_device wdt_dev = { 180 .parent = NULL, 181 .info = &wdt_info, 182 .ops = &wdt_ops, 183 .bootstatus = 0, 184 .timeout = WDT_DEFAULT_TIMEOUT, 185 .min_timeout = MIN_INTERVAL, 186 .max_timeout = MAX_INTERVAL, 187}; 188 189static int __init diag288_init(void) 190{ 191 int ret; 192 193 watchdog_set_nowayout(&wdt_dev, nowayout_info); 194 195 if (MACHINE_IS_VM) { 196 cmd_buf = kmalloc(MAX_CMDLEN, GFP_KERNEL); 197 if (!cmd_buf) { 198 pr_err("The watchdog cannot be initialized\n"); 199 return -ENOMEM; 200 } 201 202 ret = diag288_str(WDT_FUNC_INIT, MIN_INTERVAL, "BEGIN"); 203 if (ret != 0) { 204 pr_err("The watchdog cannot be initialized\n"); 205 kfree(cmd_buf); 206 return -EINVAL; 207 } 208 } else { 209 if (diag288(WDT_FUNC_INIT, WDT_DEFAULT_TIMEOUT, 210 LPARWDT_RESTART, 0)) { 211 pr_err("The watchdog cannot be initialized\n"); 212 return -EINVAL; 213 } 214 } 215 216 if (diag288(WDT_FUNC_CANCEL, 0, 0, 0)) { 217 pr_err("The watchdog cannot be deactivated\n"); 218 return -EINVAL; 219 } 220 221 return watchdog_register_device(&wdt_dev); 222} 223 224static void __exit diag288_exit(void) 225{ 226 watchdog_unregister_device(&wdt_dev); 227 kfree(cmd_buf); 228} 229 230module_init(diag288_init); 231module_exit(diag288_exit); 232