blob: 60ba1454bfe1513a830c33eef1aca694afd75862 [file] [log] [blame]
Michael Holzheuff6b8ea2006-09-20 15:58:49 +02001/*
2 * arch/s390/kernel/ipl.c
3 * ipl/reipl/dump support for Linux on s390.
4 *
5 * Copyright (C) IBM Corp. 2005,2006
6 * Author(s): Michael Holzheu <holzheu@de.ibm.com>
7 * Heiko Carstens <heiko.carstens@de.ibm.com>
8 * Volker Sameske <sameske@de.ibm.com>
9 */
10
11#include <linux/types.h>
12#include <linux/module.h>
13#include <linux/device.h>
14#include <linux/delay.h>
15#include <linux/reboot.h>
Michael Holzheu03a4d202006-12-04 15:39:58 +010016#include <linux/ctype.h>
Michael Holzheuff6b8ea2006-09-20 15:58:49 +020017#include <asm/smp.h>
18#include <asm/setup.h>
19#include <asm/cpcmd.h>
20#include <asm/cio.h>
Michael Holzheu03a4d202006-12-04 15:39:58 +010021#include <asm/ebcdic.h>
Michael Holzheuff6b8ea2006-09-20 15:58:49 +020022
23#define IPL_PARM_BLOCK_VERSION 0
Michael Holzheu03a4d202006-12-04 15:39:58 +010024#define LOADPARM_LEN 8
25
26extern char s390_readinfo_sccb[];
27#define SCCB_VALID (*((__u16*)&s390_readinfo_sccb[6]) == 0x0010)
28#define SCCB_LOADPARM (&s390_readinfo_sccb[24])
29#define SCCB_FLAG (s390_readinfo_sccb[91])
Michael Holzheuff6b8ea2006-09-20 15:58:49 +020030
31enum ipl_type {
32 IPL_TYPE_NONE = 1,
33 IPL_TYPE_UNKNOWN = 2,
34 IPL_TYPE_CCW = 4,
35 IPL_TYPE_FCP = 8,
36};
37
38#define IPL_NONE_STR "none"
39#define IPL_UNKNOWN_STR "unknown"
40#define IPL_CCW_STR "ccw"
41#define IPL_FCP_STR "fcp"
42
43static char *ipl_type_str(enum ipl_type type)
44{
45 switch (type) {
46 case IPL_TYPE_NONE:
47 return IPL_NONE_STR;
48 case IPL_TYPE_CCW:
49 return IPL_CCW_STR;
50 case IPL_TYPE_FCP:
51 return IPL_FCP_STR;
52 case IPL_TYPE_UNKNOWN:
53 default:
54 return IPL_UNKNOWN_STR;
55 }
56}
57
58enum ipl_method {
59 IPL_METHOD_NONE,
60 IPL_METHOD_CCW_CIO,
61 IPL_METHOD_CCW_DIAG,
62 IPL_METHOD_CCW_VM,
63 IPL_METHOD_FCP_RO_DIAG,
64 IPL_METHOD_FCP_RW_DIAG,
65 IPL_METHOD_FCP_RO_VM,
66};
67
68enum shutdown_action {
69 SHUTDOWN_REIPL,
70 SHUTDOWN_DUMP,
71 SHUTDOWN_STOP,
72};
73
74#define SHUTDOWN_REIPL_STR "reipl"
75#define SHUTDOWN_DUMP_STR "dump"
76#define SHUTDOWN_STOP_STR "stop"
77
78static char *shutdown_action_str(enum shutdown_action action)
79{
80 switch (action) {
81 case SHUTDOWN_REIPL:
82 return SHUTDOWN_REIPL_STR;
83 case SHUTDOWN_DUMP:
84 return SHUTDOWN_DUMP_STR;
85 case SHUTDOWN_STOP:
86 return SHUTDOWN_STOP_STR;
87 default:
88 BUG();
89 }
90}
91
92enum diag308_subcode {
93 DIAG308_IPL = 3,
94 DIAG308_DUMP = 4,
95 DIAG308_SET = 5,
96 DIAG308_STORE = 6,
97};
98
99enum diag308_ipl_type {
100 DIAG308_IPL_TYPE_FCP = 0,
101 DIAG308_IPL_TYPE_CCW = 2,
102};
103
104enum diag308_opt {
105 DIAG308_IPL_OPT_IPL = 0x10,
106 DIAG308_IPL_OPT_DUMP = 0x20,
107};
108
109enum diag308_rc {
110 DIAG308_RC_OK = 1,
111};
112
113static int diag308_set_works = 0;
114
115static int reipl_capabilities = IPL_TYPE_UNKNOWN;
116static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
117static enum ipl_method reipl_method = IPL_METHOD_NONE;
118static struct ipl_parameter_block *reipl_block_fcp;
119static struct ipl_parameter_block *reipl_block_ccw;
120
121static int dump_capabilities = IPL_TYPE_NONE;
122static enum ipl_type dump_type = IPL_TYPE_NONE;
123static enum ipl_method dump_method = IPL_METHOD_NONE;
124static struct ipl_parameter_block *dump_block_fcp;
125static struct ipl_parameter_block *dump_block_ccw;
126
127static enum shutdown_action on_panic_action = SHUTDOWN_STOP;
128
129static int diag308(unsigned long subcode, void *addr)
130{
Martin Schwidefsky94c12cc2006-09-28 16:56:43 +0200131 register unsigned long _addr asm("0") = (unsigned long) addr;
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200132 register unsigned long _rc asm("1") = 0;
133
Martin Schwidefsky94c12cc2006-09-28 16:56:43 +0200134 asm volatile(
135 " diag %0,%2,0x308\n"
136 "0:\n"
137 EX_TABLE(0b,0b)
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200138 : "+d" (_addr), "+d" (_rc)
Martin Schwidefsky94c12cc2006-09-28 16:56:43 +0200139 : "d" (subcode) : "cc", "memory");
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200140 return _rc;
141}
142
143/* SYSFS */
144
145#define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \
146static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys, \
147 char *page) \
148{ \
149 return sprintf(page, _format, _value); \
150} \
151static struct subsys_attribute sys_##_prefix##_##_name##_attr = \
152 __ATTR(_name, S_IRUGO, sys_##_prefix##_##_name##_show, NULL);
153
154#define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \
155static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys, \
156 char *page) \
157{ \
158 return sprintf(page, _fmt_out, \
159 (unsigned long long) _value); \
160} \
161static ssize_t sys_##_prefix##_##_name##_store(struct subsystem *subsys,\
162 const char *buf, size_t len) \
163{ \
164 unsigned long long value; \
165 if (sscanf(buf, _fmt_in, &value) != 1) \
166 return -EINVAL; \
167 _value = value; \
168 return len; \
169} \
170static struct subsys_attribute sys_##_prefix##_##_name##_attr = \
171 __ATTR(_name,(S_IRUGO | S_IWUSR), \
172 sys_##_prefix##_##_name##_show, \
173 sys_##_prefix##_##_name##_store);
174
175static void make_attrs_ro(struct attribute **attrs)
176{
177 while (*attrs) {
178 (*attrs)->mode = S_IRUGO;
179 attrs++;
180 }
181}
182
183/*
184 * ipl section
185 */
186
187static enum ipl_type ipl_get_type(void)
188{
189 struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
190
Heiko Carstense87bfe52006-09-20 15:59:15 +0200191 if (!(ipl_flags & IPL_DEVNO_VALID))
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200192 return IPL_TYPE_UNKNOWN;
Heiko Carstense87bfe52006-09-20 15:59:15 +0200193 if (!(ipl_flags & IPL_PARMBLOCK_VALID))
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200194 return IPL_TYPE_CCW;
195 if (ipl->hdr.version > IPL_MAX_SUPPORTED_VERSION)
196 return IPL_TYPE_UNKNOWN;
197 if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
198 return IPL_TYPE_UNKNOWN;
199 return IPL_TYPE_FCP;
200}
201
202static ssize_t ipl_type_show(struct subsystem *subsys, char *page)
203{
204 return sprintf(page, "%s\n", ipl_type_str(ipl_get_type()));
205}
206
207static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
208
209static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page)
210{
211 struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
212
213 switch (ipl_get_type()) {
214 case IPL_TYPE_CCW:
215 return sprintf(page, "0.0.%04x\n", ipl_devno);
216 case IPL_TYPE_FCP:
217 return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
218 default:
219 return 0;
220 }
221}
222
223static struct subsys_attribute sys_ipl_device_attr =
224 __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL);
225
226static ssize_t ipl_parameter_read(struct kobject *kobj, char *buf, loff_t off,
227 size_t count)
228{
229 unsigned int size = IPL_PARMBLOCK_SIZE;
230
231 if (off > size)
232 return 0;
233 if (off + count > size)
234 count = size - off;
235 memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count);
236 return count;
237}
238
239static struct bin_attribute ipl_parameter_attr = {
240 .attr = {
241 .name = "binary_parameter",
242 .mode = S_IRUGO,
243 .owner = THIS_MODULE,
244 },
245 .size = PAGE_SIZE,
246 .read = &ipl_parameter_read,
247};
248
249static ssize_t ipl_scp_data_read(struct kobject *kobj, char *buf, loff_t off,
250 size_t count)
251{
252 unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len;
253 void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data;
254
255 if (off > size)
256 return 0;
257 if (off + count > size)
258 count = size - off;
259 memcpy(buf, scp_data + off, count);
260 return count;
261}
262
263static struct bin_attribute ipl_scp_data_attr = {
264 .attr = {
265 .name = "scp_data",
266 .mode = S_IRUGO,
267 .owner = THIS_MODULE,
268 },
269 .size = PAGE_SIZE,
270 .read = &ipl_scp_data_read,
271};
272
273/* FCP ipl device attributes */
274
275DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n", (unsigned long long)
276 IPL_PARMBLOCK_START->ipl_info.fcp.wwpn);
277DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n", (unsigned long long)
278 IPL_PARMBLOCK_START->ipl_info.fcp.lun);
279DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n", (unsigned long long)
280 IPL_PARMBLOCK_START->ipl_info.fcp.bootprog);
281DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n", (unsigned long long)
282 IPL_PARMBLOCK_START->ipl_info.fcp.br_lba);
283
284static struct attribute *ipl_fcp_attrs[] = {
285 &sys_ipl_type_attr.attr,
286 &sys_ipl_device_attr.attr,
287 &sys_ipl_fcp_wwpn_attr.attr,
288 &sys_ipl_fcp_lun_attr.attr,
289 &sys_ipl_fcp_bootprog_attr.attr,
290 &sys_ipl_fcp_br_lba_attr.attr,
291 NULL,
292};
293
294static struct attribute_group ipl_fcp_attr_group = {
295 .attrs = ipl_fcp_attrs,
296};
297
298/* CCW ipl device attributes */
299
Michael Holzheu03a4d202006-12-04 15:39:58 +0100300static ssize_t ipl_ccw_loadparm_show(struct subsystem *subsys, char *page)
301{
302 char loadparm[LOADPARM_LEN + 1] = {};
303
304 if (!SCCB_VALID)
305 return sprintf(page, "#unknown#\n");
306 memcpy(loadparm, SCCB_LOADPARM, LOADPARM_LEN);
307 EBCASC(loadparm, LOADPARM_LEN);
308 strstrip(loadparm);
309 return sprintf(page, "%s\n", loadparm);
310}
311
312static struct subsys_attribute sys_ipl_ccw_loadparm_attr =
313 __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL);
314
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200315static struct attribute *ipl_ccw_attrs[] = {
316 &sys_ipl_type_attr.attr,
317 &sys_ipl_device_attr.attr,
Michael Holzheu03a4d202006-12-04 15:39:58 +0100318 &sys_ipl_ccw_loadparm_attr.attr,
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200319 NULL,
320};
321
322static struct attribute_group ipl_ccw_attr_group = {
323 .attrs = ipl_ccw_attrs,
324};
325
326/* UNKNOWN ipl device attributes */
327
328static struct attribute *ipl_unknown_attrs[] = {
329 &sys_ipl_type_attr.attr,
330 NULL,
331};
332
333static struct attribute_group ipl_unknown_attr_group = {
334 .attrs = ipl_unknown_attrs,
335};
336
337static decl_subsys(ipl, NULL, NULL);
338
339/*
340 * reipl section
341 */
342
343/* FCP reipl device attributes */
344
345DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n",
346 reipl_block_fcp->ipl_info.fcp.wwpn);
347DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n",
348 reipl_block_fcp->ipl_info.fcp.lun);
349DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
350 reipl_block_fcp->ipl_info.fcp.bootprog);
351DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n",
352 reipl_block_fcp->ipl_info.fcp.br_lba);
353DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
354 reipl_block_fcp->ipl_info.fcp.devno);
355
356static struct attribute *reipl_fcp_attrs[] = {
357 &sys_reipl_fcp_device_attr.attr,
358 &sys_reipl_fcp_wwpn_attr.attr,
359 &sys_reipl_fcp_lun_attr.attr,
360 &sys_reipl_fcp_bootprog_attr.attr,
361 &sys_reipl_fcp_br_lba_attr.attr,
362 NULL,
363};
364
365static struct attribute_group reipl_fcp_attr_group = {
366 .name = IPL_FCP_STR,
367 .attrs = reipl_fcp_attrs,
368};
369
370/* CCW reipl device attributes */
371
372DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
373 reipl_block_ccw->ipl_info.ccw.devno);
374
Michael Holzheu03a4d202006-12-04 15:39:58 +0100375static void reipl_get_ascii_loadparm(char *loadparm)
376{
377 memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param,
378 LOADPARM_LEN);
379 EBCASC(loadparm, LOADPARM_LEN);
380 loadparm[LOADPARM_LEN] = 0;
381 strstrip(loadparm);
382}
383
384static ssize_t reipl_ccw_loadparm_show(struct subsystem *subsys, char *page)
385{
386 char buf[LOADPARM_LEN + 1];
387
388 reipl_get_ascii_loadparm(buf);
389 return sprintf(page, "%s\n", buf);
390}
391
392static ssize_t reipl_ccw_loadparm_store(struct subsystem *subsys,
393 const char *buf, size_t len)
394{
395 int i, lp_len;
396
397 /* ignore trailing newline */
398 lp_len = len;
399 if ((len > 0) && (buf[len - 1] == '\n'))
400 lp_len--;
401 /* loadparm can have max 8 characters and must not start with a blank */
402 if ((lp_len > LOADPARM_LEN) || ((lp_len > 0) && (buf[0] == ' ')))
403 return -EINVAL;
404 /* loadparm can only contain "a-z,A-Z,0-9,SP,." */
405 for (i = 0; i < lp_len; i++) {
406 if (isalpha(buf[i]) || isdigit(buf[i]) || (buf[i] == ' ') ||
407 (buf[i] == '.'))
408 continue;
409 return -EINVAL;
410 }
411 /* initialize loadparm with blanks */
412 memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN);
413 /* copy and convert to ebcdic */
414 memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len);
415 ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN);
416 return len;
417}
418
419static struct subsys_attribute sys_reipl_ccw_loadparm_attr =
420 __ATTR(loadparm, 0644, reipl_ccw_loadparm_show,
421 reipl_ccw_loadparm_store);
422
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200423static struct attribute *reipl_ccw_attrs[] = {
424 &sys_reipl_ccw_device_attr.attr,
Michael Holzheu03a4d202006-12-04 15:39:58 +0100425 &sys_reipl_ccw_loadparm_attr.attr,
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200426 NULL,
427};
428
429static struct attribute_group reipl_ccw_attr_group = {
430 .name = IPL_CCW_STR,
431 .attrs = reipl_ccw_attrs,
432};
433
434/* reipl type */
435
436static int reipl_set_type(enum ipl_type type)
437{
438 if (!(reipl_capabilities & type))
439 return -EINVAL;
440
441 switch(type) {
442 case IPL_TYPE_CCW:
443 if (MACHINE_IS_VM)
444 reipl_method = IPL_METHOD_CCW_VM;
445 else
446 reipl_method = IPL_METHOD_CCW_CIO;
447 break;
448 case IPL_TYPE_FCP:
449 if (diag308_set_works)
450 reipl_method = IPL_METHOD_FCP_RW_DIAG;
451 else if (MACHINE_IS_VM)
452 reipl_method = IPL_METHOD_FCP_RO_VM;
453 else
454 reipl_method = IPL_METHOD_FCP_RO_DIAG;
455 break;
456 default:
457 reipl_method = IPL_METHOD_NONE;
458 }
459 reipl_type = type;
460 return 0;
461}
462
463static ssize_t reipl_type_show(struct subsystem *subsys, char *page)
464{
465 return sprintf(page, "%s\n", ipl_type_str(reipl_type));
466}
467
468static ssize_t reipl_type_store(struct subsystem *subsys, const char *buf,
469 size_t len)
470{
471 int rc = -EINVAL;
472
473 if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
474 rc = reipl_set_type(IPL_TYPE_CCW);
475 else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
476 rc = reipl_set_type(IPL_TYPE_FCP);
477 return (rc != 0) ? rc : len;
478}
479
480static struct subsys_attribute reipl_type_attr =
481 __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
482
483static decl_subsys(reipl, NULL, NULL);
484
485/*
486 * dump section
487 */
488
489/* FCP dump device attributes */
490
491DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
492 dump_block_fcp->ipl_info.fcp.wwpn);
493DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
494 dump_block_fcp->ipl_info.fcp.lun);
495DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
496 dump_block_fcp->ipl_info.fcp.bootprog);
497DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
498 dump_block_fcp->ipl_info.fcp.br_lba);
499DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
500 dump_block_fcp->ipl_info.fcp.devno);
501
502static struct attribute *dump_fcp_attrs[] = {
503 &sys_dump_fcp_device_attr.attr,
504 &sys_dump_fcp_wwpn_attr.attr,
505 &sys_dump_fcp_lun_attr.attr,
506 &sys_dump_fcp_bootprog_attr.attr,
507 &sys_dump_fcp_br_lba_attr.attr,
508 NULL,
509};
510
511static struct attribute_group dump_fcp_attr_group = {
512 .name = IPL_FCP_STR,
513 .attrs = dump_fcp_attrs,
514};
515
516/* CCW dump device attributes */
517
518DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
519 dump_block_ccw->ipl_info.ccw.devno);
520
521static struct attribute *dump_ccw_attrs[] = {
522 &sys_dump_ccw_device_attr.attr,
523 NULL,
524};
525
526static struct attribute_group dump_ccw_attr_group = {
527 .name = IPL_CCW_STR,
528 .attrs = dump_ccw_attrs,
529};
530
531/* dump type */
532
533static int dump_set_type(enum ipl_type type)
534{
535 if (!(dump_capabilities & type))
536 return -EINVAL;
537 switch(type) {
538 case IPL_TYPE_CCW:
539 if (MACHINE_IS_VM)
540 dump_method = IPL_METHOD_CCW_VM;
541 else
542 dump_method = IPL_METHOD_CCW_CIO;
543 break;
544 case IPL_TYPE_FCP:
545 dump_method = IPL_METHOD_FCP_RW_DIAG;
546 break;
547 default:
548 dump_method = IPL_METHOD_NONE;
549 }
550 dump_type = type;
551 return 0;
552}
553
554static ssize_t dump_type_show(struct subsystem *subsys, char *page)
555{
556 return sprintf(page, "%s\n", ipl_type_str(dump_type));
557}
558
559static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
560 size_t len)
561{
562 int rc = -EINVAL;
563
564 if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0)
565 rc = dump_set_type(IPL_TYPE_NONE);
566 else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
567 rc = dump_set_type(IPL_TYPE_CCW);
568 else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
569 rc = dump_set_type(IPL_TYPE_FCP);
570 return (rc != 0) ? rc : len;
571}
572
573static struct subsys_attribute dump_type_attr =
574 __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
575
576static decl_subsys(dump, NULL, NULL);
577
578#ifdef CONFIG_SMP
579static void dump_smp_stop_all(void)
580{
581 int cpu;
582 preempt_disable();
583 for_each_online_cpu(cpu) {
584 if (cpu == smp_processor_id())
585 continue;
586 while (signal_processor(cpu, sigp_stop) == sigp_busy)
587 udelay(10);
588 }
589 preempt_enable();
590}
591#else
592#define dump_smp_stop_all() do { } while (0)
593#endif
594
595/*
596 * Shutdown actions section
597 */
598
599static decl_subsys(shutdown_actions, NULL, NULL);
600
601/* on panic */
602
603static ssize_t on_panic_show(struct subsystem *subsys, char *page)
604{
605 return sprintf(page, "%s\n", shutdown_action_str(on_panic_action));
606}
607
608static ssize_t on_panic_store(struct subsystem *subsys, const char *buf,
609 size_t len)
610{
611 if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0)
612 on_panic_action = SHUTDOWN_REIPL;
613 else if (strncmp(buf, SHUTDOWN_DUMP_STR,
614 strlen(SHUTDOWN_DUMP_STR)) == 0)
615 on_panic_action = SHUTDOWN_DUMP;
616 else if (strncmp(buf, SHUTDOWN_STOP_STR,
617 strlen(SHUTDOWN_STOP_STR)) == 0)
618 on_panic_action = SHUTDOWN_STOP;
619 else
620 return -EINVAL;
621
622 return len;
623}
624
625static struct subsys_attribute on_panic_attr =
626 __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
627
628static void print_fcp_block(struct ipl_parameter_block *fcp_block)
629{
630 printk(KERN_EMERG "wwpn: %016llx\n",
631 (unsigned long long)fcp_block->ipl_info.fcp.wwpn);
632 printk(KERN_EMERG "lun: %016llx\n",
633 (unsigned long long)fcp_block->ipl_info.fcp.lun);
634 printk(KERN_EMERG "bootprog: %lld\n",
635 (unsigned long long)fcp_block->ipl_info.fcp.bootprog);
636 printk(KERN_EMERG "br_lba: %lld\n",
637 (unsigned long long)fcp_block->ipl_info.fcp.br_lba);
638 printk(KERN_EMERG "device: %llx\n",
639 (unsigned long long)fcp_block->ipl_info.fcp.devno);
640 printk(KERN_EMERG "opt: %x\n", fcp_block->ipl_info.fcp.opt);
641}
642
643void do_reipl(void)
644{
645 struct ccw_dev_id devid;
646 static char buf[100];
Michael Holzheu03a4d202006-12-04 15:39:58 +0100647 char loadparm[LOADPARM_LEN + 1];
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200648
649 switch (reipl_type) {
650 case IPL_TYPE_CCW:
Michael Holzheu03a4d202006-12-04 15:39:58 +0100651 reipl_get_ascii_loadparm(loadparm);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200652 printk(KERN_EMERG "reboot on ccw device: 0.0.%04x\n",
653 reipl_block_ccw->ipl_info.ccw.devno);
Michael Holzheu03a4d202006-12-04 15:39:58 +0100654 printk(KERN_EMERG "loadparm = '%s'\n", loadparm);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200655 break;
656 case IPL_TYPE_FCP:
657 printk(KERN_EMERG "reboot on fcp device:\n");
658 print_fcp_block(reipl_block_fcp);
659 break;
660 default:
661 break;
662 }
663
664 switch (reipl_method) {
665 case IPL_METHOD_CCW_CIO:
666 devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
667 devid.ssid = 0;
668 reipl_ccw_dev(&devid);
669 break;
670 case IPL_METHOD_CCW_VM:
Michael Holzheu03a4d202006-12-04 15:39:58 +0100671 if (strlen(loadparm) == 0)
672 sprintf(buf, "IPL %X",
673 reipl_block_ccw->ipl_info.ccw.devno);
674 else
675 sprintf(buf, "IPL %X LOADPARM '%s'",
676 reipl_block_ccw->ipl_info.ccw.devno, loadparm);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200677 cpcmd(buf, NULL, 0, NULL);
678 break;
679 case IPL_METHOD_CCW_DIAG:
680 diag308(DIAG308_SET, reipl_block_ccw);
681 diag308(DIAG308_IPL, NULL);
682 break;
683 case IPL_METHOD_FCP_RW_DIAG:
684 diag308(DIAG308_SET, reipl_block_fcp);
685 diag308(DIAG308_IPL, NULL);
686 break;
687 case IPL_METHOD_FCP_RO_DIAG:
688 diag308(DIAG308_IPL, NULL);
689 break;
690 case IPL_METHOD_FCP_RO_VM:
691 cpcmd("IPL", NULL, 0, NULL);
692 break;
693 case IPL_METHOD_NONE:
694 default:
695 if (MACHINE_IS_VM)
696 cpcmd("IPL", NULL, 0, NULL);
697 diag308(DIAG308_IPL, NULL);
698 break;
699 }
Michael Holzheu3902e472006-12-04 15:40:05 +0100700 printk(KERN_EMERG "reboot failed!\n");
701 signal_processor(smp_processor_id(), sigp_stop_and_store_status);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200702}
703
704static void do_dump(void)
705{
706 struct ccw_dev_id devid;
707 static char buf[100];
708
709 switch (dump_type) {
710 case IPL_TYPE_CCW:
711 printk(KERN_EMERG "Automatic dump on ccw device: 0.0.%04x\n",
712 dump_block_ccw->ipl_info.ccw.devno);
713 break;
714 case IPL_TYPE_FCP:
715 printk(KERN_EMERG "Automatic dump on fcp device:\n");
716 print_fcp_block(dump_block_fcp);
717 break;
718 default:
719 return;
720 }
721
722 switch (dump_method) {
723 case IPL_METHOD_CCW_CIO:
724 dump_smp_stop_all();
725 devid.devno = dump_block_ccw->ipl_info.ccw.devno;
726 devid.ssid = 0;
727 reipl_ccw_dev(&devid);
728 break;
729 case IPL_METHOD_CCW_VM:
730 dump_smp_stop_all();
731 sprintf(buf, "STORE STATUS");
732 cpcmd(buf, NULL, 0, NULL);
733 sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
734 cpcmd(buf, NULL, 0, NULL);
735 break;
736 case IPL_METHOD_CCW_DIAG:
737 diag308(DIAG308_SET, dump_block_ccw);
738 diag308(DIAG308_DUMP, NULL);
739 break;
740 case IPL_METHOD_FCP_RW_DIAG:
741 diag308(DIAG308_SET, dump_block_fcp);
742 diag308(DIAG308_DUMP, NULL);
743 break;
744 case IPL_METHOD_NONE:
745 default:
746 return;
747 }
748 printk(KERN_EMERG "Dump failed!\n");
749}
750
751/* init functions */
752
753static int __init ipl_register_fcp_files(void)
754{
755 int rc;
756
757 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
758 &ipl_fcp_attr_group);
759 if (rc)
760 goto out;
761 rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
762 &ipl_parameter_attr);
763 if (rc)
764 goto out_ipl_parm;
765 rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
766 &ipl_scp_data_attr);
767 if (!rc)
768 goto out;
769
770 sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr);
771
772out_ipl_parm:
773 sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group);
774out:
775 return rc;
776}
777
778static int __init ipl_init(void)
779{
780 int rc;
781
782 rc = firmware_register(&ipl_subsys);
783 if (rc)
784 return rc;
785 switch (ipl_get_type()) {
786 case IPL_TYPE_CCW:
787 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
788 &ipl_ccw_attr_group);
789 break;
790 case IPL_TYPE_FCP:
791 rc = ipl_register_fcp_files();
792 break;
793 default:
794 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
795 &ipl_unknown_attr_group);
796 break;
797 }
798 if (rc)
799 firmware_unregister(&ipl_subsys);
800 return rc;
801}
802
803static void __init reipl_probe(void)
804{
805 void *buffer;
806
807 buffer = (void *) get_zeroed_page(GFP_KERNEL);
808 if (!buffer)
809 return;
810 if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK)
811 diag308_set_works = 1;
812 free_page((unsigned long)buffer);
813}
814
815static int __init reipl_ccw_init(void)
816{
817 int rc;
818
819 reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
820 if (!reipl_block_ccw)
821 return -ENOMEM;
822 rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_ccw_attr_group);
823 if (rc) {
824 free_page((unsigned long)reipl_block_ccw);
825 return rc;
826 }
827 reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
828 reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
829 reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw);
830 reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
Michael Holzheu03a4d202006-12-04 15:39:58 +0100831 /* check if read scp info worked and set loadparm */
832 if (SCCB_VALID)
833 memcpy(reipl_block_ccw->ipl_info.ccw.load_param,
834 SCCB_LOADPARM, LOADPARM_LEN);
835 else
836 /* read scp info failed: set empty loadparm (EBCDIC blanks) */
837 memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40,
838 LOADPARM_LEN);
839 /* FIXME: check for diag308_set_works when enabling diag ccw reipl */
840 if (!MACHINE_IS_VM)
841 sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200842 if (ipl_get_type() == IPL_TYPE_CCW)
843 reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
844 reipl_capabilities |= IPL_TYPE_CCW;
845 return 0;
846}
847
848static int __init reipl_fcp_init(void)
849{
850 int rc;
851
852 if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP))
853 return 0;
854 if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP))
855 make_attrs_ro(reipl_fcp_attrs);
856
857 reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
858 if (!reipl_block_fcp)
859 return -ENOMEM;
860 rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_fcp_attr_group);
861 if (rc) {
862 free_page((unsigned long)reipl_block_fcp);
863 return rc;
864 }
865 if (ipl_get_type() == IPL_TYPE_FCP) {
866 memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
867 } else {
868 reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
869 reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
870 reipl_block_fcp->hdr.blk0_len =
871 sizeof(reipl_block_fcp->ipl_info.fcp);
872 reipl_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
873 reipl_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_IPL;
874 }
875 reipl_capabilities |= IPL_TYPE_FCP;
876 return 0;
877}
878
879static int __init reipl_init(void)
880{
881 int rc;
882
883 rc = firmware_register(&reipl_subsys);
884 if (rc)
885 return rc;
886 rc = subsys_create_file(&reipl_subsys, &reipl_type_attr);
887 if (rc) {
888 firmware_unregister(&reipl_subsys);
889 return rc;
890 }
891 rc = reipl_ccw_init();
892 if (rc)
893 return rc;
894 rc = reipl_fcp_init();
895 if (rc)
896 return rc;
897 rc = reipl_set_type(ipl_get_type());
898 if (rc)
899 return rc;
900 return 0;
901}
902
903static int __init dump_ccw_init(void)
904{
905 int rc;
906
907 dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
908 if (!dump_block_ccw)
909 return -ENOMEM;
910 rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_ccw_attr_group);
911 if (rc) {
912 free_page((unsigned long)dump_block_ccw);
913 return rc;
914 }
915 dump_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
916 dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
917 dump_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw);
918 dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
919 dump_capabilities |= IPL_TYPE_CCW;
920 return 0;
921}
922
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200923static int __init dump_fcp_init(void)
924{
925 int rc;
926
Michael Holzheu03a4d202006-12-04 15:39:58 +0100927 if(!(SCCB_FLAG & 0x2) || !SCCB_VALID)
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200928 return 0; /* LDIPL DUMP is not installed */
929 if (!diag308_set_works)
930 return 0;
931 dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
932 if (!dump_block_fcp)
933 return -ENOMEM;
934 rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_fcp_attr_group);
935 if (rc) {
936 free_page((unsigned long)dump_block_fcp);
937 return rc;
938 }
939 dump_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
940 dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
941 dump_block_fcp->hdr.blk0_len = sizeof(dump_block_fcp->ipl_info.fcp);
942 dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
943 dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
944 dump_capabilities |= IPL_TYPE_FCP;
945 return 0;
946}
947
948#define SHUTDOWN_ON_PANIC_PRIO 0
949
950static int shutdown_on_panic_notify(struct notifier_block *self,
951 unsigned long event, void *data)
952{
953 if (on_panic_action == SHUTDOWN_DUMP)
954 do_dump();
955 else if (on_panic_action == SHUTDOWN_REIPL)
956 do_reipl();
957 return NOTIFY_OK;
958}
959
960static struct notifier_block shutdown_on_panic_nb = {
961 .notifier_call = shutdown_on_panic_notify,
962 .priority = SHUTDOWN_ON_PANIC_PRIO
963};
964
965static int __init dump_init(void)
966{
967 int rc;
968
969 rc = firmware_register(&dump_subsys);
970 if (rc)
971 return rc;
972 rc = subsys_create_file(&dump_subsys, &dump_type_attr);
973 if (rc) {
974 firmware_unregister(&dump_subsys);
975 return rc;
976 }
977 rc = dump_ccw_init();
978 if (rc)
979 return rc;
980 rc = dump_fcp_init();
981 if (rc)
982 return rc;
983 dump_set_type(IPL_TYPE_NONE);
984 return 0;
985}
986
987static int __init shutdown_actions_init(void)
988{
989 int rc;
990
991 rc = firmware_register(&shutdown_actions_subsys);
992 if (rc)
993 return rc;
994 rc = subsys_create_file(&shutdown_actions_subsys, &on_panic_attr);
995 if (rc) {
996 firmware_unregister(&shutdown_actions_subsys);
997 return rc;
998 }
999 atomic_notifier_chain_register(&panic_notifier_list,
1000 &shutdown_on_panic_nb);
1001 return 0;
1002}
1003
1004static int __init s390_ipl_init(void)
1005{
1006 int rc;
1007
1008 reipl_probe();
1009 rc = ipl_init();
1010 if (rc)
1011 return rc;
1012 rc = reipl_init();
1013 if (rc)
1014 return rc;
1015 rc = dump_init();
1016 if (rc)
1017 return rc;
1018 rc = shutdown_actions_init();
1019 if (rc)
1020 return rc;
1021 return 0;
1022}
1023
1024__initcall(s390_ipl_init);