// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_watchdog.c
*
* A watchdog timer based upon the IPMI interface.
*
* Author: MontaVista Software, Inc.
* Corey Minyard <minyard@mvista.com>
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
*/
#define pr_fmt(fmt) "IPMI Watchdog: " fmt
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
#include <linux/mutex.h>
#include <linux/watchdog.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/kdebug.h>
#include <linux/rwsem.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/notifier.h>
#include <linux/nmi.h>
#include <linux/reboot.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/sched/signal.h>
#ifdef CONFIG_X86
/*
* This is ugly, but I've determined that x86 is the only architecture
* that can reasonably support the IPMI NMI watchdog timeout at this
* time. If another architecture adds this capability somehow, it
* will have to be a somewhat different mechanism and I have no idea
* how it will work. So in the unlikely event that another
* architecture supports this, we can figure out a good generic
* mechanism for it at that time.
*/
#include <asm/kdebug.h>
#include <asm/nmi.h>
#define HAVE_DIE_NMI
#endif
/*
* The IPMI command/response information for the watchdog timer.
*/
/* values for byte 1 of the set command, byte 2 of the get response. */
#define WDOG_DONT_LOG (1 << 7)
#define WDOG_DONT_STOP_ON_SET (1 << 6)
#define WDOG_SET_TIMER_USE(byte, use) \
byte = ((byte) & 0xf8) | ((use) & 0x7)
#define WDOG_GET_TIMER_USE(byte) ((byte) & 0x7)
#define WDOG_TIMER_USE_BIOS_FRB2 1
#define WDOG_TIMER_USE_BIOS_POST 2
#define WDOG_TIMER_USE_OS_LOAD 3
#define WDOG_TIMER_USE_SMS_OS 4
#define WDOG_TIMER_USE_OEM 5
/* values for byte 2 of the set command, byte 3 of the get response. */
#define WDOG_SET_PRETIMEOUT_ACT(byte, use) \
byte = ((byte) & 0x8f) | (((use) & 0x7) << 4)
#define WDOG_GET_PRETIMEOUT_ACT(byte) (((byte) >> 4) & 0x7)
#define WDOG_PRETIMEOUT_NONE 0
#define WDOG_PRETIMEOUT_SMI 1
#define WDOG_PRETIMEOUT_NMI 2
#define WDOG_PRETIMEOUT_MSG_INT 3
/* Operations that can be performed on a pretimout. */
#define WDOG_PREOP_NONE 0
#define WDOG_PREOP_PANIC 1
/* Cause data to be available to read. Doesn't work in NMI mode. */
#define WDOG_PREOP_GIVE_DATA 2
/* Actions to perform on a full timeout. */
#define WDOG_SET_TIMEOUT_ACT(byte, use) \
byte = ((byte) & 0xf8) | ((use) & 0x7)
#define WDOG_GET_TIMEOUT_ACT(byte) ((byte) & 0x7)
#define WDOG_TIMEOUT_NONE 0
#define WDOG_TIMEOUT_RESET 1
#define WDOG_TIMEOUT_POWER_DOWN 2
#define WDOG_TIMEOUT_POWER_CYCLE 3
/*
* Byte 3 of the get command, byte 4 of the get response is the
* pre-timeout in seconds.
*/
/* Bits for setting byte 4 of the set command, byte 5 of the get response. */
#define WDOG_EXPIRE_CLEAR_BIOS_FRB2 (1 << 1)
#define WDOG_EXPIRE_CLEAR_BIOS_POST (1 << 2)
#define WDOG_EXPIRE_CLEAR_OS_LOAD (1 << 3)
#define WDOG_EXPIRE_CLEAR_SMS_OS (1 << 4)
#define WDOG_EXPIRE_CLEAR_OEM (1 << 5)
/*
* Setting/getting the watchdog timer value. This is for bytes 5 and
* 6 (the timeout time) of the set command, and bytes 6 and 7 (the
* timeout time) and 8 and 9 (the current countdown value) of the
* response. The timeout value is given in seconds (in the command it
* is 100ms intervals).
*/
#define WDOG_SET_TIMEOUT(byte1, byte2, val) \
(byte1) = (((val) * 10) & 0xff), (byte2) = (((val) * 10) >> 8)
#define WDOG_GET_TIMEOUT(byte1, byte2) \
(((byte1) | ((byte2) << 8)) / 10)
#define IPMI_WDOG_RESET_TIMER 0x22
#define IPMI_WDOG_SET_TIMER 0x24
#define IPMI_WDOG_GET_TIMER 0x25
#define IPMI_WDOG_TIMER_NOT_INIT_RESP 0x80
static DEFINE_MUTEX(ipmi_watchdog_mutex);
static bool nowayout = WATCHDOG_NOWAYOUT;
static struct ipmi_user *watchdog_user;
static int watchdog_ifnum;
/* Default the timeout to 10 seconds. */
static int timeout = 10;
/* The pre-timeout is disabled by default. */
static int pretimeout;
/* Default timeout to set on panic */
static int panic_wdt_timeout = 255;
/* Default action is to reset the board on a timeout. */
static unsigned char action_val = WDOG_TIMEOUT_RESET;
static char action[16]