diff options
author | Hendrik Brueckner <brueckner@linux.vnet.ibm.com> | 2009-09-11 04:28:40 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-09-11 04:29:46 -0400 |
commit | 684d2fd48e718e70dad21ef7c528649578147e48 (patch) | |
tree | d830bd35b8871f29998dbeada751e07e310ddac8 /arch | |
parent | 6292b9ef5a4e85d6b782412a85725dd38df24b85 (diff) |
[S390] kernel: Append scpdata to kernel boot command line
Append scpdata to the kernel boot command line. If scpdata starts
with the equal sign (=), the kernel boot command line is replaced.
(For consistency with zIPL and IPL PARM parameters.)
To use scpdata for the kernel boot command line, scpdata must consist
of ascii characters only. If scpdata contains other characters,
scpdata is not appended to the kernel boot command line.
In addition, re-IPL is extended for setting scpdata for the next
Linux reboot.
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/s390/include/asm/ipl.h | 5 | ||||
-rw-r--r-- | arch/s390/include/asm/setup.h | 2 | ||||
-rw-r--r-- | arch/s390/kernel/early.c | 35 | ||||
-rw-r--r-- | arch/s390/kernel/ipl.c | 157 |
4 files changed, 172 insertions, 27 deletions
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h index 1171e6d144a3..5e95d95450b3 100644 --- a/arch/s390/include/asm/ipl.h +++ b/arch/s390/include/asm/ipl.h | |||
@@ -57,6 +57,8 @@ struct ipl_block_fcp { | |||
57 | } __attribute__((packed)); | 57 | } __attribute__((packed)); |
58 | 58 | ||
59 | #define DIAG308_VMPARM_SIZE 64 | 59 | #define DIAG308_VMPARM_SIZE 64 |
60 | #define DIAG308_SCPDATA_SIZE (PAGE_SIZE - (sizeof(struct ipl_list_hdr) + \ | ||
61 | offsetof(struct ipl_block_fcp, scp_data))) | ||
60 | 62 | ||
61 | struct ipl_block_ccw { | 63 | struct ipl_block_ccw { |
62 | u8 load_parm[8]; | 64 | u8 load_parm[8]; |
@@ -91,7 +93,8 @@ extern void do_halt(void); | |||
91 | extern void do_poff(void); | 93 | extern void do_poff(void); |
92 | extern void ipl_save_parameters(void); | 94 | extern void ipl_save_parameters(void); |
93 | extern void ipl_update_parameters(void); | 95 | extern void ipl_update_parameters(void); |
94 | extern void get_ipl_vmparm(char *); | 96 | extern size_t append_ipl_vmparm(char *, size_t); |
97 | extern size_t append_ipl_scpdata(char *, size_t); | ||
95 | 98 | ||
96 | enum { | 99 | enum { |
97 | IPL_DEVNO_VALID = 1, | 100 | IPL_DEVNO_VALID = 1, |
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 38b0fc221ed7..e37478e87286 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h | |||
@@ -8,7 +8,7 @@ | |||
8 | #ifndef _ASM_S390_SETUP_H | 8 | #ifndef _ASM_S390_SETUP_H |
9 | #define _ASM_S390_SETUP_H | 9 | #define _ASM_S390_SETUP_H |
10 | 10 | ||
11 | #define COMMAND_LINE_SIZE 1024 | 11 | #define COMMAND_LINE_SIZE 4096 |
12 | 12 | ||
13 | #define ARCH_COMMAND_LINE_SIZE 896 | 13 | #define ARCH_COMMAND_LINE_SIZE 896 |
14 | 14 | ||
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index cae14c499511..21f3799fe27c 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c | |||
@@ -81,6 +81,8 @@ asm( | |||
81 | " br 14\n" | 81 | " br 14\n" |
82 | " .size savesys_ipl_nss, .-savesys_ipl_nss\n"); | 82 | " .size savesys_ipl_nss, .-savesys_ipl_nss\n"); |
83 | 83 | ||
84 | static __initdata char upper_command_line[COMMAND_LINE_SIZE]; | ||
85 | |||
84 | static noinline __init void create_kernel_nss(void) | 86 | static noinline __init void create_kernel_nss(void) |
85 | { | 87 | { |
86 | unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size; | 88 | unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size; |
@@ -90,7 +92,6 @@ static noinline __init void create_kernel_nss(void) | |||
90 | int response; | 92 | int response; |
91 | size_t len; | 93 | size_t len; |
92 | char *savesys_ptr; | 94 | char *savesys_ptr; |
93 | char upper_command_line[COMMAND_LINE_SIZE]; | ||
94 | char defsys_cmd[DEFSYS_CMD_SIZE]; | 95 | char defsys_cmd[DEFSYS_CMD_SIZE]; |
95 | char savesys_cmd[SAVESYS_CMD_SIZE]; | 96 | char savesys_cmd[SAVESYS_CMD_SIZE]; |
96 | 97 | ||
@@ -367,21 +368,35 @@ static __init void rescue_initrd(void) | |||
367 | } | 368 | } |
368 | 369 | ||
369 | /* Set up boot command line */ | 370 | /* Set up boot command line */ |
370 | static void __init setup_boot_command_line(void) | 371 | static void __init append_to_cmdline(size_t (*ipl_data)(char *, size_t)) |
371 | { | 372 | { |
372 | char *parm = NULL; | 373 | char *parm, *delim; |
374 | size_t rc, len; | ||
375 | |||
376 | len = strlen(boot_command_line); | ||
373 | 377 | ||
378 | delim = boot_command_line + len; /* '\0' character position */ | ||
379 | parm = boot_command_line + len + 1; /* append right after '\0' */ | ||
380 | |||
381 | rc = ipl_data(parm, COMMAND_LINE_SIZE - len - 1); | ||
382 | if (rc) { | ||
383 | if (*parm == '=') | ||
384 | memmove(boot_command_line, parm + 1, rc); | ||
385 | else | ||
386 | *delim = ' '; /* replace '\0' with space */ | ||
387 | } | ||
388 | } | ||
389 | |||
390 | static void __init setup_boot_command_line(void) | ||
391 | { | ||
374 | /* copy arch command line */ | 392 | /* copy arch command line */ |
375 | strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); | 393 | strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); |
376 | 394 | ||
377 | /* append IPL PARM data to the boot command line */ | 395 | /* append IPL PARM data to the boot command line */ |
378 | if (MACHINE_IS_VM) { | 396 | if (MACHINE_IS_VM) |
379 | parm = boot_command_line + strlen(boot_command_line); | 397 | append_to_cmdline(append_ipl_vmparm); |
380 | *parm++ = ' '; | 398 | |
381 | get_ipl_vmparm(parm); | 399 | append_to_cmdline(append_ipl_scpdata); |
382 | if (parm[0] == '=') | ||
383 | memmove(boot_command_line, parm + 1, strlen(parm)); | ||
384 | } | ||
385 | } | 400 | } |
386 | 401 | ||
387 | 402 | ||
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 371a2d88f4ac..04451a5e3c15 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
@@ -272,17 +272,18 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, | |||
272 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); | 272 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); |
273 | 273 | ||
274 | /* VM IPL PARM routines */ | 274 | /* VM IPL PARM routines */ |
275 | static void reipl_get_ascii_vmparm(char *dest, | 275 | size_t reipl_get_ascii_vmparm(char *dest, size_t size, |
276 | const struct ipl_parameter_block *ipb) | 276 | const struct ipl_parameter_block *ipb) |
277 | { | 277 | { |
278 | int i; | 278 | int i; |
279 | int len = 0; | 279 | size_t len; |
280 | char has_lowercase = 0; | 280 | char has_lowercase = 0; |
281 | 281 | ||
282 | len = 0; | ||
282 | if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) && | 283 | if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) && |
283 | (ipb->ipl_info.ccw.vm_parm_len > 0)) { | 284 | (ipb->ipl_info.ccw.vm_parm_len > 0)) { |
284 | 285 | ||
285 | len = ipb->ipl_info.ccw.vm_parm_len; | 286 | len = min_t(size_t, size - 1, ipb->ipl_info.ccw.vm_parm_len); |
286 | memcpy(dest, ipb->ipl_info.ccw.vm_parm, len); | 287 | memcpy(dest, ipb->ipl_info.ccw.vm_parm, len); |
287 | /* If at least one character is lowercase, we assume mixed | 288 | /* If at least one character is lowercase, we assume mixed |
288 | * case; otherwise we convert everything to lowercase. | 289 | * case; otherwise we convert everything to lowercase. |
@@ -299,14 +300,20 @@ static void reipl_get_ascii_vmparm(char *dest, | |||
299 | EBCASC(dest, len); | 300 | EBCASC(dest, len); |
300 | } | 301 | } |
301 | dest[len] = 0; | 302 | dest[len] = 0; |
303 | |||
304 | return len; | ||
302 | } | 305 | } |
303 | 306 | ||
304 | void get_ipl_vmparm(char *dest) | 307 | size_t append_ipl_vmparm(char *dest, size_t size) |
305 | { | 308 | { |
309 | size_t rc; | ||
310 | |||
311 | rc = 0; | ||
306 | if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW)) | 312 | if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW)) |
307 | reipl_get_ascii_vmparm(dest, &ipl_block); | 313 | rc = reipl_get_ascii_vmparm(dest, size, &ipl_block); |
308 | else | 314 | else |
309 | dest[0] = 0; | 315 | dest[0] = 0; |
316 | return rc; | ||
310 | } | 317 | } |
311 | 318 | ||
312 | static ssize_t ipl_vm_parm_show(struct kobject *kobj, | 319 | static ssize_t ipl_vm_parm_show(struct kobject *kobj, |
@@ -314,10 +321,56 @@ static ssize_t ipl_vm_parm_show(struct kobject *kobj, | |||
314 | { | 321 | { |
315 | char parm[DIAG308_VMPARM_SIZE + 1] = {}; | 322 | char parm[DIAG308_VMPARM_SIZE + 1] = {}; |
316 | 323 | ||
317 | get_ipl_vmparm(parm); | 324 | append_ipl_vmparm(parm, sizeof(parm)); |
318 | return sprintf(page, "%s\n", parm); | 325 | return sprintf(page, "%s\n", parm); |
319 | } | 326 | } |
320 | 327 | ||
328 | static size_t scpdata_length(const char* buf, size_t count) | ||
329 | { | ||
330 | while (count) { | ||
331 | if (buf[count - 1] != '\0' && buf[count - 1] != ' ') | ||
332 | break; | ||
333 | count--; | ||
334 | } | ||
335 | return count; | ||
336 | } | ||
337 | |||
338 | size_t reipl_append_ascii_scpdata(char *dest, size_t size, | ||
339 | const struct ipl_parameter_block *ipb) | ||
340 | { | ||
341 | size_t count; | ||
342 | size_t i; | ||
343 | |||
344 | count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data, | ||
345 | ipb->ipl_info.fcp.scp_data_len)); | ||
346 | if (!count) | ||
347 | goto out; | ||
348 | |||
349 | for (i = 0; i < count; i++) | ||
350 | if (!isascii(ipb->ipl_info.fcp.scp_data[i])) { | ||
351 | count = 0; | ||
352 | goto out; | ||
353 | } | ||
354 | |||
355 | memcpy(dest, ipb->ipl_info.fcp.scp_data, count); | ||
356 | out: | ||
357 | dest[count] = '\0'; | ||
358 | return count; | ||
359 | } | ||
360 | |||
361 | size_t append_ipl_scpdata(char *dest, size_t len) | ||
362 | { | ||
363 | size_t rc; | ||
364 | |||
365 | rc = 0; | ||
366 | if (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP) | ||
367 | rc = reipl_append_ascii_scpdata(dest, len, &ipl_block); | ||
368 | else | ||
369 | dest[0] = 0; | ||
370 | return rc; | ||
371 | } | ||
372 | |||
373 | |||
321 | static struct kobj_attribute sys_ipl_vm_parm_attr = | 374 | static struct kobj_attribute sys_ipl_vm_parm_attr = |
322 | __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); | 375 | __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); |
323 | 376 | ||
@@ -553,7 +606,7 @@ static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, | |||
553 | { | 606 | { |
554 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; | 607 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; |
555 | 608 | ||
556 | reipl_get_ascii_vmparm(vmparm, ipb); | 609 | reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); |
557 | return sprintf(page, "%s\n", vmparm); | 610 | return sprintf(page, "%s\n", vmparm); |
558 | } | 611 | } |
559 | 612 | ||
@@ -626,6 +679,59 @@ static struct kobj_attribute sys_reipl_ccw_vmparm_attr = | |||
626 | 679 | ||
627 | /* FCP reipl device attributes */ | 680 | /* FCP reipl device attributes */ |
628 | 681 | ||
682 | static ssize_t reipl_fcp_scpdata_read(struct kobject *kobj, | ||
683 | struct bin_attribute *attr, | ||
684 | char *buf, loff_t off, size_t count) | ||
685 | { | ||
686 | size_t size = reipl_block_fcp->ipl_info.fcp.scp_data_len; | ||
687 | void *scp_data = reipl_block_fcp->ipl_info.fcp.scp_data; | ||
688 | |||
689 | return memory_read_from_buffer(buf, count, &off, scp_data, size); | ||
690 | } | ||
691 | |||
692 | static ssize_t reipl_fcp_scpdata_write(struct kobject *kobj, | ||
693 | struct bin_attribute *attr, | ||
694 | char *buf, loff_t off, size_t count) | ||
695 | { | ||
696 | size_t padding; | ||
697 | size_t scpdata_len; | ||
698 | |||
699 | if (off < 0) | ||
700 | return -EINVAL; | ||
701 | |||
702 | if (off >= DIAG308_SCPDATA_SIZE) | ||
703 | return -ENOSPC; | ||
704 | |||
705 | if (count > DIAG308_SCPDATA_SIZE - off) | ||
706 | count = DIAG308_SCPDATA_SIZE - off; | ||
707 | |||
708 | memcpy(reipl_block_fcp->ipl_info.fcp.scp_data, buf + off, count); | ||
709 | scpdata_len = off + count; | ||
710 | |||
711 | if (scpdata_len % 8) { | ||
712 | padding = 8 - (scpdata_len % 8); | ||
713 | memset(reipl_block_fcp->ipl_info.fcp.scp_data + scpdata_len, | ||
714 | 0, padding); | ||
715 | scpdata_len += padding; | ||
716 | } | ||
717 | |||
718 | reipl_block_fcp->ipl_info.fcp.scp_data_len = scpdata_len; | ||
719 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN + scpdata_len; | ||
720 | reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN + scpdata_len; | ||
721 | |||
722 | return count; | ||
723 | } | ||
724 | |||
725 | static struct bin_attribute sys_reipl_fcp_scp_data_attr = { | ||
726 | .attr = { | ||
727 | .name = "scp_data", | ||
728 | .mode = S_IRUGO | S_IWUSR, | ||
729 | }, | ||
730 | .size = PAGE_SIZE, | ||
731 | .read = reipl_fcp_scpdata_read, | ||
732 | .write = reipl_fcp_scpdata_write, | ||
733 | }; | ||
734 | |||
629 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", | 735 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", |
630 | reipl_block_fcp->ipl_info.fcp.wwpn); | 736 | reipl_block_fcp->ipl_info.fcp.wwpn); |
631 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n", | 737 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n", |
@@ -647,7 +753,6 @@ static struct attribute *reipl_fcp_attrs[] = { | |||
647 | }; | 753 | }; |
648 | 754 | ||
649 | static struct attribute_group reipl_fcp_attr_group = { | 755 | static struct attribute_group reipl_fcp_attr_group = { |
650 | .name = IPL_FCP_STR, | ||
651 | .attrs = reipl_fcp_attrs, | 756 | .attrs = reipl_fcp_attrs, |
652 | }; | 757 | }; |
653 | 758 | ||
@@ -895,6 +1000,7 @@ static struct kobj_attribute reipl_type_attr = | |||
895 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); | 1000 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
896 | 1001 | ||
897 | static struct kset *reipl_kset; | 1002 | static struct kset *reipl_kset; |
1003 | static struct kset *reipl_fcp_kset; | ||
898 | 1004 | ||
899 | static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | 1005 | static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, |
900 | const enum ipl_method m) | 1006 | const enum ipl_method m) |
@@ -906,7 +1012,7 @@ static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | |||
906 | 1012 | ||
907 | reipl_get_ascii_loadparm(loadparm, ipb); | 1013 | reipl_get_ascii_loadparm(loadparm, ipb); |
908 | reipl_get_ascii_nss_name(nss_name, ipb); | 1014 | reipl_get_ascii_nss_name(nss_name, ipb); |
909 | reipl_get_ascii_vmparm(vmparm, ipb); | 1015 | reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); |
910 | 1016 | ||
911 | switch (m) { | 1017 | switch (m) { |
912 | case REIPL_METHOD_CCW_VM: | 1018 | case REIPL_METHOD_CCW_VM: |
@@ -1076,23 +1182,44 @@ static int __init reipl_fcp_init(void) | |||
1076 | int rc; | 1182 | int rc; |
1077 | 1183 | ||
1078 | if (!diag308_set_works) { | 1184 | if (!diag308_set_works) { |
1079 | if (ipl_info.type == IPL_TYPE_FCP) | 1185 | if (ipl_info.type == IPL_TYPE_FCP) { |
1080 | make_attrs_ro(reipl_fcp_attrs); | 1186 | make_attrs_ro(reipl_fcp_attrs); |
1081 | else | 1187 | sys_reipl_fcp_scp_data_attr.attr.mode = S_IRUGO; |
1188 | } else | ||
1082 | return 0; | 1189 | return 0; |
1083 | } | 1190 | } |
1084 | 1191 | ||
1085 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); | 1192 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1086 | if (!reipl_block_fcp) | 1193 | if (!reipl_block_fcp) |
1087 | return -ENOMEM; | 1194 | return -ENOMEM; |
1088 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_fcp_attr_group); | 1195 | |
1196 | /* sysfs: create fcp kset for mixing attr group and bin attrs */ | ||
1197 | reipl_fcp_kset = kset_create_and_add(IPL_FCP_STR, NULL, | ||
1198 | &reipl_kset->kobj); | ||
1199 | if (!reipl_kset) { | ||
1200 | free_page((unsigned long) reipl_block_fcp); | ||
1201 | return -ENOMEM; | ||
1202 | } | ||
1203 | |||
1204 | rc = sysfs_create_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group); | ||
1089 | if (rc) { | 1205 | if (rc) { |
1090 | free_page((unsigned long)reipl_block_fcp); | 1206 | kset_unregister(reipl_fcp_kset); |
1207 | free_page((unsigned long) reipl_block_fcp); | ||
1091 | return rc; | 1208 | return rc; |
1092 | } | 1209 | } |
1093 | if (ipl_info.type == IPL_TYPE_FCP) { | 1210 | |
1211 | rc = sysfs_create_bin_file(&reipl_fcp_kset->kobj, | ||
1212 | &sys_reipl_fcp_scp_data_attr); | ||
1213 | if (rc) { | ||
1214 | sysfs_remove_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group); | ||
1215 | kset_unregister(reipl_fcp_kset); | ||
1216 | free_page((unsigned long) reipl_block_fcp); | ||
1217 | return rc; | ||
1218 | } | ||
1219 | |||
1220 | if (ipl_info.type == IPL_TYPE_FCP) | ||
1094 | memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); | 1221 | memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); |
1095 | } else { | 1222 | else { |
1096 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; | 1223 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; |
1097 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; | 1224 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; |
1098 | reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; | 1225 | reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; |