blob: ec127826f2219338e424190c76cbf56dcbb69731 [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;
Michael Holzheu5986b0e2006-12-04 15:40:13 +0100667 if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno)
668 diag308(DIAG308_IPL, NULL);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200669 devid.ssid = 0;
670 reipl_ccw_dev(&devid);
671 break;
672 case IPL_METHOD_CCW_VM:
Michael Holzheu03a4d202006-12-04 15:39:58 +0100673 if (strlen(loadparm) == 0)
674 sprintf(buf, "IPL %X",
675 reipl_block_ccw->ipl_info.ccw.devno);
676 else
677 sprintf(buf, "IPL %X LOADPARM '%s'",
678 reipl_block_ccw->ipl_info.ccw.devno, loadparm);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200679 cpcmd(buf, NULL, 0, NULL);
680 break;
681 case IPL_METHOD_CCW_DIAG:
682 diag308(DIAG308_SET, reipl_block_ccw);
683 diag308(DIAG308_IPL, NULL);
684 break;
685 case IPL_METHOD_FCP_RW_DIAG:
686 diag308(DIAG308_SET, reipl_block_fcp);
687 diag308(DIAG308_IPL, NULL);
688 break;
689 case IPL_METHOD_FCP_RO_DIAG:
690 diag308(DIAG308_IPL, NULL);
691 break;
692 case IPL_METHOD_FCP_RO_VM:
693 cpcmd("IPL", NULL, 0, NULL);
694 break;
695 case IPL_METHOD_NONE:
696 default:
697 if (MACHINE_IS_VM)
698 cpcmd("IPL", NULL, 0, NULL);
699 diag308(DIAG308_IPL, NULL);
700 break;
701 }
Michael Holzheu3902e472006-12-04 15:40:05 +0100702 printk(KERN_EMERG "reboot failed!\n");
703 signal_processor(smp_processor_id(), sigp_stop_and_store_status);
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200704}
705
706static void do_dump(void)
707{
708 struct ccw_dev_id devid;
709 static char buf[100];
710
711 switch (dump_type) {
712 case IPL_TYPE_CCW:
713 printk(KERN_EMERG "Automatic dump on ccw device: 0.0.%04x\n",
714 dump_block_ccw->ipl_info.ccw.devno);
715 break;
716 case IPL_TYPE_FCP:
717 printk(KERN_EMERG "Automatic dump on fcp device:\n");
718 print_fcp_block(dump_block_fcp);
719 break;
720 default:
721 return;
722 }
723
724 switch (dump_method) {
725 case IPL_METHOD_CCW_CIO:
726 dump_smp_stop_all();
727 devid.devno = dump_block_ccw->ipl_info.ccw.devno;
728 devid.ssid = 0;
729 reipl_ccw_dev(&devid);
730 break;
731 case IPL_METHOD_CCW_VM:
732 dump_smp_stop_all();
733 sprintf(buf, "STORE STATUS");
734 cpcmd(buf, NULL, 0, NULL);
735 sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
736 cpcmd(buf, NULL, 0, NULL);
737 break;
738 case IPL_METHOD_CCW_DIAG:
739 diag308(DIAG308_SET, dump_block_ccw);
740 diag308(DIAG308_DUMP, NULL);
741 break;
742 case IPL_METHOD_FCP_RW_DIAG:
743 diag308(DIAG308_SET, dump_block_fcp);
744 diag308(DIAG308_DUMP, NULL);
745 break;
746 case IPL_METHOD_NONE:
747 default:
748 return;
749 }
750 printk(KERN_EMERG "Dump failed!\n");
751}
752
753/* init functions */
754
755static int __init ipl_register_fcp_files(void)
756{
757 int rc;
758
759 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
760 &ipl_fcp_attr_group);
761 if (rc)
762 goto out;
763 rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
764 &ipl_parameter_attr);
765 if (rc)
766 goto out_ipl_parm;
767 rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
768 &ipl_scp_data_attr);
769 if (!rc)
770 goto out;
771
772 sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr);
773
774out_ipl_parm:
775 sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group);
776out:
777 return rc;
778}
779
780static int __init ipl_init(void)
781{
782 int rc;
783
784 rc = firmware_register(&ipl_subsys);
785 if (rc)
786 return rc;
787 switch (ipl_get_type()) {
788 case IPL_TYPE_CCW:
789 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
790 &ipl_ccw_attr_group);
791 break;
792 case IPL_TYPE_FCP:
793 rc = ipl_register_fcp_files();
794 break;
795 default:
796 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
797 &ipl_unknown_attr_group);
798 break;
799 }
800 if (rc)
801 firmware_unregister(&ipl_subsys);
802 return rc;
803}
804
805static void __init reipl_probe(void)
806{
807 void *buffer;
808
809 buffer = (void *) get_zeroed_page(GFP_KERNEL);
810 if (!buffer)
811 return;
812 if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK)
813 diag308_set_works = 1;
814 free_page((unsigned long)buffer);
815}
816
817static int __init reipl_ccw_init(void)
818{
819 int rc;
820
821 reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
822 if (!reipl_block_ccw)
823 return -ENOMEM;
824 rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_ccw_attr_group);
825 if (rc) {
826 free_page((unsigned long)reipl_block_ccw);
827 return rc;
828 }
829 reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
830 reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
831 reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw);
832 reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
Michael Holzheu03a4d202006-12-04 15:39:58 +0100833 /* check if read scp info worked and set loadparm */
834 if (SCCB_VALID)
835 memcpy(reipl_block_ccw->ipl_info.ccw.load_param,
836 SCCB_LOADPARM, LOADPARM_LEN);
837 else
838 /* read scp info failed: set empty loadparm (EBCDIC blanks) */
839 memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40,
840 LOADPARM_LEN);
841 /* FIXME: check for diag308_set_works when enabling diag ccw reipl */
842 if (!MACHINE_IS_VM)
843 sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200844 if (ipl_get_type() == IPL_TYPE_CCW)
845 reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
846 reipl_capabilities |= IPL_TYPE_CCW;
847 return 0;
848}
849
850static int __init reipl_fcp_init(void)
851{
852 int rc;
853
854 if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP))
855 return 0;
856 if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP))
857 make_attrs_ro(reipl_fcp_attrs);
858
859 reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
860 if (!reipl_block_fcp)
861 return -ENOMEM;
862 rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_fcp_attr_group);
863 if (rc) {
864 free_page((unsigned long)reipl_block_fcp);
865 return rc;
866 }
867 if (ipl_get_type() == IPL_TYPE_FCP) {
868 memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
869 } else {
870 reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
871 reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
872 reipl_block_fcp->hdr.blk0_len =
873 sizeof(reipl_block_fcp->ipl_info.fcp);
874 reipl_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
875 reipl_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_IPL;
876 }
877 reipl_capabilities |= IPL_TYPE_FCP;
878 return 0;
879}
880
881static int __init reipl_init(void)
882{
883 int rc;
884
885 rc = firmware_register(&reipl_subsys);
886 if (rc)
887 return rc;
888 rc = subsys_create_file(&reipl_subsys, &reipl_type_attr);
889 if (rc) {
890 firmware_unregister(&reipl_subsys);
891 return rc;
892 }
893 rc = reipl_ccw_init();
894 if (rc)
895 return rc;
896 rc = reipl_fcp_init();
897 if (rc)
898 return rc;
899 rc = reipl_set_type(ipl_get_type());
900 if (rc)
901 return rc;
902 return 0;
903}
904
905static int __init dump_ccw_init(void)
906{
907 int rc;
908
909 dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
910 if (!dump_block_ccw)
911 return -ENOMEM;
912 rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_ccw_attr_group);
913 if (rc) {
914 free_page((unsigned long)dump_block_ccw);
915 return rc;
916 }
917 dump_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
918 dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
919 dump_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw);
920 dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
921 dump_capabilities |= IPL_TYPE_CCW;
922 return 0;
923}
924
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200925static int __init dump_fcp_init(void)
926{
927 int rc;
928
Michael Holzheu03a4d202006-12-04 15:39:58 +0100929 if(!(SCCB_FLAG & 0x2) || !SCCB_VALID)
Michael Holzheuff6b8ea2006-09-20 15:58:49 +0200930 return 0; /* LDIPL DUMP is not installed */
931 if (!diag308_set_works)
932 return 0;
933 dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
934 if (!dump_block_fcp)
935 return -ENOMEM;
936 rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_fcp_attr_group);
937 if (rc) {
938 free_page((unsigned long)dump_block_fcp);
939 return rc;
940 }
941 dump_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
942 dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
943 dump_block_fcp->hdr.blk0_len = sizeof(dump_block_fcp->ipl_info.fcp);
944 dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
945 dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
946 dump_capabilities |= IPL_TYPE_FCP;
947 return 0;
948}
949
950#define SHUTDOWN_ON_PANIC_PRIO 0
951
952static int shutdown_on_panic_notify(struct notifier_block *self,
953 unsigned long event, void *data)
954{
955 if (on_panic_action == SHUTDOWN_DUMP)
956 do_dump();
957 else if (on_panic_action == SHUTDOWN_REIPL)
958 do_reipl();
959 return NOTIFY_OK;
960}
961
962static struct notifier_block shutdown_on_panic_nb = {
963 .notifier_call = shutdown_on_panic_notify,
964 .priority = SHUTDOWN_ON_PANIC_PRIO
965};
966
967static int __init dump_init(void)
968{
969 int rc;
970
971 rc = firmware_register(&dump_subsys);
972 if (rc)
973 return rc;
974 rc = subsys_create_file(&dump_subsys, &dump_type_attr);
975 if (rc) {
976 firmware_unregister(&dump_subsys);
977 return rc;
978 }
979 rc = dump_ccw_init();
980 if (rc)
981 return rc;
982 rc = dump_fcp_init();
983 if (rc)
984 return rc;
985 dump_set_type(IPL_TYPE_NONE);
986 return 0;
987}
988
989static int __init shutdown_actions_init(void)
990{
991 int rc;
992
993 rc = firmware_register(&shutdown_actions_subsys);
994 if (rc)
995 return rc;
996 rc = subsys_create_file(&shutdown_actions_subsys, &on_panic_attr);
997 if (rc) {
998 firmware_unregister(&shutdown_actions_subsys);
999 return rc;
1000 }
1001 atomic_notifier_chain_register(&panic_notifier_list,
1002 &shutdown_on_panic_nb);
1003 return 0;
1004}
1005
1006static int __init s390_ipl_init(void)
1007{
1008 int rc;
1009
1010 reipl_probe();
1011 rc = ipl_init();
1012 if (rc)
1013 return rc;
1014 rc = reipl_init();
1015 if (rc)
1016 return rc;
1017 rc = dump_init();
1018 if (rc)
1019 return rc;
1020 rc = shutdown_actions_init();
1021 if (rc)
1022 return rc;
1023 return 0;
1024}
1025
1026__initcall(s390_ipl_init);