xref: /linux/drivers/s390/char/sclp_ocf.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
27eb9d5beSMartin Schwidefsky /*
37eb9d5beSMartin Schwidefsky  *    SCLP OCF communication parameters sysfs interface
47eb9d5beSMartin Schwidefsky  *
57eb9d5beSMartin Schwidefsky  *    Copyright IBM Corp. 2011
67eb9d5beSMartin Schwidefsky  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
77eb9d5beSMartin Schwidefsky  */
87eb9d5beSMartin Schwidefsky 
97eb9d5beSMartin Schwidefsky #define KMSG_COMPONENT "sclp_ocf"
107eb9d5beSMartin Schwidefsky #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
117eb9d5beSMartin Schwidefsky 
127eb9d5beSMartin Schwidefsky #include <linux/kernel.h>
137eb9d5beSMartin Schwidefsky #include <linux/init.h>
147eb9d5beSMartin Schwidefsky #include <linux/stat.h>
157eb9d5beSMartin Schwidefsky #include <linux/device.h>
167eb9d5beSMartin Schwidefsky #include <linux/string.h>
177eb9d5beSMartin Schwidefsky #include <linux/ctype.h>
187eb9d5beSMartin Schwidefsky #include <linux/kmod.h>
197eb9d5beSMartin Schwidefsky #include <linux/timer.h>
207eb9d5beSMartin Schwidefsky #include <linux/err.h>
217eb9d5beSMartin Schwidefsky #include <asm/ebcdic.h>
227eb9d5beSMartin Schwidefsky #include <asm/sclp.h>
237eb9d5beSMartin Schwidefsky 
247eb9d5beSMartin Schwidefsky #include "sclp.h"
257eb9d5beSMartin Schwidefsky 
267eb9d5beSMartin Schwidefsky #define OCF_LENGTH_HMC_NETWORK 8UL
277eb9d5beSMartin Schwidefsky #define OCF_LENGTH_CPC_NAME 8UL
287eb9d5beSMartin Schwidefsky 
297eb9d5beSMartin Schwidefsky static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
30e435dc31SJanosch Frank static char cpc_name[OCF_LENGTH_CPC_NAME]; /* in EBCDIC */
317eb9d5beSMartin Schwidefsky 
327eb9d5beSMartin Schwidefsky static DEFINE_SPINLOCK(sclp_ocf_lock);
337eb9d5beSMartin Schwidefsky static struct work_struct sclp_ocf_change_work;
347eb9d5beSMartin Schwidefsky 
357eb9d5beSMartin Schwidefsky static struct kset *ocf_kset;
367eb9d5beSMartin Schwidefsky 
377eb9d5beSMartin Schwidefsky static void sclp_ocf_change_notify(struct work_struct *work)
387eb9d5beSMartin Schwidefsky {
397eb9d5beSMartin Schwidefsky 	kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
407eb9d5beSMartin Schwidefsky }
417eb9d5beSMartin Schwidefsky 
427eb9d5beSMartin Schwidefsky /* Handler for OCF event. Look for the CPC image name. */
437eb9d5beSMartin Schwidefsky static void sclp_ocf_handler(struct evbuf_header *evbuf)
447eb9d5beSMartin Schwidefsky {
457eb9d5beSMartin Schwidefsky 	struct gds_vector *v;
467eb9d5beSMartin Schwidefsky 	struct gds_subvector *sv, *netid, *cpc;
477eb9d5beSMartin Schwidefsky 	size_t size;
487eb9d5beSMartin Schwidefsky 
497eb9d5beSMartin Schwidefsky 	/* Find the 0x9f00 block. */
507eb9d5beSMartin Schwidefsky 	v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
517eb9d5beSMartin Schwidefsky 				 0x9f00);
527eb9d5beSMartin Schwidefsky 	if (!v)
537eb9d5beSMartin Schwidefsky 		return;
547eb9d5beSMartin Schwidefsky 	/* Find the 0x9f22 block inside the 0x9f00 block. */
557eb9d5beSMartin Schwidefsky 	v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
567eb9d5beSMartin Schwidefsky 	if (!v)
577eb9d5beSMartin Schwidefsky 		return;
587eb9d5beSMartin Schwidefsky 	/* Find the 0x81 block inside the 0x9f22 block. */
597eb9d5beSMartin Schwidefsky 	sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
607eb9d5beSMartin Schwidefsky 	if (!sv)
617eb9d5beSMartin Schwidefsky 		return;
627eb9d5beSMartin Schwidefsky 	/* Find the 0x01 block inside the 0x81 block. */
637eb9d5beSMartin Schwidefsky 	netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
647eb9d5beSMartin Schwidefsky 	/* Find the 0x02 block inside the 0x81 block. */
657eb9d5beSMartin Schwidefsky 	cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
667eb9d5beSMartin Schwidefsky 	/* Copy network name and cpc name. */
677eb9d5beSMartin Schwidefsky 	spin_lock(&sclp_ocf_lock);
687eb9d5beSMartin Schwidefsky 	if (netid) {
697eb9d5beSMartin Schwidefsky 		size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
707eb9d5beSMartin Schwidefsky 		memcpy(hmc_network, netid + 1, size);
717eb9d5beSMartin Schwidefsky 		EBCASC(hmc_network, size);
727eb9d5beSMartin Schwidefsky 		hmc_network[size] = 0;
737eb9d5beSMartin Schwidefsky 	}
747eb9d5beSMartin Schwidefsky 	if (cpc) {
757eb9d5beSMartin Schwidefsky 		size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
76e435dc31SJanosch Frank 		memset(cpc_name, 0, OCF_LENGTH_CPC_NAME);
777eb9d5beSMartin Schwidefsky 		memcpy(cpc_name, cpc + 1, size);
787eb9d5beSMartin Schwidefsky 	}
797eb9d5beSMartin Schwidefsky 	spin_unlock(&sclp_ocf_lock);
807eb9d5beSMartin Schwidefsky 	schedule_work(&sclp_ocf_change_work);
817eb9d5beSMartin Schwidefsky }
827eb9d5beSMartin Schwidefsky 
837eb9d5beSMartin Schwidefsky static struct sclp_register sclp_ocf_event = {
847eb9d5beSMartin Schwidefsky 	.receive_mask = EVTYP_OCF_MASK,
857eb9d5beSMartin Schwidefsky 	.receiver_fn = sclp_ocf_handler,
867eb9d5beSMartin Schwidefsky };
877eb9d5beSMartin Schwidefsky 
88e435dc31SJanosch Frank void sclp_ocf_cpc_name_copy(char *dst)
89e435dc31SJanosch Frank {
90e435dc31SJanosch Frank 	spin_lock_irq(&sclp_ocf_lock);
91e435dc31SJanosch Frank 	memcpy(dst, cpc_name, OCF_LENGTH_CPC_NAME);
92e435dc31SJanosch Frank 	spin_unlock_irq(&sclp_ocf_lock);
93e435dc31SJanosch Frank }
94e435dc31SJanosch Frank EXPORT_SYMBOL(sclp_ocf_cpc_name_copy);
95e435dc31SJanosch Frank 
967eb9d5beSMartin Schwidefsky static ssize_t cpc_name_show(struct kobject *kobj,
977eb9d5beSMartin Schwidefsky 			     struct kobj_attribute *attr, char *page)
987eb9d5beSMartin Schwidefsky {
99e435dc31SJanosch Frank 	char name[OCF_LENGTH_CPC_NAME + 1];
1007eb9d5beSMartin Schwidefsky 
101e435dc31SJanosch Frank 	sclp_ocf_cpc_name_copy(name);
102e435dc31SJanosch Frank 	name[OCF_LENGTH_CPC_NAME] = 0;
103e435dc31SJanosch Frank 	EBCASC(name, OCF_LENGTH_CPC_NAME);
104e435dc31SJanosch Frank 	return snprintf(page, PAGE_SIZE, "%s\n", name);
1057eb9d5beSMartin Schwidefsky }
1067eb9d5beSMartin Schwidefsky 
1077eb9d5beSMartin Schwidefsky static struct kobj_attribute cpc_name_attr =
1087eb9d5beSMartin Schwidefsky 	__ATTR(cpc_name, 0444, cpc_name_show, NULL);
1097eb9d5beSMartin Schwidefsky 
1107eb9d5beSMartin Schwidefsky static ssize_t hmc_network_show(struct kobject *kobj,
1117eb9d5beSMartin Schwidefsky 				struct kobj_attribute *attr, char *page)
1127eb9d5beSMartin Schwidefsky {
1137eb9d5beSMartin Schwidefsky 	int rc;
1147eb9d5beSMartin Schwidefsky 
1157eb9d5beSMartin Schwidefsky 	spin_lock_irq(&sclp_ocf_lock);
1167eb9d5beSMartin Schwidefsky 	rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
1177eb9d5beSMartin Schwidefsky 	spin_unlock_irq(&sclp_ocf_lock);
1187eb9d5beSMartin Schwidefsky 	return rc;
1197eb9d5beSMartin Schwidefsky }
1207eb9d5beSMartin Schwidefsky 
1217eb9d5beSMartin Schwidefsky static struct kobj_attribute hmc_network_attr =
1227eb9d5beSMartin Schwidefsky 	__ATTR(hmc_network, 0444, hmc_network_show, NULL);
1237eb9d5beSMartin Schwidefsky 
1247eb9d5beSMartin Schwidefsky static struct attribute *ocf_attrs[] = {
1257eb9d5beSMartin Schwidefsky 	&cpc_name_attr.attr,
1267eb9d5beSMartin Schwidefsky 	&hmc_network_attr.attr,
1277eb9d5beSMartin Schwidefsky 	NULL,
1287eb9d5beSMartin Schwidefsky };
1297eb9d5beSMartin Schwidefsky 
1308351378fSArvind Yadav static const struct attribute_group ocf_attr_group = {
1317eb9d5beSMartin Schwidefsky 	.attrs = ocf_attrs,
1327eb9d5beSMartin Schwidefsky };
1337eb9d5beSMartin Schwidefsky 
1347eb9d5beSMartin Schwidefsky static int __init ocf_init(void)
1357eb9d5beSMartin Schwidefsky {
1367eb9d5beSMartin Schwidefsky 	int rc;
1377eb9d5beSMartin Schwidefsky 
1387eb9d5beSMartin Schwidefsky 	INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
1397eb9d5beSMartin Schwidefsky 	ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
1407eb9d5beSMartin Schwidefsky 	if (!ocf_kset)
1417eb9d5beSMartin Schwidefsky 		return -ENOMEM;
1427eb9d5beSMartin Schwidefsky 
1437eb9d5beSMartin Schwidefsky 	rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
1447eb9d5beSMartin Schwidefsky 	if (rc) {
1457eb9d5beSMartin Schwidefsky 		kset_unregister(ocf_kset);
1467eb9d5beSMartin Schwidefsky 		return rc;
1477eb9d5beSMartin Schwidefsky 	}
1487eb9d5beSMartin Schwidefsky 
1497eb9d5beSMartin Schwidefsky 	return sclp_register(&sclp_ocf_event);
1507eb9d5beSMartin Schwidefsky }
1517eb9d5beSMartin Schwidefsky 
1527eb9d5beSMartin Schwidefsky device_initcall(ocf_init);
153