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

nv_tco.c (12309B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	nv_tco 0.01:	TCO timer driver for NV chipsets
      4 *
      5 *	(c) Copyright 2005 Google Inc., All Rights Reserved.
      6 *
      7 *	Based off i8xx_tco.c:
      8 *	(c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
      9 *	Reserved.
     10 *				https://www.kernelconcepts.de
     11 *
     12 *	TCO timer driver for NV chipsets
     13 *	based on softdog.c by Alan Cox <alan@redhat.com>
     14 */
     15
     16/*
     17 *	Includes, defines, variables, module parameters, ...
     18 */
     19
     20#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     21
     22#include <linux/module.h>
     23#include <linux/moduleparam.h>
     24#include <linux/types.h>
     25#include <linux/miscdevice.h>
     26#include <linux/watchdog.h>
     27#include <linux/init.h>
     28#include <linux/fs.h>
     29#include <linux/pci.h>
     30#include <linux/ioport.h>
     31#include <linux/jiffies.h>
     32#include <linux/platform_device.h>
     33#include <linux/uaccess.h>
     34#include <linux/io.h>
     35
     36#include "nv_tco.h"
     37
     38/* Module and version information */
     39#define TCO_VERSION "0.01"
     40#define TCO_MODULE_NAME "NV_TCO"
     41#define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION
     42
     43/* internal variables */
     44static unsigned int tcobase;
     45static DEFINE_SPINLOCK(tco_lock);	/* Guards the hardware */
     46static unsigned long timer_alive;
     47static char tco_expect_close;
     48static struct pci_dev *tco_pci;
     49
     50/* the watchdog platform device */
     51static struct platform_device *nv_tco_platform_device;
     52
     53/* module parameters */
     54#define WATCHDOG_HEARTBEAT 30	/* 30 sec default heartbeat (2<heartbeat<39) */
     55static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
     56module_param(heartbeat, int, 0);
     57MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, "
     58			    "default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
     59
     60static bool nowayout = WATCHDOG_NOWAYOUT;
     61module_param(nowayout, bool, 0);
     62MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
     63		" (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     64
     65/*
     66 * Some TCO specific functions
     67 */
     68static inline unsigned char seconds_to_ticks(int seconds)
     69{
     70	/* the internal timer is stored as ticks which decrement
     71	 * every 0.6 seconds */
     72	return (seconds * 10) / 6;
     73}
     74
     75static void tco_timer_start(void)
     76{
     77	u32 val;
     78	unsigned long flags;
     79
     80	spin_lock_irqsave(&tco_lock, flags);
     81	val = inl(TCO_CNT(tcobase));
     82	val &= ~TCO_CNT_TCOHALT;
     83	outl(val, TCO_CNT(tcobase));
     84	spin_unlock_irqrestore(&tco_lock, flags);
     85}
     86
     87static void tco_timer_stop(void)
     88{
     89	u32 val;
     90	unsigned long flags;
     91
     92	spin_lock_irqsave(&tco_lock, flags);
     93	val = inl(TCO_CNT(tcobase));
     94	val |= TCO_CNT_TCOHALT;
     95	outl(val, TCO_CNT(tcobase));
     96	spin_unlock_irqrestore(&tco_lock, flags);
     97}
     98
     99static void tco_timer_keepalive(void)
    100{
    101	unsigned long flags;
    102
    103	spin_lock_irqsave(&tco_lock, flags);
    104	outb(0x01, TCO_RLD(tcobase));
    105	spin_unlock_irqrestore(&tco_lock, flags);
    106}
    107
    108static int tco_timer_set_heartbeat(int t)
    109{
    110	int ret = 0;
    111	unsigned char tmrval;
    112	unsigned long flags;
    113	u8 val;
    114
    115	/*
    116	 * note seconds_to_ticks(t) > t, so if t > 0x3f, so is
    117	 * tmrval=seconds_to_ticks(t).  Check that the count in seconds isn't
    118	 * out of range on it's own (to avoid overflow in tmrval).
    119	 */
    120	if (t < 0 || t > 0x3f)
    121		return -EINVAL;
    122	tmrval = seconds_to_ticks(t);
    123
    124	/* "Values of 0h-3h are ignored and should not be attempted" */
    125	if (tmrval > 0x3f || tmrval < 0x04)
    126		return -EINVAL;
    127
    128	/* Write new heartbeat to watchdog */
    129	spin_lock_irqsave(&tco_lock, flags);
    130	val = inb(TCO_TMR(tcobase));
    131	val &= 0xc0;
    132	val |= tmrval;
    133	outb(val, TCO_TMR(tcobase));
    134	val = inb(TCO_TMR(tcobase));
    135
    136	if ((val & 0x3f) != tmrval)
    137		ret = -EINVAL;
    138	spin_unlock_irqrestore(&tco_lock, flags);
    139
    140	if (ret)
    141		return ret;
    142
    143	heartbeat = t;
    144	return 0;
    145}
    146
    147/*
    148 *	/dev/watchdog handling
    149 */
    150
    151static int nv_tco_open(struct inode *inode, struct file *file)
    152{
    153	/* /dev/watchdog can only be opened once */
    154	if (test_and_set_bit(0, &timer_alive))
    155		return -EBUSY;
    156
    157	/* Reload and activate timer */
    158	tco_timer_keepalive();
    159	tco_timer_start();
    160	return stream_open(inode, file);
    161}
    162
    163static int nv_tco_release(struct inode *inode, struct file *file)
    164{
    165	/* Shut off the timer */
    166	if (tco_expect_close == 42) {
    167		tco_timer_stop();
    168	} else {
    169		pr_crit("Unexpected close, not stopping watchdog!\n");
    170		tco_timer_keepalive();
    171	}
    172	clear_bit(0, &timer_alive);
    173	tco_expect_close = 0;
    174	return 0;
    175}
    176
    177static ssize_t nv_tco_write(struct file *file, const char __user *data,
    178			    size_t len, loff_t *ppos)
    179{
    180	/* See if we got the magic character 'V' and reload the timer */
    181	if (len) {
    182		if (!nowayout) {
    183			size_t i;
    184
    185			/*
    186			 * note: just in case someone wrote the magic character
    187			 * five months ago...
    188			 */
    189			tco_expect_close = 0;
    190
    191			/*
    192			 * scan to see whether or not we got the magic
    193			 * character
    194			 */
    195			for (i = 0; i != len; i++) {
    196				char c;
    197				if (get_user(c, data + i))
    198					return -EFAULT;
    199				if (c == 'V')
    200					tco_expect_close = 42;
    201			}
    202		}
    203
    204		/* someone wrote to us, we should reload the timer */
    205		tco_timer_keepalive();
    206	}
    207	return len;
    208}
    209
    210static long nv_tco_ioctl(struct file *file, unsigned int cmd,
    211			 unsigned long arg)
    212{
    213	int new_options, retval = -EINVAL;
    214	int new_heartbeat;
    215	void __user *argp = (void __user *)arg;
    216	int __user *p = argp;
    217	static const struct watchdog_info ident = {
    218		.options =		WDIOF_SETTIMEOUT |
    219					WDIOF_KEEPALIVEPING |
    220					WDIOF_MAGICCLOSE,
    221		.firmware_version =	0,
    222		.identity =		TCO_MODULE_NAME,
    223	};
    224
    225	switch (cmd) {
    226	case WDIOC_GETSUPPORT:
    227		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
    228	case WDIOC_GETSTATUS:
    229	case WDIOC_GETBOOTSTATUS:
    230		return put_user(0, p);
    231	case WDIOC_SETOPTIONS:
    232		if (get_user(new_options, p))
    233			return -EFAULT;
    234		if (new_options & WDIOS_DISABLECARD) {
    235			tco_timer_stop();
    236			retval = 0;
    237		}
    238		if (new_options & WDIOS_ENABLECARD) {
    239			tco_timer_keepalive();
    240			tco_timer_start();
    241			retval = 0;
    242		}
    243		return retval;
    244	case WDIOC_KEEPALIVE:
    245		tco_timer_keepalive();
    246		return 0;
    247	case WDIOC_SETTIMEOUT:
    248		if (get_user(new_heartbeat, p))
    249			return -EFAULT;
    250		if (tco_timer_set_heartbeat(new_heartbeat))
    251			return -EINVAL;
    252		tco_timer_keepalive();
    253		fallthrough;
    254	case WDIOC_GETTIMEOUT:
    255		return put_user(heartbeat, p);
    256	default:
    257		return -ENOTTY;
    258	}
    259}
    260
    261/*
    262 *	Kernel Interfaces
    263 */
    264
    265static const struct file_operations nv_tco_fops = {
    266	.owner =		THIS_MODULE,
    267	.llseek =		no_llseek,
    268	.write =		nv_tco_write,
    269	.unlocked_ioctl =	nv_tco_ioctl,
    270	.compat_ioctl =		compat_ptr_ioctl,
    271	.open =			nv_tco_open,
    272	.release =		nv_tco_release,
    273};
    274
    275static struct miscdevice nv_tco_miscdev = {
    276	.minor =	WATCHDOG_MINOR,
    277	.name =		"watchdog",
    278	.fops =		&nv_tco_fops,
    279};
    280
    281/*
    282 * Data for PCI driver interface
    283 *
    284 * This data only exists for exporting the supported
    285 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
    286 * register a pci_driver, because someone else might one day
    287 * want to register another driver on the same PCI id.
    288 */
    289static const struct pci_device_id tco_pci_tbl[] = {
    290	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
    291	  PCI_ANY_ID, PCI_ANY_ID, },
    292	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
    293	  PCI_ANY_ID, PCI_ANY_ID, },
    294	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS,
    295	  PCI_ANY_ID, PCI_ANY_ID, },
    296	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS,
    297	  PCI_ANY_ID, PCI_ANY_ID, },
    298	{ 0, },			/* End of list */
    299};
    300MODULE_DEVICE_TABLE(pci, tco_pci_tbl);
    301
    302/*
    303 *	Init & exit routines
    304 */
    305
    306static unsigned char nv_tco_getdevice(void)
    307{
    308	struct pci_dev *dev = NULL;
    309	u32 val;
    310
    311	/* Find the PCI device */
    312	for_each_pci_dev(dev) {
    313		if (pci_match_id(tco_pci_tbl, dev) != NULL) {
    314			tco_pci = dev;
    315			break;
    316		}
    317	}
    318
    319	if (!tco_pci)
    320		return 0;
    321
    322	/* Find the base io port */
    323	pci_read_config_dword(tco_pci, 0x64, &val);
    324	val &= 0xffff;
    325	if (val == 0x0001 || val == 0x0000) {
    326		/* Something is wrong here, bar isn't setup */
    327		pr_err("failed to get tcobase address\n");
    328		return 0;
    329	}
    330	val &= 0xff00;
    331	tcobase = val + 0x40;
    332
    333	if (!request_region(tcobase, 0x10, "NV TCO")) {
    334		pr_err("I/O address 0x%04x already in use\n", tcobase);
    335		return 0;
    336	}
    337
    338	/* Set a reasonable heartbeat before we stop the timer */
    339	tco_timer_set_heartbeat(30);
    340
    341	/*
    342	 * Stop the TCO before we change anything so we don't race with
    343	 * a zeroed timer.
    344	 */
    345	tco_timer_keepalive();
    346	tco_timer_stop();
    347
    348	/* Disable SMI caused by TCO */
    349	if (!request_region(MCP51_SMI_EN(tcobase), 4, "NV TCO")) {
    350		pr_err("I/O address 0x%04x already in use\n",
    351		       MCP51_SMI_EN(tcobase));
    352		goto out;
    353	}
    354	val = inl(MCP51_SMI_EN(tcobase));
    355	val &= ~MCP51_SMI_EN_TCO;
    356	outl(val, MCP51_SMI_EN(tcobase));
    357	val = inl(MCP51_SMI_EN(tcobase));
    358	release_region(MCP51_SMI_EN(tcobase), 4);
    359	if (val & MCP51_SMI_EN_TCO) {
    360		pr_err("Could not disable SMI caused by TCO\n");
    361		goto out;
    362	}
    363
    364	/* Check chipset's NO_REBOOT bit */
    365	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    366	val |= MCP51_SMBUS_SETUP_B_TCO_REBOOT;
    367	pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
    368	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    369	if (!(val & MCP51_SMBUS_SETUP_B_TCO_REBOOT)) {
    370		pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
    371		goto out;
    372	}
    373
    374	return 1;
    375out:
    376	release_region(tcobase, 0x10);
    377	return 0;
    378}
    379
    380static int nv_tco_init(struct platform_device *dev)
    381{
    382	int ret;
    383
    384	/* Check whether or not the hardware watchdog is there */
    385	if (!nv_tco_getdevice())
    386		return -ENODEV;
    387
    388	/* Check to see if last reboot was due to watchdog timeout */
    389	pr_info("Watchdog reboot %sdetected\n",
    390		inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not ");
    391
    392	/* Clear out the old status */
    393	outl(TCO_STS_RESET, TCO_STS(tcobase));
    394
    395	/*
    396	 * Check that the heartbeat value is within it's range.
    397	 * If not, reset to the default.
    398	 */
    399	if (tco_timer_set_heartbeat(heartbeat)) {
    400		heartbeat = WATCHDOG_HEARTBEAT;
    401		tco_timer_set_heartbeat(heartbeat);
    402		pr_info("heartbeat value must be 2<heartbeat<39, using %d\n",
    403			heartbeat);
    404	}
    405
    406	ret = misc_register(&nv_tco_miscdev);
    407	if (ret != 0) {
    408		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    409		       WATCHDOG_MINOR, ret);
    410		goto unreg_region;
    411	}
    412
    413	clear_bit(0, &timer_alive);
    414
    415	tco_timer_stop();
    416
    417	pr_info("initialized (0x%04x). heartbeat=%d sec (nowayout=%d)\n",
    418		tcobase, heartbeat, nowayout);
    419
    420	return 0;
    421
    422unreg_region:
    423	release_region(tcobase, 0x10);
    424	return ret;
    425}
    426
    427static void nv_tco_cleanup(void)
    428{
    429	u32 val;
    430
    431	/* Stop the timer before we leave */
    432	if (!nowayout)
    433		tco_timer_stop();
    434
    435	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
    436	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    437	val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT;
    438	pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
    439	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    440	if (val & MCP51_SMBUS_SETUP_B_TCO_REBOOT) {
    441		pr_crit("Couldn't unset REBOOT bit.  Machine may soon reset\n");
    442	}
    443
    444	/* Deregister */
    445	misc_deregister(&nv_tco_miscdev);
    446	release_region(tcobase, 0x10);
    447}
    448
    449static int nv_tco_remove(struct platform_device *dev)
    450{
    451	if (tcobase)
    452		nv_tco_cleanup();
    453
    454	return 0;
    455}
    456
    457static void nv_tco_shutdown(struct platform_device *dev)
    458{
    459	u32 val;
    460
    461	tco_timer_stop();
    462
    463	/* Some BIOSes fail the POST (once) if the NO_REBOOT flag is not
    464	 * unset during shutdown. */
    465	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    466	val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT;
    467	pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
    468}
    469
    470static struct platform_driver nv_tco_driver = {
    471	.probe		= nv_tco_init,
    472	.remove		= nv_tco_remove,
    473	.shutdown	= nv_tco_shutdown,
    474	.driver		= {
    475		.name	= TCO_MODULE_NAME,
    476	},
    477};
    478
    479static int __init nv_tco_init_module(void)
    480{
    481	int err;
    482
    483	pr_info("NV TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
    484
    485	err = platform_driver_register(&nv_tco_driver);
    486	if (err)
    487		return err;
    488
    489	nv_tco_platform_device = platform_device_register_simple(
    490					TCO_MODULE_NAME, -1, NULL, 0);
    491	if (IS_ERR(nv_tco_platform_device)) {
    492		err = PTR_ERR(nv_tco_platform_device);
    493		goto unreg_platform_driver;
    494	}
    495
    496	return 0;
    497
    498unreg_platform_driver:
    499	platform_driver_unregister(&nv_tco_driver);
    500	return err;
    501}
    502
    503static void __exit nv_tco_cleanup_module(void)
    504{
    505	platform_device_unregister(nv_tco_platform_device);
    506	platform_driver_unregister(&nv_tco_driver);
    507	pr_info("NV TCO Watchdog Module Unloaded\n");
    508}
    509
    510module_init(nv_tco_init_module);
    511module_exit(nv_tco_cleanup_module);
    512
    513MODULE_AUTHOR("Mike Waychison");
    514MODULE_DESCRIPTION("TCO timer driver for NV chipsets");
    515MODULE_LICENSE("GPL");