1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30
31#include <linux/fs.h>
32#include <linux/init.h>
33#include <linux/kernel.h>
34#include <linux/miscdevice.h>
35#include <linux/module.h>
36#include <linux/notifier.h>
37#include <linux/reboot.h>
38#include <linux/types.h>
39#include <linux/watchdog.h>
40#include <linux/uaccess.h>
41
42#include <asm/rtas.h>
43
44#define WDRTAS_MAGIC_CHAR 42
45#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \
46 WDIOF_MAGICCLOSE)
47
48MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
49MODULE_DESCRIPTION("RTAS watchdog driver");
50MODULE_LICENSE("GPL");
51MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
52MODULE_ALIAS_MISCDEV(TEMP_MINOR);
53
54static bool wdrtas_nowayout = WATCHDOG_NOWAYOUT;
55static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
56static char wdrtas_expect_close;
57
58static int wdrtas_interval;
59
60#define WDRTAS_THERMAL_SENSOR 3
61static int wdrtas_token_get_sensor_state;
62#define WDRTAS_SURVEILLANCE_IND 9000
63static int wdrtas_token_set_indicator;
64#define WDRTAS_SP_SPI 28
65static int wdrtas_token_get_sp;
66static int wdrtas_token_event_scan;
67
68#define WDRTAS_DEFAULT_INTERVAL 300
69
70#define WDRTAS_LOGBUFFER_LEN 128
71static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN];
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87static int wdrtas_set_interval(int interval)
88{
89 long result;
90 static int print_msg = 10;
91
92
93 interval = (interval + 59) / 60;
94
95 result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
96 WDRTAS_SURVEILLANCE_IND, 0, interval);
97 if (result < 0 && print_msg) {
98 pr_err("setting the watchdog to %i timeout failed: %li\n",
99 interval, result);
100 print_msg--;
101 }
102
103 return result;
104}
105
106#define WDRTAS_SP_SPI_LEN 4
107
108
109
110
111
112
113
114
115
116
117
118static int wdrtas_get_interval(int fallback_value)
119{
120 long result;
121 char value[WDRTAS_SP_SPI_LEN];
122
123 spin_lock(&rtas_data_buf_lock);
124 memset(rtas_data_buf, 0, WDRTAS_SP_SPI_LEN);
125 result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
126 WDRTAS_SP_SPI, __pa(rtas_data_buf),
127 WDRTAS_SP_SPI_LEN);
128
129 memcpy(value, rtas_data_buf, WDRTAS_SP_SPI_LEN);
130 spin_unlock(&rtas_data_buf_lock);
131
132 if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
133 pr_warn("could not get sp_spi watchdog timeout (%li). Continuing\n",
134 result);
135 return fallback_value;
136 }
137
138
139 return ((int)value[2]) * 60;
140}
141
142
143
144
145
146
147
148static void wdrtas_timer_start(void)
149{
150 wdrtas_set_interval(wdrtas_interval);
151}
152
153
154
155
156
157
158
159static void wdrtas_timer_stop(void)
160{
161 wdrtas_set_interval(0);
162}
163
164
165
166
167
168
169
170static void wdrtas_log_scanned_event(void)
171{
172 int i;
173
174 for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
175 pr_info("dumping event (line %i/%i), data = "
176 "%02x %02x %02x %02x %02x %02x %02x %02x "
177 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
178 (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
179 wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
180 wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
181 wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
182 wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
183 wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
184 wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
185 wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
186 wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
187}
188
189
190
191
192
193
194
195
196static void wdrtas_timer_keepalive(void)
197{
198 long result;
199
200 do {
201 result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
202 RTAS_EVENT_SCAN_ALL_EVENTS, 0,
203 (void *)__pa(wdrtas_logbuffer),
204 WDRTAS_LOGBUFFER_LEN);
205 if (result < 0)
206 pr_err("event-scan failed: %li\n", result);
207 if (result == 0)
208 wdrtas_log_scanned_event();
209 } while (result == 0);
210}
211
212
213
214
215
216
217
218
219
220static int wdrtas_get_temperature(void)
221{
222 int result;
223 int temperature = 0;
224
225 result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
226
227 if (result < 0)
228 pr_warn("reading the thermal sensor failed: %i\n", result);
229 else
230 temperature = ((temperature * 9) / 5) + 32;
231
232 return temperature;
233}
234
235
236
237
238
239
240
241static int wdrtas_get_status(void)
242{
243 return 0;
244}
245
246
247
248
249
250
251
252static int wdrtas_get_boot_status(void)
253{
254 return 0;
255}
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272static ssize_t wdrtas_write(struct file *file, const char __user *buf,
273 size_t len, loff_t *ppos)
274{
275 int i;
276 char c;
277
278 if (!len)
279 goto out;
280
281 if (!wdrtas_nowayout) {
282 wdrtas_expect_close = 0;
283
284 for (i = 0; i < len; i++) {
285 if (get_user(c, buf + i))
286 return -EFAULT;
287
288 if (c == 'V')
289 wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
290 }
291 }
292
293 wdrtas_timer_keepalive();
294
295out:
296 return len;
297}
298
299
300
301
302
303
304
305
306
307
308
309
310static long wdrtas_ioctl(struct file *file, unsigned int cmd,
311 unsigned long arg)
312{
313 int __user *argp = (void __user *)arg;
314 int i;
315 static const struct watchdog_info wdinfo = {
316 .options = WDRTAS_SUPPORTED_MASK,
317 .firmware_version = 0,
318 .identity = "wdrtas",
319 };
320
321 switch (cmd) {
322 case WDIOC_GETSUPPORT:
323 if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
324 return -EFAULT;
325 return 0;
326
327 case WDIOC_GETSTATUS:
328 i = wdrtas_get_status();
329 return put_user(i, argp);
330
331 case WDIOC_GETBOOTSTATUS:
332 i = wdrtas_get_boot_status();
333 return put_user(i, argp);
334
335 case WDIOC_GETTEMP:
336 if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
337 return -EOPNOTSUPP;
338
339 i = wdrtas_get_temperature();
340 return put_user(i, argp);
341
342 case WDIOC_SETOPTIONS:
343 if (get_user(i, argp))
344 return -EFAULT;
345 if (i & WDIOS_DISABLECARD)
346 wdrtas_timer_stop();
347 if (i & WDIOS_ENABLECARD) {
348 wdrtas_timer_keepalive();
349 wdrtas_timer_start();
350 }
351
352
353
354 return 0;
355
356 case WDIOC_KEEPALIVE:
357 wdrtas_timer_keepalive();
358 return 0;
359
360 case WDIOC_SETTIMEOUT:
361 if (get_user(i, argp))
362 return -EFAULT;
363
364 if (wdrtas_set_interval(i))
365 return -EINVAL;
366
367 wdrtas_timer_keepalive();
368
369 if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
370 wdrtas_interval = i;
371 else
372 wdrtas_interval = wdrtas_get_interval(i);
373
374
375 case WDIOC_GETTIMEOUT:
376 return put_user(wdrtas_interval, argp);
377
378 default:
379 return -ENOTTY;
380 }
381}
382
383
384
385
386
387
388
389
390
391
392
393static int wdrtas_open(struct inode *inode, struct file *file)
394{
395
396 if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
397 atomic_dec(&wdrtas_miscdev_open);
398 return -EBUSY;
399 }
400
401 wdrtas_timer_start();
402 wdrtas_timer_keepalive();
403
404 return nonseekable_open(inode, file);
405}
406
407
408
409
410
411
412
413
414
415
416static int wdrtas_close(struct inode *inode, struct file *file)
417{
418
419 if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
420 wdrtas_timer_stop();
421 else {
422 pr_warn("got unexpected close. Watchdog not stopped.\n");
423 wdrtas_timer_keepalive();
424 }
425
426 wdrtas_expect_close = 0;
427 atomic_dec(&wdrtas_miscdev_open);
428 return 0;
429}
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
445 size_t count, loff_t *ppos)
446{
447 int temperature = 0;
448
449 temperature = wdrtas_get_temperature();
450 if (temperature < 0)
451 return temperature;
452
453 if (copy_to_user(buf, &temperature, 1))
454 return -EFAULT;
455
456 return 1;
457}
458
459
460
461
462
463
464
465
466
467
468static int wdrtas_temp_open(struct inode *inode, struct file *file)
469{
470 return nonseekable_open(inode, file);
471}
472
473
474
475
476
477
478
479
480
481
482static int wdrtas_temp_close(struct inode *inode, struct file *file)
483{
484 return 0;
485}
486
487
488
489
490
491
492
493
494
495
496
497static int wdrtas_reboot(struct notifier_block *this,
498 unsigned long code, void *ptr)
499{
500 if (code == SYS_DOWN || code == SYS_HALT)
501 wdrtas_timer_stop();
502
503 return NOTIFY_DONE;
504}
505
506
507
508static const struct file_operations wdrtas_fops = {
509 .owner = THIS_MODULE,
510 .llseek = no_llseek,
511 .write = wdrtas_write,
512 .unlocked_ioctl = wdrtas_ioctl,
513 .open = wdrtas_open,
514 .release = wdrtas_close,
515};
516
517static struct miscdevice wdrtas_miscdev = {
518 .minor = WATCHDOG_MINOR,
519 .name = "watchdog",
520 .fops = &wdrtas_fops,
521};
522
523static const struct file_operations wdrtas_temp_fops = {
524 .owner = THIS_MODULE,
525 .llseek = no_llseek,
526 .read = wdrtas_temp_read,
527 .open = wdrtas_temp_open,
528 .release = wdrtas_temp_close,
529};
530
531static struct miscdevice wdrtas_tempdev = {
532 .minor = TEMP_MINOR,
533 .name = "temperature",
534 .fops = &wdrtas_temp_fops,
535};
536
537static struct notifier_block wdrtas_notifier = {
538 .notifier_call = wdrtas_reboot,
539};
540
541
542
543
544
545
546
547
548
549
550static int wdrtas_get_tokens(void)
551{
552 wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
553 if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
554 pr_warn("couldn't get token for get-sensor-state. Trying to continue without temperature support.\n");
555 }
556
557 wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
558 if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
559 pr_warn("couldn't get token for ibm,get-system-parameter. Trying to continue with a default timeout value of %i seconds.\n",
560 WDRTAS_DEFAULT_INTERVAL);
561 }
562
563 wdrtas_token_set_indicator = rtas_token("set-indicator");
564 if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
565 pr_err("couldn't get token for set-indicator. Terminating watchdog code.\n");
566 return -EIO;
567 }
568
569 wdrtas_token_event_scan = rtas_token("event-scan");
570 if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
571 pr_err("couldn't get token for event-scan. Terminating watchdog code.\n");
572 return -EIO;
573 }
574
575 return 0;
576}
577
578
579
580
581
582
583
584static void wdrtas_unregister_devs(void)
585{
586 misc_deregister(&wdrtas_miscdev);
587 if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
588 misc_deregister(&wdrtas_tempdev);
589}
590
591
592
593
594
595
596
597
598
599static int wdrtas_register_devs(void)
600{
601 int result;
602
603 result = misc_register(&wdrtas_miscdev);
604 if (result) {
605 pr_err("couldn't register watchdog misc device. Terminating watchdog code.\n");
606 return result;
607 }
608
609 if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
610 result = misc_register(&wdrtas_tempdev);
611 if (result) {
612 pr_warn("couldn't register watchdog temperature misc device. Continuing without temperature support.\n");
613 wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
614 }
615 }
616
617 return 0;
618}
619
620
621
622
623
624
625
626
627static int __init wdrtas_init(void)
628{
629 if (wdrtas_get_tokens())
630 return -ENODEV;
631
632 if (wdrtas_register_devs())
633 return -ENODEV;
634
635 if (register_reboot_notifier(&wdrtas_notifier)) {
636 pr_err("could not register reboot notifier. Terminating watchdog code.\n");
637 wdrtas_unregister_devs();
638 return -ENODEV;
639 }
640
641 if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
642 wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
643 else
644 wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
645
646 return 0;
647}
648
649
650
651
652
653
654static void __exit wdrtas_exit(void)
655{
656 if (!wdrtas_nowayout)
657 wdrtas_timer_stop();
658
659 wdrtas_unregister_devs();
660
661 unregister_reboot_notifier(&wdrtas_notifier);
662}
663
664module_init(wdrtas_init);
665module_exit(wdrtas_exit);
666