Merge tag 'apparmor-pr-2018-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / watchdog / hpwdt.c
1 /*
2  *      HPE WatchDog Driver
3  *      based on
4  *
5  *      SoftDog 0.05:   A Software Watchdog Device
6  *
7  *      (c) Copyright 2015 Hewlett Packard Enterprise Development LP
8  *      Thomas Mingarelli <thomas.mingarelli@hpe.com>
9  *
10  *      This program is free software; you can redistribute it and/or
11  *      modify it under the terms of the GNU General Public License
12  *      version 2 as published by the Free Software Foundation
13  *
14  */
15
16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18 #include <linux/device.h>
19 #include <linux/fs.h>
20 #include <linux/io.h>
21 #include <linux/bitops.h>
22 #include <linux/kernel.h>
23 #include <linux/miscdevice.h>
24 #include <linux/module.h>
25 #include <linux/moduleparam.h>
26 #include <linux/pci.h>
27 #include <linux/pci_ids.h>
28 #include <linux/types.h>
29 #include <linux/uaccess.h>
30 #include <linux/watchdog.h>
31 #include <asm/nmi.h>
32
33 #define HPWDT_VERSION                   "1.4.0"
34 #define SECS_TO_TICKS(secs)             ((secs) * 1000 / 128)
35 #define TICKS_TO_SECS(ticks)            ((ticks) * 128 / 1000)
36 #define HPWDT_MAX_TIMER                 TICKS_TO_SECS(65535)
37 #define DEFAULT_MARGIN                  30
38
39 static unsigned int soft_margin = DEFAULT_MARGIN;       /* in seconds */
40 static unsigned int reload;                     /* the computed soft_margin */
41 static bool nowayout = WATCHDOG_NOWAYOUT;
42 #ifdef CONFIG_HPWDT_NMI_DECODING
43 static unsigned int allow_kdump = 1;
44 #endif
45 static char expect_release;
46 static unsigned long hpwdt_is_open;
47
48 static void __iomem *pci_mem_addr;              /* the PCI-memory address */
49 static unsigned long __iomem *hpwdt_nmistat;
50 static unsigned long __iomem *hpwdt_timer_reg;
51 static unsigned long __iomem *hpwdt_timer_con;
52
53 static const struct pci_device_id hpwdt_devices[] = {
54         { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) },   /* iLO2 */
55         { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) },       /* iLO3 */
56         {0},                    /* terminate list */
57 };
58 MODULE_DEVICE_TABLE(pci, hpwdt_devices);
59
60
61 /*
62  *      Watchdog operations
63  */
64 static void hpwdt_start(void)
65 {
66         reload = SECS_TO_TICKS(soft_margin);
67         iowrite16(reload, hpwdt_timer_reg);
68         iowrite8(0x85, hpwdt_timer_con);
69 }
70
71 static void hpwdt_stop(void)
72 {
73         unsigned long data;
74
75         data = ioread8(hpwdt_timer_con);
76         data &= 0xFE;
77         iowrite8(data, hpwdt_timer_con);
78 }
79
80 static void hpwdt_ping(void)
81 {
82         iowrite16(reload, hpwdt_timer_reg);
83 }
84
85 static int hpwdt_change_timer(int new_margin)
86 {
87         if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) {
88                 pr_warn("New value passed in is invalid: %d seconds\n",
89                         new_margin);
90                 return -EINVAL;
91         }
92
93         soft_margin = new_margin;
94         pr_debug("New timer passed in is %d seconds\n", new_margin);
95         reload = SECS_TO_TICKS(soft_margin);
96
97         return 0;
98 }
99
100 static int hpwdt_time_left(void)
101 {
102         return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
103 }
104
105 #ifdef CONFIG_HPWDT_NMI_DECODING
106 static int hpwdt_my_nmi(void)
107 {
108         return ioread8(hpwdt_nmistat) & 0x6;
109 }
110
111 /*
112  *      NMI Handler
113  */
114 static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
115 {
116         if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi())
117                 return NMI_DONE;
118
119         if (allow_kdump)
120                 hpwdt_stop();
121
122         nmi_panic(regs, "An NMI occurred. Depending on your system the reason "
123                 "for the NMI is logged in any one of the following "
124                 "resources:\n"
125                 "1. Integrated Management Log (IML)\n"
126                 "2. OA Syslog\n"
127                 "3. OA Forward Progress Log\n"
128                 "4. iLO Event Log");
129
130         return NMI_HANDLED;
131 }
132 #endif /* CONFIG_HPWDT_NMI_DECODING */
133
134 /*
135  *      /dev/watchdog handling
136  */
137 static int hpwdt_open(struct inode *inode, struct file *file)
138 {
139         /* /dev/watchdog can only be opened once */
140         if (test_and_set_bit(0, &hpwdt_is_open))
141                 return -EBUSY;
142
143         /* Start the watchdog */
144         hpwdt_start();
145         hpwdt_ping();
146
147         return nonseekable_open(inode, file);
148 }
149
150 static int hpwdt_release(struct inode *inode, struct file *file)
151 {
152         /* Stop the watchdog */
153         if (expect_release == 42) {
154                 hpwdt_stop();
155         } else {
156                 pr_crit("Unexpected close, not stopping watchdog!\n");
157                 hpwdt_ping();
158         }
159
160         expect_release = 0;
161
162         /* /dev/watchdog is being closed, make sure it can be re-opened */
163         clear_bit(0, &hpwdt_is_open);
164
165         return 0;
166 }
167
168 static ssize_t hpwdt_write(struct file *file, const char __user *data,
169         size_t len, loff_t *ppos)
170 {
171         /* See if we got the magic character 'V' and reload the timer */
172         if (len) {
173                 if (!nowayout) {
174                         size_t i;
175
176                         /* note: just in case someone wrote the magic character
177                          * five months ago... */
178                         expect_release = 0;
179
180                         /* scan to see whether or not we got the magic char. */
181                         for (i = 0; i != len; i++) {
182                                 char c;
183                                 if (get_user(c, data + i))
184                                         return -EFAULT;
185                                 if (c == 'V')
186                                         expect_release = 42;
187                         }
188                 }
189
190                 /* someone wrote to us, we should reload the timer */
191                 hpwdt_ping();
192         }
193
194         return len;
195 }
196
197 static const struct watchdog_info ident = {
198         .options = WDIOF_SETTIMEOUT |
199                    WDIOF_KEEPALIVEPING |
200                    WDIOF_MAGICCLOSE,
201         .identity = "HPE iLO2+ HW Watchdog Timer",
202 };
203
204 static long hpwdt_ioctl(struct file *file, unsigned int cmd,
205         unsigned long arg)
206 {
207         void __user *argp = (void __user *)arg;
208         int __user *p = argp;
209         int new_margin, options;
210         int ret = -ENOTTY;
211
212         switch (cmd) {
213         case WDIOC_GETSUPPORT:
214                 ret = 0;
215                 if (copy_to_user(argp, &ident, sizeof(ident)))
216                         ret = -EFAULT;
217                 break;
218
219         case WDIOC_GETSTATUS:
220         case WDIOC_GETBOOTSTATUS:
221                 ret = put_user(0, p);
222                 break;
223
224         case WDIOC_KEEPALIVE:
225                 hpwdt_ping();
226                 ret = 0;
227                 break;
228
229         case WDIOC_SETOPTIONS:
230                 ret = get_user(options, p);
231                 if (ret)
232                         break;
233
234                 if (options & WDIOS_DISABLECARD)
235                         hpwdt_stop();
236
237                 if (options & WDIOS_ENABLECARD) {
238                         hpwdt_start();
239                         hpwdt_ping();
240                 }
241                 break;
242
243         case WDIOC_SETTIMEOUT:
244                 ret = get_user(new_margin, p);
245                 if (ret)
246                         break;
247
248                 ret = hpwdt_change_timer(new_margin);
249                 if (ret)
250                         break;
251
252                 hpwdt_ping();
253                 /* Fall */
254         case WDIOC_GETTIMEOUT:
255                 ret = put_user(soft_margin, p);
256                 break;
257
258         case WDIOC_GETTIMELEFT:
259                 ret = put_user(hpwdt_time_left(), p);
260                 break;
261         }
262         return ret;
263 }
264
265 /*
266  *      Kernel interfaces
267  */
268 static const struct file_operations hpwdt_fops = {
269         .owner = THIS_MODULE,
270         .llseek = no_llseek,
271         .write = hpwdt_write,
272         .unlocked_ioctl = hpwdt_ioctl,
273         .open = hpwdt_open,
274         .release = hpwdt_release,
275 };
276
277 static struct miscdevice hpwdt_miscdev = {
278         .minor = WATCHDOG_MINOR,
279         .name = "watchdog",
280         .fops = &hpwdt_fops,
281 };
282
283 /*
284  *      Init & Exit
285  */
286
287
288 static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
289 {
290 #ifdef CONFIG_HPWDT_NMI_DECODING
291         int retval;
292         /*
293          * Only one function can register for NMI_UNKNOWN
294          */
295         retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
296         if (retval)
297                 goto error;
298         retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
299         if (retval)
300                 goto error1;
301         retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
302         if (retval)
303                 goto error2;
304
305         dev_info(&dev->dev,
306                         "HPE Watchdog Timer Driver: NMI decoding initialized"
307                         ", allow kernel dump: %s (default = 1/ON)\n",
308                         (allow_kdump == 0) ? "OFF" : "ON");
309         return 0;
310
311 error2:
312         unregister_nmi_handler(NMI_SERR, "hpwdt");
313 error1:
314         unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
315 error:
316         dev_warn(&dev->dev,
317                 "Unable to register a die notifier (err=%d).\n",
318                 retval);
319         return retval;
320 #endif  /* CONFIG_HPWDT_NMI_DECODING */
321         return 0;
322 }
323
324 static void hpwdt_exit_nmi_decoding(void)
325 {
326 #ifdef CONFIG_HPWDT_NMI_DECODING
327         unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
328         unregister_nmi_handler(NMI_SERR, "hpwdt");
329         unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
330 #endif
331 }
332
333 static int hpwdt_init_one(struct pci_dev *dev,
334                                         const struct pci_device_id *ent)
335 {
336         int retval;
337
338         /*
339          * First let's find out if we are on an iLO2+ server. We will
340          * not run on a legacy ASM box.
341          * So we only support the G5 ProLiant servers and higher.
342          */
343         if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
344             dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
345                 dev_warn(&dev->dev,
346                         "This server does not have an iLO2+ ASIC.\n");
347                 return -ENODEV;
348         }
349
350         /*
351          * Ignore all auxilary iLO devices with the following PCI ID
352          */
353         if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
354             dev->subsystem_device == 0x1979)
355                 return -ENODEV;
356
357         if (pci_enable_device(dev)) {
358                 dev_warn(&dev->dev,
359                         "Not possible to enable PCI Device: 0x%x:0x%x.\n",
360                         ent->vendor, ent->device);
361                 return -ENODEV;
362         }
363
364         pci_mem_addr = pci_iomap(dev, 1, 0x80);
365         if (!pci_mem_addr) {
366                 dev_warn(&dev->dev,
367                         "Unable to detect the iLO2+ server memory.\n");
368                 retval = -ENOMEM;
369                 goto error_pci_iomap;
370         }
371         hpwdt_nmistat   = pci_mem_addr + 0x6e;
372         hpwdt_timer_reg = pci_mem_addr + 0x70;
373         hpwdt_timer_con = pci_mem_addr + 0x72;
374
375         /* Make sure that timer is disabled until /dev/watchdog is opened */
376         hpwdt_stop();
377
378         /* Make sure that we have a valid soft_margin */
379         if (hpwdt_change_timer(soft_margin))
380                 hpwdt_change_timer(DEFAULT_MARGIN);
381
382         /* Initialize NMI Decoding functionality */
383         retval = hpwdt_init_nmi_decoding(dev);
384         if (retval != 0)
385                 goto error_init_nmi_decoding;
386
387         retval = misc_register(&hpwdt_miscdev);
388         if (retval < 0) {
389                 dev_warn(&dev->dev,
390                         "Unable to register miscdev on minor=%d (err=%d).\n",
391                         WATCHDOG_MINOR, retval);
392                 goto error_misc_register;
393         }
394
395         dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s"
396                         ", timer margin: %d seconds (nowayout=%d).\n",
397                         HPWDT_VERSION, soft_margin, nowayout);
398         return 0;
399
400 error_misc_register:
401         hpwdt_exit_nmi_decoding();
402 error_init_nmi_decoding:
403         pci_iounmap(dev, pci_mem_addr);
404 error_pci_iomap:
405         pci_disable_device(dev);
406         return retval;
407 }
408
409 static void hpwdt_exit(struct pci_dev *dev)
410 {
411         if (!nowayout)
412                 hpwdt_stop();
413
414         misc_deregister(&hpwdt_miscdev);
415         hpwdt_exit_nmi_decoding();
416         pci_iounmap(dev, pci_mem_addr);
417         pci_disable_device(dev);
418 }
419
420 static struct pci_driver hpwdt_driver = {
421         .name = "hpwdt",
422         .id_table = hpwdt_devices,
423         .probe = hpwdt_init_one,
424         .remove = hpwdt_exit,
425 };
426
427 MODULE_AUTHOR("Tom Mingarelli");
428 MODULE_DESCRIPTION("hp watchdog driver");
429 MODULE_LICENSE("GPL");
430 MODULE_VERSION(HPWDT_VERSION);
431
432 module_param(soft_margin, int, 0);
433 MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
434
435 module_param(nowayout, bool, 0);
436 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
437                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
438
439 #ifdef CONFIG_HPWDT_NMI_DECODING
440 module_param(allow_kdump, int, 0);
441 MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
442 #endif /* CONFIG_HPWDT_NMI_DECODING */
443
444 module_pci_driver(hpwdt_driver);