xref: /OK3568_Linux_fs/kernel/drivers/s390/char/sclp_ocf.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *    SCLP OCF communication parameters sysfs interface
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *    Copyright IBM Corp. 2011
6*4882a593Smuzhiyun  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #define KMSG_COMPONENT "sclp_ocf"
10*4882a593Smuzhiyun #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/stat.h>
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/string.h>
17*4882a593Smuzhiyun #include <linux/ctype.h>
18*4882a593Smuzhiyun #include <linux/kmod.h>
19*4882a593Smuzhiyun #include <linux/timer.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <asm/ebcdic.h>
22*4882a593Smuzhiyun #include <asm/sclp.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include "sclp.h"
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #define OCF_LENGTH_HMC_NETWORK 8UL
27*4882a593Smuzhiyun #define OCF_LENGTH_CPC_NAME 8UL
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
30*4882a593Smuzhiyun static char cpc_name[OCF_LENGTH_CPC_NAME]; /* in EBCDIC */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun static DEFINE_SPINLOCK(sclp_ocf_lock);
33*4882a593Smuzhiyun static struct work_struct sclp_ocf_change_work;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun static struct kset *ocf_kset;
36*4882a593Smuzhiyun 
sclp_ocf_change_notify(struct work_struct * work)37*4882a593Smuzhiyun static void sclp_ocf_change_notify(struct work_struct *work)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /* Handler for OCF event. Look for the CPC image name. */
sclp_ocf_handler(struct evbuf_header * evbuf)43*4882a593Smuzhiyun static void sclp_ocf_handler(struct evbuf_header *evbuf)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	struct gds_vector *v;
46*4882a593Smuzhiyun 	struct gds_subvector *sv, *netid, *cpc;
47*4882a593Smuzhiyun 	size_t size;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	/* Find the 0x9f00 block. */
50*4882a593Smuzhiyun 	v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
51*4882a593Smuzhiyun 				 0x9f00);
52*4882a593Smuzhiyun 	if (!v)
53*4882a593Smuzhiyun 		return;
54*4882a593Smuzhiyun 	/* Find the 0x9f22 block inside the 0x9f00 block. */
55*4882a593Smuzhiyun 	v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
56*4882a593Smuzhiyun 	if (!v)
57*4882a593Smuzhiyun 		return;
58*4882a593Smuzhiyun 	/* Find the 0x81 block inside the 0x9f22 block. */
59*4882a593Smuzhiyun 	sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
60*4882a593Smuzhiyun 	if (!sv)
61*4882a593Smuzhiyun 		return;
62*4882a593Smuzhiyun 	/* Find the 0x01 block inside the 0x81 block. */
63*4882a593Smuzhiyun 	netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
64*4882a593Smuzhiyun 	/* Find the 0x02 block inside the 0x81 block. */
65*4882a593Smuzhiyun 	cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
66*4882a593Smuzhiyun 	/* Copy network name and cpc name. */
67*4882a593Smuzhiyun 	spin_lock(&sclp_ocf_lock);
68*4882a593Smuzhiyun 	if (netid) {
69*4882a593Smuzhiyun 		size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
70*4882a593Smuzhiyun 		memcpy(hmc_network, netid + 1, size);
71*4882a593Smuzhiyun 		EBCASC(hmc_network, size);
72*4882a593Smuzhiyun 		hmc_network[size] = 0;
73*4882a593Smuzhiyun 	}
74*4882a593Smuzhiyun 	if (cpc) {
75*4882a593Smuzhiyun 		size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
76*4882a593Smuzhiyun 		memset(cpc_name, 0, OCF_LENGTH_CPC_NAME);
77*4882a593Smuzhiyun 		memcpy(cpc_name, cpc + 1, size);
78*4882a593Smuzhiyun 	}
79*4882a593Smuzhiyun 	spin_unlock(&sclp_ocf_lock);
80*4882a593Smuzhiyun 	schedule_work(&sclp_ocf_change_work);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun static struct sclp_register sclp_ocf_event = {
84*4882a593Smuzhiyun 	.receive_mask = EVTYP_OCF_MASK,
85*4882a593Smuzhiyun 	.receiver_fn = sclp_ocf_handler,
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun 
sclp_ocf_cpc_name_copy(char * dst)88*4882a593Smuzhiyun void sclp_ocf_cpc_name_copy(char *dst)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	spin_lock_irq(&sclp_ocf_lock);
91*4882a593Smuzhiyun 	memcpy(dst, cpc_name, OCF_LENGTH_CPC_NAME);
92*4882a593Smuzhiyun 	spin_unlock_irq(&sclp_ocf_lock);
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun EXPORT_SYMBOL(sclp_ocf_cpc_name_copy);
95*4882a593Smuzhiyun 
cpc_name_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)96*4882a593Smuzhiyun static ssize_t cpc_name_show(struct kobject *kobj,
97*4882a593Smuzhiyun 			     struct kobj_attribute *attr, char *page)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	char name[OCF_LENGTH_CPC_NAME + 1];
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	sclp_ocf_cpc_name_copy(name);
102*4882a593Smuzhiyun 	name[OCF_LENGTH_CPC_NAME] = 0;
103*4882a593Smuzhiyun 	EBCASC(name, OCF_LENGTH_CPC_NAME);
104*4882a593Smuzhiyun 	return snprintf(page, PAGE_SIZE, "%s\n", name);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun static struct kobj_attribute cpc_name_attr =
108*4882a593Smuzhiyun 	__ATTR(cpc_name, 0444, cpc_name_show, NULL);
109*4882a593Smuzhiyun 
hmc_network_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)110*4882a593Smuzhiyun static ssize_t hmc_network_show(struct kobject *kobj,
111*4882a593Smuzhiyun 				struct kobj_attribute *attr, char *page)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	int rc;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	spin_lock_irq(&sclp_ocf_lock);
116*4882a593Smuzhiyun 	rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
117*4882a593Smuzhiyun 	spin_unlock_irq(&sclp_ocf_lock);
118*4882a593Smuzhiyun 	return rc;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun static struct kobj_attribute hmc_network_attr =
122*4882a593Smuzhiyun 	__ATTR(hmc_network, 0444, hmc_network_show, NULL);
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun static struct attribute *ocf_attrs[] = {
125*4882a593Smuzhiyun 	&cpc_name_attr.attr,
126*4882a593Smuzhiyun 	&hmc_network_attr.attr,
127*4882a593Smuzhiyun 	NULL,
128*4882a593Smuzhiyun };
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static const struct attribute_group ocf_attr_group = {
131*4882a593Smuzhiyun 	.attrs = ocf_attrs,
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun 
ocf_init(void)134*4882a593Smuzhiyun static int __init ocf_init(void)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	int rc;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
139*4882a593Smuzhiyun 	ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
140*4882a593Smuzhiyun 	if (!ocf_kset)
141*4882a593Smuzhiyun 		return -ENOMEM;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
144*4882a593Smuzhiyun 	if (rc) {
145*4882a593Smuzhiyun 		kset_unregister(ocf_kset);
146*4882a593Smuzhiyun 		return rc;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	return sclp_register(&sclp_ocf_event);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun device_initcall(ocf_init);
153