diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 166 |
1 files changed, 151 insertions, 15 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 371a2d88f4ac..ee57a42e6e93 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,65 @@ 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 | int has_lowercase; | ||
344 | |||
345 | count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data, | ||
346 | ipb->ipl_info.fcp.scp_data_len)); | ||
347 | if (!count) | ||
348 | goto out; | ||
349 | |||
350 | has_lowercase = 0; | ||
351 | for (i = 0; i < count; i++) { | ||
352 | if (!isascii(ipb->ipl_info.fcp.scp_data[i])) { | ||
353 | count = 0; | ||
354 | goto out; | ||
355 | } | ||
356 | if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i])) | ||
357 | has_lowercase = 1; | ||
358 | } | ||
359 | |||
360 | if (has_lowercase) | ||
361 | memcpy(dest, ipb->ipl_info.fcp.scp_data, count); | ||
362 | else | ||
363 | for (i = 0; i < count; i++) | ||
364 | dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]); | ||
365 | out: | ||
366 | dest[count] = '\0'; | ||
367 | return count; | ||
368 | } | ||
369 | |||
370 | size_t append_ipl_scpdata(char *dest, size_t len) | ||
371 | { | ||
372 | size_t rc; | ||
373 | |||
374 | rc = 0; | ||
375 | if (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP) | ||
376 | rc = reipl_append_ascii_scpdata(dest, len, &ipl_block); | ||
377 | else | ||
378 | dest[0] = 0; | ||
379 | return rc; | ||
380 | } | ||
381 | |||
382 | |||
321 | static struct kobj_attribute sys_ipl_vm_parm_attr = | 383 | static struct kobj_attribute sys_ipl_vm_parm_attr = |
322 | __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); | 384 | __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); |
323 | 385 | ||
@@ -553,7 +615,7 @@ static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, | |||
553 | { | 615 | { |
554 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; | 616 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; |
555 | 617 | ||
556 | reipl_get_ascii_vmparm(vmparm, ipb); | 618 | reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); |
557 | return sprintf(page, "%s\n", vmparm); | 619 | return sprintf(page, "%s\n", vmparm); |
558 | } | 620 | } |
559 | 621 | ||
@@ -626,6 +688,59 @@ static struct kobj_attribute sys_reipl_ccw_vmparm_attr = | |||
626 | 688 | ||
627 | /* FCP reipl device attributes */ | 689 | /* FCP reipl device attributes */ |
628 | 690 | ||
691 | static ssize_t reipl_fcp_scpdata_read(struct kobject *kobj, | ||
692 | struct bin_attribute *attr, | ||
693 | char *buf, loff_t off, size_t count) | ||
694 | { | ||
695 | size_t size = reipl_block_fcp->ipl_info.fcp.scp_data_len; | ||
696 | void *scp_data = reipl_block_fcp->ipl_info.fcp.scp_data; | ||
697 | |||
698 | return memory_read_from_buffer(buf, count, &off, scp_data, size); | ||
699 | } | ||
700 | |||
701 | static ssize_t reipl_fcp_scpdata_write(struct kobject *kobj, | ||
702 | struct bin_attribute *attr, | ||
703 | char *buf, loff_t off, size_t count) | ||
704 | { | ||
705 | size_t padding; | ||
706 | size_t scpdata_len; | ||
707 | |||
708 | if (off < 0) | ||
709 | return -EINVAL; | ||
710 | |||
711 | if (off >= DIAG308_SCPDATA_SIZE) | ||
712 | return -ENOSPC; | ||
713 | |||
714 | if (count > DIAG308_SCPDATA_SIZE - off) | ||
715 | count = DIAG308_SCPDATA_SIZE - off; | ||
716 | |||
717 | memcpy(reipl_block_fcp->ipl_info.fcp.scp_data, buf + off, count); | ||
718 | scpdata_len = off + count; | ||
719 | |||
720 | if (scpdata_len % 8) { | ||
721 | padding = 8 - (scpdata_len % 8); | ||
722 | memset(reipl_block_fcp->ipl_info.fcp.scp_data + scpdata_len, | ||
723 | 0, padding); | ||
724 | scpdata_len += padding; | ||
725 | } | ||
726 | |||
727 | reipl_block_fcp->ipl_info.fcp.scp_data_len = scpdata_len; | ||
728 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN + scpdata_len; | ||
729 | reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN + scpdata_len; | ||
730 | |||
731 | return count; | ||
732 | } | ||
733 | |||
734 | static struct bin_attribute sys_reipl_fcp_scp_data_attr = { | ||
735 | .attr = { | ||
736 | .name = "scp_data", | ||
737 | .mode = S_IRUGO | S_IWUSR, | ||
738 | }, | ||
739 | .size = PAGE_SIZE, | ||
740 | .read = reipl_fcp_scpdata_read, | ||
741 | .write = reipl_fcp_scpdata_write, | ||
742 | }; | ||
743 | |||
629 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", | 744 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", |
630 | reipl_block_fcp->ipl_info.fcp.wwpn); | 745 | reipl_block_fcp->ipl_info.fcp.wwpn); |
631 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n", | 746 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n", |
@@ -647,7 +762,6 @@ static struct attribute *reipl_fcp_attrs[] = { | |||
647 | }; | 762 | }; |
648 | 763 | ||
649 | static struct attribute_group reipl_fcp_attr_group = { | 764 | static struct attribute_group reipl_fcp_attr_group = { |
650 | .name = IPL_FCP_STR, | ||
651 | .attrs = reipl_fcp_attrs, | 765 | .attrs = reipl_fcp_attrs, |
652 | }; | 766 | }; |
653 | 767 | ||
@@ -895,6 +1009,7 @@ static struct kobj_attribute reipl_type_attr = | |||
895 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); | 1009 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
896 | 1010 | ||
897 | static struct kset *reipl_kset; | 1011 | static struct kset *reipl_kset; |
1012 | static struct kset *reipl_fcp_kset; | ||
898 | 1013 | ||
899 | static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | 1014 | static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, |
900 | const enum ipl_method m) | 1015 | const enum ipl_method m) |
@@ -906,7 +1021,7 @@ static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | |||
906 | 1021 | ||
907 | reipl_get_ascii_loadparm(loadparm, ipb); | 1022 | reipl_get_ascii_loadparm(loadparm, ipb); |
908 | reipl_get_ascii_nss_name(nss_name, ipb); | 1023 | reipl_get_ascii_nss_name(nss_name, ipb); |
909 | reipl_get_ascii_vmparm(vmparm, ipb); | 1024 | reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); |
910 | 1025 | ||
911 | switch (m) { | 1026 | switch (m) { |
912 | case REIPL_METHOD_CCW_VM: | 1027 | case REIPL_METHOD_CCW_VM: |
@@ -1076,23 +1191,44 @@ static int __init reipl_fcp_init(void) | |||
1076 | int rc; | 1191 | int rc; |
1077 | 1192 | ||
1078 | if (!diag308_set_works) { | 1193 | if (!diag308_set_works) { |
1079 | if (ipl_info.type == IPL_TYPE_FCP) | 1194 | if (ipl_info.type == IPL_TYPE_FCP) { |
1080 | make_attrs_ro(reipl_fcp_attrs); | 1195 | make_attrs_ro(reipl_fcp_attrs); |
1081 | else | 1196 | sys_reipl_fcp_scp_data_attr.attr.mode = S_IRUGO; |
1197 | } else | ||
1082 | return 0; | 1198 | return 0; |
1083 | } | 1199 | } |
1084 | 1200 | ||
1085 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); | 1201 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1086 | if (!reipl_block_fcp) | 1202 | if (!reipl_block_fcp) |
1087 | return -ENOMEM; | 1203 | return -ENOMEM; |
1088 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_fcp_attr_group); | 1204 | |
1205 | /* sysfs: create fcp kset for mixing attr group and bin attrs */ | ||
1206 | reipl_fcp_kset = kset_create_and_add(IPL_FCP_STR, NULL, | ||
1207 | &reipl_kset->kobj); | ||
1208 | if (!reipl_kset) { | ||
1209 | free_page((unsigned long) reipl_block_fcp); | ||
1210 | return -ENOMEM; | ||
1211 | } | ||
1212 | |||
1213 | rc = sysfs_create_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group); | ||
1214 | if (rc) { | ||
1215 | kset_unregister(reipl_fcp_kset); | ||
1216 | free_page((unsigned long) reipl_block_fcp); | ||
1217 | return rc; | ||
1218 | } | ||
1219 | |||
1220 | rc = sysfs_create_bin_file(&reipl_fcp_kset->kobj, | ||
1221 | &sys_reipl_fcp_scp_data_attr); | ||
1089 | if (rc) { | 1222 | if (rc) { |
1090 | free_page((unsigned long)reipl_block_fcp); | 1223 | sysfs_remove_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group); |
1224 | kset_unregister(reipl_fcp_kset); | ||
1225 | free_page((unsigned long) reipl_block_fcp); | ||
1091 | return rc; | 1226 | return rc; |
1092 | } | 1227 | } |
1093 | if (ipl_info.type == IPL_TYPE_FCP) { | 1228 | |
1229 | if (ipl_info.type == IPL_TYPE_FCP) | ||
1094 | memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); | 1230 | memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); |
1095 | } else { | 1231 | else { |
1096 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; | 1232 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; |
1097 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; | 1233 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; |
1098 | reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; | 1234 | reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; |