diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 462 |
1 files changed, 383 insertions, 79 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 532542447d66..54b2779b5e2f 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
16 | #include <linux/ctype.h> | 16 | #include <linux/ctype.h> |
17 | #include <linux/fs.h> | ||
17 | #include <asm/ipl.h> | 18 | #include <asm/ipl.h> |
18 | #include <asm/smp.h> | 19 | #include <asm/smp.h> |
19 | #include <asm/setup.h> | 20 | #include <asm/setup.h> |
@@ -22,6 +23,7 @@ | |||
22 | #include <asm/ebcdic.h> | 23 | #include <asm/ebcdic.h> |
23 | #include <asm/reset.h> | 24 | #include <asm/reset.h> |
24 | #include <asm/sclp.h> | 25 | #include <asm/sclp.h> |
26 | #include <asm/setup.h> | ||
25 | 27 | ||
26 | #define IPL_PARM_BLOCK_VERSION 0 | 28 | #define IPL_PARM_BLOCK_VERSION 0 |
27 | 29 | ||
@@ -121,6 +123,7 @@ enum ipl_method { | |||
121 | REIPL_METHOD_FCP_RO_VM, | 123 | REIPL_METHOD_FCP_RO_VM, |
122 | REIPL_METHOD_FCP_DUMP, | 124 | REIPL_METHOD_FCP_DUMP, |
123 | REIPL_METHOD_NSS, | 125 | REIPL_METHOD_NSS, |
126 | REIPL_METHOD_NSS_DIAG, | ||
124 | REIPL_METHOD_DEFAULT, | 127 | REIPL_METHOD_DEFAULT, |
125 | }; | 128 | }; |
126 | 129 | ||
@@ -134,14 +137,15 @@ enum dump_method { | |||
134 | 137 | ||
135 | static int diag308_set_works = 0; | 138 | static int diag308_set_works = 0; |
136 | 139 | ||
140 | static struct ipl_parameter_block ipl_block; | ||
141 | |||
137 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; | 142 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; |
138 | 143 | ||
139 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; | 144 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; |
140 | static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; | 145 | static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; |
141 | static struct ipl_parameter_block *reipl_block_fcp; | 146 | static struct ipl_parameter_block *reipl_block_fcp; |
142 | static struct ipl_parameter_block *reipl_block_ccw; | 147 | static struct ipl_parameter_block *reipl_block_ccw; |
143 | 148 | static struct ipl_parameter_block *reipl_block_nss; | |
144 | static char reipl_nss_name[NSS_NAME_SIZE + 1]; | ||
145 | 149 | ||
146 | static int dump_capabilities = DUMP_TYPE_NONE; | 150 | static int dump_capabilities = DUMP_TYPE_NONE; |
147 | static enum dump_type dump_type = DUMP_TYPE_NONE; | 151 | static enum dump_type dump_type = DUMP_TYPE_NONE; |
@@ -263,6 +267,56 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, | |||
263 | 267 | ||
264 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); | 268 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); |
265 | 269 | ||
270 | /* VM IPL PARM routines */ | ||
271 | static void reipl_get_ascii_vmparm(char *dest, | ||
272 | const struct ipl_parameter_block *ipb) | ||
273 | { | ||
274 | int i; | ||
275 | int len = 0; | ||
276 | char has_lowercase = 0; | ||
277 | |||
278 | if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) && | ||
279 | (ipb->ipl_info.ccw.vm_parm_len > 0)) { | ||
280 | |||
281 | len = ipb->ipl_info.ccw.vm_parm_len; | ||
282 | memcpy(dest, ipb->ipl_info.ccw.vm_parm, len); | ||
283 | /* If at least one character is lowercase, we assume mixed | ||
284 | * case; otherwise we convert everything to lowercase. | ||
285 | */ | ||
286 | for (i = 0; i < len; i++) | ||
287 | if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */ | ||
288 | (dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */ | ||
289 | (dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */ | ||
290 | has_lowercase = 1; | ||
291 | break; | ||
292 | } | ||
293 | if (!has_lowercase) | ||
294 | EBC_TOLOWER(dest, len); | ||
295 | EBCASC(dest, len); | ||
296 | } | ||
297 | dest[len] = 0; | ||
298 | } | ||
299 | |||
300 | void get_ipl_vmparm(char *dest) | ||
301 | { | ||
302 | if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW)) | ||
303 | reipl_get_ascii_vmparm(dest, &ipl_block); | ||
304 | else | ||
305 | dest[0] = 0; | ||
306 | } | ||
307 | |||
308 | static ssize_t ipl_vm_parm_show(struct kobject *kobj, | ||
309 | struct kobj_attribute *attr, char *page) | ||
310 | { | ||
311 | char parm[DIAG308_VMPARM_SIZE + 1] = {}; | ||
312 | |||
313 | get_ipl_vmparm(parm); | ||
314 | return sprintf(page, "%s\n", parm); | ||
315 | } | ||
316 | |||
317 | static struct kobj_attribute sys_ipl_vm_parm_attr = | ||
318 | __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); | ||
319 | |||
266 | static ssize_t sys_ipl_device_show(struct kobject *kobj, | 320 | static ssize_t sys_ipl_device_show(struct kobject *kobj, |
267 | struct kobj_attribute *attr, char *page) | 321 | struct kobj_attribute *attr, char *page) |
268 | { | 322 | { |
@@ -285,14 +339,8 @@ static struct kobj_attribute sys_ipl_device_attr = | |||
285 | static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, | 339 | static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, |
286 | char *buf, loff_t off, size_t count) | 340 | char *buf, loff_t off, size_t count) |
287 | { | 341 | { |
288 | unsigned int size = IPL_PARMBLOCK_SIZE; | 342 | return memory_read_from_buffer(buf, count, &off, IPL_PARMBLOCK_START, |
289 | 343 | IPL_PARMBLOCK_SIZE); | |
290 | if (off > size) | ||
291 | return 0; | ||
292 | if (off + count > size) | ||
293 | count = size - off; | ||
294 | memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count); | ||
295 | return count; | ||
296 | } | 344 | } |
297 | 345 | ||
298 | static struct bin_attribute ipl_parameter_attr = { | 346 | static struct bin_attribute ipl_parameter_attr = { |
@@ -310,12 +358,7 @@ static ssize_t ipl_scp_data_read(struct kobject *kobj, struct bin_attribute *att | |||
310 | unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; | 358 | unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; |
311 | void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; | 359 | void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; |
312 | 360 | ||
313 | if (off > size) | 361 | return memory_read_from_buffer(buf, count, &off, scp_data, size); |
314 | return 0; | ||
315 | if (off + count > size) | ||
316 | count = size - off; | ||
317 | memcpy(buf, scp_data + off, count); | ||
318 | return count; | ||
319 | } | 362 | } |
320 | 363 | ||
321 | static struct bin_attribute ipl_scp_data_attr = { | 364 | static struct bin_attribute ipl_scp_data_attr = { |
@@ -370,15 +413,27 @@ static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj, | |||
370 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = | 413 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = |
371 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); | 414 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); |
372 | 415 | ||
373 | static struct attribute *ipl_ccw_attrs[] = { | 416 | static struct attribute *ipl_ccw_attrs_vm[] = { |
374 | &sys_ipl_type_attr.attr, | 417 | &sys_ipl_type_attr.attr, |
375 | &sys_ipl_device_attr.attr, | 418 | &sys_ipl_device_attr.attr, |
376 | &sys_ipl_ccw_loadparm_attr.attr, | 419 | &sys_ipl_ccw_loadparm_attr.attr, |
420 | &sys_ipl_vm_parm_attr.attr, | ||
377 | NULL, | 421 | NULL, |
378 | }; | 422 | }; |
379 | 423 | ||
380 | static struct attribute_group ipl_ccw_attr_group = { | 424 | static struct attribute *ipl_ccw_attrs_lpar[] = { |
381 | .attrs = ipl_ccw_attrs, | 425 | &sys_ipl_type_attr.attr, |
426 | &sys_ipl_device_attr.attr, | ||
427 | &sys_ipl_ccw_loadparm_attr.attr, | ||
428 | NULL, | ||
429 | }; | ||
430 | |||
431 | static struct attribute_group ipl_ccw_attr_group_vm = { | ||
432 | .attrs = ipl_ccw_attrs_vm, | ||
433 | }; | ||
434 | |||
435 | static struct attribute_group ipl_ccw_attr_group_lpar = { | ||
436 | .attrs = ipl_ccw_attrs_lpar | ||
382 | }; | 437 | }; |
383 | 438 | ||
384 | /* NSS ipl device attributes */ | 439 | /* NSS ipl device attributes */ |
@@ -388,6 +443,8 @@ DEFINE_IPL_ATTR_RO(ipl_nss, name, "%s\n", kernel_nss_name); | |||
388 | static struct attribute *ipl_nss_attrs[] = { | 443 | static struct attribute *ipl_nss_attrs[] = { |
389 | &sys_ipl_type_attr.attr, | 444 | &sys_ipl_type_attr.attr, |
390 | &sys_ipl_nss_name_attr.attr, | 445 | &sys_ipl_nss_name_attr.attr, |
446 | &sys_ipl_ccw_loadparm_attr.attr, | ||
447 | &sys_ipl_vm_parm_attr.attr, | ||
391 | NULL, | 448 | NULL, |
392 | }; | 449 | }; |
393 | 450 | ||
@@ -450,7 +507,12 @@ static int __init ipl_init(void) | |||
450 | } | 507 | } |
451 | switch (ipl_info.type) { | 508 | switch (ipl_info.type) { |
452 | case IPL_TYPE_CCW: | 509 | case IPL_TYPE_CCW: |
453 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group); | 510 | if (MACHINE_IS_VM) |
511 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
512 | &ipl_ccw_attr_group_vm); | ||
513 | else | ||
514 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
515 | &ipl_ccw_attr_group_lpar); | ||
454 | break; | 516 | break; |
455 | case IPL_TYPE_FCP: | 517 | case IPL_TYPE_FCP: |
456 | case IPL_TYPE_FCP_DUMP: | 518 | case IPL_TYPE_FCP_DUMP: |
@@ -481,6 +543,83 @@ static struct shutdown_action __refdata ipl_action = { | |||
481 | * reipl shutdown action: Reboot Linux on shutdown. | 543 | * reipl shutdown action: Reboot Linux on shutdown. |
482 | */ | 544 | */ |
483 | 545 | ||
546 | /* VM IPL PARM attributes */ | ||
547 | static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, | ||
548 | char *page) | ||
549 | { | ||
550 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; | ||
551 | |||
552 | reipl_get_ascii_vmparm(vmparm, ipb); | ||
553 | return sprintf(page, "%s\n", vmparm); | ||
554 | } | ||
555 | |||
556 | static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb, | ||
557 | size_t vmparm_max, | ||
558 | const char *buf, size_t len) | ||
559 | { | ||
560 | int i, ip_len; | ||
561 | |||
562 | /* ignore trailing newline */ | ||
563 | ip_len = len; | ||
564 | if ((len > 0) && (buf[len - 1] == '\n')) | ||
565 | ip_len--; | ||
566 | |||
567 | if (ip_len > vmparm_max) | ||
568 | return -EINVAL; | ||
569 | |||
570 | /* parm is used to store kernel options, check for common chars */ | ||
571 | for (i = 0; i < ip_len; i++) | ||
572 | if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i]))) | ||
573 | return -EINVAL; | ||
574 | |||
575 | memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE); | ||
576 | ipb->ipl_info.ccw.vm_parm_len = ip_len; | ||
577 | if (ip_len > 0) { | ||
578 | ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID; | ||
579 | memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len); | ||
580 | ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len); | ||
581 | } else { | ||
582 | ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID; | ||
583 | } | ||
584 | |||
585 | return len; | ||
586 | } | ||
587 | |||
588 | /* NSS wrapper */ | ||
589 | static ssize_t reipl_nss_vmparm_show(struct kobject *kobj, | ||
590 | struct kobj_attribute *attr, char *page) | ||
591 | { | ||
592 | return reipl_generic_vmparm_show(reipl_block_nss, page); | ||
593 | } | ||
594 | |||
595 | static ssize_t reipl_nss_vmparm_store(struct kobject *kobj, | ||
596 | struct kobj_attribute *attr, | ||
597 | const char *buf, size_t len) | ||
598 | { | ||
599 | return reipl_generic_vmparm_store(reipl_block_nss, 56, buf, len); | ||
600 | } | ||
601 | |||
602 | /* CCW wrapper */ | ||
603 | static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj, | ||
604 | struct kobj_attribute *attr, char *page) | ||
605 | { | ||
606 | return reipl_generic_vmparm_show(reipl_block_ccw, page); | ||
607 | } | ||
608 | |||
609 | static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj, | ||
610 | struct kobj_attribute *attr, | ||
611 | const char *buf, size_t len) | ||
612 | { | ||
613 | return reipl_generic_vmparm_store(reipl_block_ccw, 64, buf, len); | ||
614 | } | ||
615 | |||
616 | static struct kobj_attribute sys_reipl_nss_vmparm_attr = | ||
617 | __ATTR(parm, S_IRUGO | S_IWUSR, reipl_nss_vmparm_show, | ||
618 | reipl_nss_vmparm_store); | ||
619 | static struct kobj_attribute sys_reipl_ccw_vmparm_attr = | ||
620 | __ATTR(parm, S_IRUGO | S_IWUSR, reipl_ccw_vmparm_show, | ||
621 | reipl_ccw_vmparm_store); | ||
622 | |||
484 | /* FCP reipl device attributes */ | 623 | /* FCP reipl device attributes */ |
485 | 624 | ||
486 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", | 625 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", |
@@ -513,27 +652,26 @@ static struct attribute_group reipl_fcp_attr_group = { | |||
513 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | 652 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", |
514 | reipl_block_ccw->ipl_info.ccw.devno); | 653 | reipl_block_ccw->ipl_info.ccw.devno); |
515 | 654 | ||
516 | static void reipl_get_ascii_loadparm(char *loadparm) | 655 | static void reipl_get_ascii_loadparm(char *loadparm, |
656 | struct ipl_parameter_block *ibp) | ||
517 | { | 657 | { |
518 | memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param, | 658 | memcpy(loadparm, ibp->ipl_info.ccw.load_parm, LOADPARM_LEN); |
519 | LOADPARM_LEN); | ||
520 | EBCASC(loadparm, LOADPARM_LEN); | 659 | EBCASC(loadparm, LOADPARM_LEN); |
521 | loadparm[LOADPARM_LEN] = 0; | 660 | loadparm[LOADPARM_LEN] = 0; |
522 | strstrip(loadparm); | 661 | strstrip(loadparm); |
523 | } | 662 | } |
524 | 663 | ||
525 | static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, | 664 | static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb, |
526 | struct kobj_attribute *attr, char *page) | 665 | char *page) |
527 | { | 666 | { |
528 | char buf[LOADPARM_LEN + 1]; | 667 | char buf[LOADPARM_LEN + 1]; |
529 | 668 | ||
530 | reipl_get_ascii_loadparm(buf); | 669 | reipl_get_ascii_loadparm(buf, ipb); |
531 | return sprintf(page, "%s\n", buf); | 670 | return sprintf(page, "%s\n", buf); |
532 | } | 671 | } |
533 | 672 | ||
534 | static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, | 673 | static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, |
535 | struct kobj_attribute *attr, | 674 | const char *buf, size_t len) |
536 | const char *buf, size_t len) | ||
537 | { | 675 | { |
538 | int i, lp_len; | 676 | int i, lp_len; |
539 | 677 | ||
@@ -552,35 +690,128 @@ static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, | |||
552 | return -EINVAL; | 690 | return -EINVAL; |
553 | } | 691 | } |
554 | /* initialize loadparm with blanks */ | 692 | /* initialize loadparm with blanks */ |
555 | memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN); | 693 | memset(ipb->ipl_info.ccw.load_parm, ' ', LOADPARM_LEN); |
556 | /* copy and convert to ebcdic */ | 694 | /* copy and convert to ebcdic */ |
557 | memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len); | 695 | memcpy(ipb->ipl_info.ccw.load_parm, buf, lp_len); |
558 | ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN); | 696 | ASCEBC(ipb->ipl_info.ccw.load_parm, LOADPARM_LEN); |
559 | return len; | 697 | return len; |
560 | } | 698 | } |
561 | 699 | ||
700 | /* NSS wrapper */ | ||
701 | static ssize_t reipl_nss_loadparm_show(struct kobject *kobj, | ||
702 | struct kobj_attribute *attr, char *page) | ||
703 | { | ||
704 | return reipl_generic_loadparm_show(reipl_block_nss, page); | ||
705 | } | ||
706 | |||
707 | static ssize_t reipl_nss_loadparm_store(struct kobject *kobj, | ||
708 | struct kobj_attribute *attr, | ||
709 | const char *buf, size_t len) | ||
710 | { | ||
711 | return reipl_generic_loadparm_store(reipl_block_nss, buf, len); | ||
712 | } | ||
713 | |||
714 | /* CCW wrapper */ | ||
715 | static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, | ||
716 | struct kobj_attribute *attr, char *page) | ||
717 | { | ||
718 | return reipl_generic_loadparm_show(reipl_block_ccw, page); | ||
719 | } | ||
720 | |||
721 | static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, | ||
722 | struct kobj_attribute *attr, | ||
723 | const char *buf, size_t len) | ||
724 | { | ||
725 | return reipl_generic_loadparm_store(reipl_block_ccw, buf, len); | ||
726 | } | ||
727 | |||
562 | static struct kobj_attribute sys_reipl_ccw_loadparm_attr = | 728 | static struct kobj_attribute sys_reipl_ccw_loadparm_attr = |
563 | __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, | 729 | __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_ccw_loadparm_show, |
564 | reipl_ccw_loadparm_store); | 730 | reipl_ccw_loadparm_store); |
565 | 731 | ||
566 | static struct attribute *reipl_ccw_attrs[] = { | 732 | static struct attribute *reipl_ccw_attrs_vm[] = { |
567 | &sys_reipl_ccw_device_attr.attr, | 733 | &sys_reipl_ccw_device_attr.attr, |
568 | &sys_reipl_ccw_loadparm_attr.attr, | 734 | &sys_reipl_ccw_loadparm_attr.attr, |
735 | &sys_reipl_ccw_vmparm_attr.attr, | ||
569 | NULL, | 736 | NULL, |
570 | }; | 737 | }; |
571 | 738 | ||
572 | static struct attribute_group reipl_ccw_attr_group = { | 739 | static struct attribute *reipl_ccw_attrs_lpar[] = { |
740 | &sys_reipl_ccw_device_attr.attr, | ||
741 | &sys_reipl_ccw_loadparm_attr.attr, | ||
742 | NULL, | ||
743 | }; | ||
744 | |||
745 | static struct attribute_group reipl_ccw_attr_group_vm = { | ||
746 | .name = IPL_CCW_STR, | ||
747 | .attrs = reipl_ccw_attrs_vm, | ||
748 | }; | ||
749 | |||
750 | static struct attribute_group reipl_ccw_attr_group_lpar = { | ||
573 | .name = IPL_CCW_STR, | 751 | .name = IPL_CCW_STR, |
574 | .attrs = reipl_ccw_attrs, | 752 | .attrs = reipl_ccw_attrs_lpar, |
575 | }; | 753 | }; |
576 | 754 | ||
577 | 755 | ||
578 | /* NSS reipl device attributes */ | 756 | /* NSS reipl device attributes */ |
757 | static void reipl_get_ascii_nss_name(char *dst, | ||
758 | struct ipl_parameter_block *ipb) | ||
759 | { | ||
760 | memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE); | ||
761 | EBCASC(dst, NSS_NAME_SIZE); | ||
762 | dst[NSS_NAME_SIZE] = 0; | ||
763 | } | ||
764 | |||
765 | static ssize_t reipl_nss_name_show(struct kobject *kobj, | ||
766 | struct kobj_attribute *attr, char *page) | ||
767 | { | ||
768 | char nss_name[NSS_NAME_SIZE + 1] = {}; | ||
579 | 769 | ||
580 | DEFINE_IPL_ATTR_STR_RW(reipl_nss, name, "%s\n", "%s\n", reipl_nss_name); | 770 | reipl_get_ascii_nss_name(nss_name, reipl_block_nss); |
771 | return sprintf(page, "%s\n", nss_name); | ||
772 | } | ||
773 | |||
774 | static ssize_t reipl_nss_name_store(struct kobject *kobj, | ||
775 | struct kobj_attribute *attr, | ||
776 | const char *buf, size_t len) | ||
777 | { | ||
778 | int nss_len; | ||
779 | |||
780 | /* ignore trailing newline */ | ||
781 | nss_len = len; | ||
782 | if ((len > 0) && (buf[len - 1] == '\n')) | ||
783 | nss_len--; | ||
784 | |||
785 | if (nss_len > NSS_NAME_SIZE) | ||
786 | return -EINVAL; | ||
787 | |||
788 | memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE); | ||
789 | if (nss_len > 0) { | ||
790 | reipl_block_nss->ipl_info.ccw.vm_flags |= | ||
791 | DIAG308_VM_FLAGS_NSS_VALID; | ||
792 | memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len); | ||
793 | ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len); | ||
794 | EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len); | ||
795 | } else { | ||
796 | reipl_block_nss->ipl_info.ccw.vm_flags &= | ||
797 | ~DIAG308_VM_FLAGS_NSS_VALID; | ||
798 | } | ||
799 | |||
800 | return len; | ||
801 | } | ||
802 | |||
803 | static struct kobj_attribute sys_reipl_nss_name_attr = | ||
804 | __ATTR(name, S_IRUGO | S_IWUSR, reipl_nss_name_show, | ||
805 | reipl_nss_name_store); | ||
806 | |||
807 | static struct kobj_attribute sys_reipl_nss_loadparm_attr = | ||
808 | __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nss_loadparm_show, | ||
809 | reipl_nss_loadparm_store); | ||
581 | 810 | ||
582 | static struct attribute *reipl_nss_attrs[] = { | 811 | static struct attribute *reipl_nss_attrs[] = { |
583 | &sys_reipl_nss_name_attr.attr, | 812 | &sys_reipl_nss_name_attr.attr, |
813 | &sys_reipl_nss_loadparm_attr.attr, | ||
814 | &sys_reipl_nss_vmparm_attr.attr, | ||
584 | NULL, | 815 | NULL, |
585 | }; | 816 | }; |
586 | 817 | ||
@@ -617,7 +848,10 @@ static int reipl_set_type(enum ipl_type type) | |||
617 | reipl_method = REIPL_METHOD_FCP_DUMP; | 848 | reipl_method = REIPL_METHOD_FCP_DUMP; |
618 | break; | 849 | break; |
619 | case IPL_TYPE_NSS: | 850 | case IPL_TYPE_NSS: |
620 | reipl_method = REIPL_METHOD_NSS; | 851 | if (diag308_set_works) |
852 | reipl_method = REIPL_METHOD_NSS_DIAG; | ||
853 | else | ||
854 | reipl_method = REIPL_METHOD_NSS; | ||
621 | break; | 855 | break; |
622 | case IPL_TYPE_UNKNOWN: | 856 | case IPL_TYPE_UNKNOWN: |
623 | reipl_method = REIPL_METHOD_DEFAULT; | 857 | reipl_method = REIPL_METHOD_DEFAULT; |
@@ -655,11 +889,38 @@ static struct kobj_attribute reipl_type_attr = | |||
655 | 889 | ||
656 | static struct kset *reipl_kset; | 890 | static struct kset *reipl_kset; |
657 | 891 | ||
892 | static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | ||
893 | const enum ipl_method m) | ||
894 | { | ||
895 | char loadparm[LOADPARM_LEN + 1] = {}; | ||
896 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; | ||
897 | char nss_name[NSS_NAME_SIZE + 1] = {}; | ||
898 | size_t pos = 0; | ||
899 | |||
900 | reipl_get_ascii_loadparm(loadparm, ipb); | ||
901 | reipl_get_ascii_nss_name(nss_name, ipb); | ||
902 | reipl_get_ascii_vmparm(vmparm, ipb); | ||
903 | |||
904 | switch (m) { | ||
905 | case REIPL_METHOD_CCW_VM: | ||
906 | pos = sprintf(dst, "IPL %X CLEAR", ipb->ipl_info.ccw.devno); | ||
907 | break; | ||
908 | case REIPL_METHOD_NSS: | ||
909 | pos = sprintf(dst, "IPL %s", nss_name); | ||
910 | break; | ||
911 | default: | ||
912 | break; | ||
913 | } | ||
914 | if (strlen(loadparm) > 0) | ||
915 | pos += sprintf(dst + pos, " LOADPARM '%s'", loadparm); | ||
916 | if (strlen(vmparm) > 0) | ||
917 | sprintf(dst + pos, " PARM %s", vmparm); | ||
918 | } | ||
919 | |||
658 | static void reipl_run(struct shutdown_trigger *trigger) | 920 | static void reipl_run(struct shutdown_trigger *trigger) |
659 | { | 921 | { |
660 | struct ccw_dev_id devid; | 922 | struct ccw_dev_id devid; |
661 | static char buf[100]; | 923 | static char buf[128]; |
662 | char loadparm[LOADPARM_LEN + 1]; | ||
663 | 924 | ||
664 | switch (reipl_method) { | 925 | switch (reipl_method) { |
665 | case REIPL_METHOD_CCW_CIO: | 926 | case REIPL_METHOD_CCW_CIO: |
@@ -668,13 +929,7 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
668 | reipl_ccw_dev(&devid); | 929 | reipl_ccw_dev(&devid); |
669 | break; | 930 | break; |
670 | case REIPL_METHOD_CCW_VM: | 931 | case REIPL_METHOD_CCW_VM: |
671 | reipl_get_ascii_loadparm(loadparm); | 932 | get_ipl_string(buf, reipl_block_ccw, REIPL_METHOD_CCW_VM); |
672 | if (strlen(loadparm) == 0) | ||
673 | sprintf(buf, "IPL %X CLEAR", | ||
674 | reipl_block_ccw->ipl_info.ccw.devno); | ||
675 | else | ||
676 | sprintf(buf, "IPL %X CLEAR LOADPARM '%s'", | ||
677 | reipl_block_ccw->ipl_info.ccw.devno, loadparm); | ||
678 | __cpcmd(buf, NULL, 0, NULL); | 933 | __cpcmd(buf, NULL, 0, NULL); |
679 | break; | 934 | break; |
680 | case REIPL_METHOD_CCW_DIAG: | 935 | case REIPL_METHOD_CCW_DIAG: |
@@ -691,8 +946,12 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
691 | case REIPL_METHOD_FCP_RO_VM: | 946 | case REIPL_METHOD_FCP_RO_VM: |
692 | __cpcmd("IPL", NULL, 0, NULL); | 947 | __cpcmd("IPL", NULL, 0, NULL); |
693 | break; | 948 | break; |
949 | case REIPL_METHOD_NSS_DIAG: | ||
950 | diag308(DIAG308_SET, reipl_block_nss); | ||
951 | diag308(DIAG308_IPL, NULL); | ||
952 | break; | ||
694 | case REIPL_METHOD_NSS: | 953 | case REIPL_METHOD_NSS: |
695 | sprintf(buf, "IPL %s", reipl_nss_name); | 954 | get_ipl_string(buf, reipl_block_nss, REIPL_METHOD_NSS); |
696 | __cpcmd(buf, NULL, 0, NULL); | 955 | __cpcmd(buf, NULL, 0, NULL); |
697 | break; | 956 | break; |
698 | case REIPL_METHOD_DEFAULT: | 957 | case REIPL_METHOD_DEFAULT: |
@@ -707,16 +966,36 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
707 | disabled_wait((unsigned long) __builtin_return_address(0)); | 966 | disabled_wait((unsigned long) __builtin_return_address(0)); |
708 | } | 967 | } |
709 | 968 | ||
710 | static void __init reipl_probe(void) | 969 | static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) |
711 | { | 970 | { |
712 | void *buffer; | 971 | ipb->hdr.len = IPL_PARM_BLK_CCW_LEN; |
972 | ipb->hdr.version = IPL_PARM_BLOCK_VERSION; | ||
973 | ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; | ||
974 | ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW; | ||
975 | } | ||
713 | 976 | ||
714 | buffer = (void *) get_zeroed_page(GFP_KERNEL); | 977 | static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb) |
715 | if (!buffer) | 978 | { |
716 | return; | 979 | /* LOADPARM */ |
717 | if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) | 980 | /* check if read scp info worked and set loadparm */ |
718 | diag308_set_works = 1; | 981 | if (sclp_ipl_info.is_valid) |
719 | free_page((unsigned long)buffer); | 982 | memcpy(ipb->ipl_info.ccw.load_parm, |
983 | &sclp_ipl_info.loadparm, LOADPARM_LEN); | ||
984 | else | ||
985 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | ||
986 | memset(ipb->ipl_info.ccw.load_parm, 0x40, LOADPARM_LEN); | ||
987 | ipb->hdr.flags = DIAG308_FLAGS_LP_VALID; | ||
988 | |||
989 | /* VM PARM */ | ||
990 | if (MACHINE_IS_VM && diag308_set_works && | ||
991 | (ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) { | ||
992 | |||
993 | ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID; | ||
994 | ipb->ipl_info.ccw.vm_parm_len = | ||
995 | ipl_block.ipl_info.ccw.vm_parm_len; | ||
996 | memcpy(ipb->ipl_info.ccw.vm_parm, | ||
997 | ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE); | ||
998 | } | ||
720 | } | 999 | } |
721 | 1000 | ||
722 | static int __init reipl_nss_init(void) | 1001 | static int __init reipl_nss_init(void) |
@@ -725,10 +1004,31 @@ static int __init reipl_nss_init(void) | |||
725 | 1004 | ||
726 | if (!MACHINE_IS_VM) | 1005 | if (!MACHINE_IS_VM) |
727 | return 0; | 1006 | return 0; |
1007 | |||
1008 | reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL); | ||
1009 | if (!reipl_block_nss) | ||
1010 | return -ENOMEM; | ||
1011 | |||
1012 | if (!diag308_set_works) | ||
1013 | sys_reipl_nss_vmparm_attr.attr.mode = S_IRUGO; | ||
1014 | |||
728 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); | 1015 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); |
729 | if (rc) | 1016 | if (rc) |
730 | return rc; | 1017 | return rc; |
731 | strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); | 1018 | |
1019 | reipl_block_ccw_init(reipl_block_nss); | ||
1020 | if (ipl_info.type == IPL_TYPE_NSS) { | ||
1021 | memset(reipl_block_nss->ipl_info.ccw.nss_name, | ||
1022 | ' ', NSS_NAME_SIZE); | ||
1023 | memcpy(reipl_block_nss->ipl_info.ccw.nss_name, | ||
1024 | kernel_nss_name, strlen(kernel_nss_name)); | ||
1025 | ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, NSS_NAME_SIZE); | ||
1026 | reipl_block_nss->ipl_info.ccw.vm_flags |= | ||
1027 | DIAG308_VM_FLAGS_NSS_VALID; | ||
1028 | |||
1029 | reipl_block_ccw_fill_parms(reipl_block_nss); | ||
1030 | } | ||
1031 | |||
732 | reipl_capabilities |= IPL_TYPE_NSS; | 1032 | reipl_capabilities |= IPL_TYPE_NSS; |
733 | return 0; | 1033 | return 0; |
734 | } | 1034 | } |
@@ -740,28 +1040,27 @@ static int __init reipl_ccw_init(void) | |||
740 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); | 1040 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); |
741 | if (!reipl_block_ccw) | 1041 | if (!reipl_block_ccw) |
742 | return -ENOMEM; | 1042 | return -ENOMEM; |
743 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_ccw_attr_group); | 1043 | |
744 | if (rc) { | 1044 | if (MACHINE_IS_VM) { |
745 | free_page((unsigned long)reipl_block_ccw); | 1045 | if (!diag308_set_works) |
746 | return rc; | 1046 | sys_reipl_ccw_vmparm_attr.attr.mode = S_IRUGO; |
1047 | rc = sysfs_create_group(&reipl_kset->kobj, | ||
1048 | &reipl_ccw_attr_group_vm); | ||
1049 | } else { | ||
1050 | if(!diag308_set_works) | ||
1051 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | ||
1052 | rc = sysfs_create_group(&reipl_kset->kobj, | ||
1053 | &reipl_ccw_attr_group_lpar); | ||
747 | } | 1054 | } |
748 | reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN; | 1055 | if (rc) |
749 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | 1056 | return rc; |
750 | reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; | 1057 | |
751 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | 1058 | reipl_block_ccw_init(reipl_block_ccw); |
752 | reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID; | 1059 | if (ipl_info.type == IPL_TYPE_CCW) { |
753 | /* check if read scp info worked and set loadparm */ | ||
754 | if (sclp_ipl_info.is_valid) | ||
755 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, | ||
756 | &sclp_ipl_info.loadparm, LOADPARM_LEN); | ||
757 | else | ||
758 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | ||
759 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, | ||
760 | LOADPARM_LEN); | ||
761 | if (!MACHINE_IS_VM && !diag308_set_works) | ||
762 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | ||
763 | if (ipl_info.type == IPL_TYPE_CCW) | ||
764 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | 1060 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; |
1061 | reipl_block_ccw_fill_parms(reipl_block_ccw); | ||
1062 | } | ||
1063 | |||
765 | reipl_capabilities |= IPL_TYPE_CCW; | 1064 | reipl_capabilities |= IPL_TYPE_CCW; |
766 | return 0; | 1065 | return 0; |
767 | } | 1066 | } |
@@ -1298,7 +1597,6 @@ static void __init shutdown_actions_init(void) | |||
1298 | 1597 | ||
1299 | static int __init s390_ipl_init(void) | 1598 | static int __init s390_ipl_init(void) |
1300 | { | 1599 | { |
1301 | reipl_probe(); | ||
1302 | sclp_get_ipl_info(&sclp_ipl_info); | 1600 | sclp_get_ipl_info(&sclp_ipl_info); |
1303 | shutdown_actions_init(); | 1601 | shutdown_actions_init(); |
1304 | shutdown_triggers_init(); | 1602 | shutdown_triggers_init(); |
@@ -1405,6 +1703,12 @@ void __init setup_ipl(void) | |||
1405 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); | 1703 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); |
1406 | } | 1704 | } |
1407 | 1705 | ||
1706 | void __init ipl_update_parameters(void) | ||
1707 | { | ||
1708 | if (diag308(DIAG308_STORE, &ipl_block) == DIAG308_RC_OK) | ||
1709 | diag308_set_works = 1; | ||
1710 | } | ||
1711 | |||
1408 | void __init ipl_save_parameters(void) | 1712 | void __init ipl_save_parameters(void) |
1409 | { | 1713 | { |
1410 | struct cio_iplinfo iplinfo; | 1714 | struct cio_iplinfo iplinfo; |