diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 157 |
1 files changed, 142 insertions, 15 deletions
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; |