summaryrefslogtreecommitdiff
path: root/drivers/s390/char
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-05-23 12:24:43 +0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-05-23 12:24:32 +0400
commit7eb9d5bec552eff896ebf079386dc47e9dc2fc89 (patch)
tree2ccffd0b2426759008ba1221fd081295e7830e6b /drivers/s390/char
parent30c2df51173ea4e4755ad52be7f2914f01e32404 (diff)
downloadlinux-7eb9d5bec552eff896ebf079386dc47e9dc2fc89.tar.xz
[S390] get CPC image name
Provide sysfs attributes that contain the CPC name and the HMC network name of the machine the operating system is running on. This information is retrieved with the operation communication parameters (OCF) sclp interface. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/char')
-rw-r--r--drivers/s390/char/Makefile2
-rw-r--r--drivers/s390/char/sclp.h2
-rw-r--r--drivers/s390/char/sclp_ocf.c145
3 files changed, 148 insertions, 1 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index af01b5abd069..f3c325207445 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -3,7 +3,7 @@
#
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
- sclp_cmd.o sclp_config.o sclp_cpi_sys.o
+ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o
obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index bc23b05bfe7d..49a1bb52bc87 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -28,6 +28,7 @@
#define EVTYP_CONFMGMDATA 0x04
#define EVTYP_SDIAS 0x1C
#define EVTYP_ASYNC 0x0A
+#define EVTYP_OCF 0x1E
#define EVTYP_OPCMD_MASK 0x80000000
#define EVTYP_MSG_MASK 0x40000000
@@ -40,6 +41,7 @@
#define EVTYP_CONFMGMDATA_MASK 0x10000000
#define EVTYP_SDIAS_MASK 0x00000010
#define EVTYP_ASYNC_MASK 0x00400000
+#define EVTYP_OCF_MASK 0x00000004
#define GNRLMSGFLGS_DOM 0x8000
#define GNRLMSGFLGS_SNDALRM 0x4000
diff --git a/drivers/s390/char/sclp_ocf.c b/drivers/s390/char/sclp_ocf.c
new file mode 100644
index 000000000000..ab294d5a534e
--- /dev/null
+++ b/drivers/s390/char/sclp_ocf.c
@@ -0,0 +1,145 @@
+/*
+ * drivers/s390/char/sclp_ocf.c
+ * SCLP OCF communication parameters sysfs interface
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "sclp_ocf"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <asm/ebcdic.h>
+#include <asm/sclp.h>
+
+#include "sclp.h"
+
+#define OCF_LENGTH_HMC_NETWORK 8UL
+#define OCF_LENGTH_CPC_NAME 8UL
+
+static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
+static char cpc_name[OCF_LENGTH_CPC_NAME + 1];
+
+static DEFINE_SPINLOCK(sclp_ocf_lock);
+static struct work_struct sclp_ocf_change_work;
+
+static struct kset *ocf_kset;
+
+static void sclp_ocf_change_notify(struct work_struct *work)
+{
+ kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
+}
+
+/* Handler for OCF event. Look for the CPC image name. */
+static void sclp_ocf_handler(struct evbuf_header *evbuf)
+{
+ struct gds_vector *v;
+ struct gds_subvector *sv, *netid, *cpc;
+ size_t size;
+
+ /* Find the 0x9f00 block. */
+ v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
+ 0x9f00);
+ if (!v)
+ return;
+ /* Find the 0x9f22 block inside the 0x9f00 block. */
+ v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
+ if (!v)
+ return;
+ /* Find the 0x81 block inside the 0x9f22 block. */
+ sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
+ if (!sv)
+ return;
+ /* Find the 0x01 block inside the 0x81 block. */
+ netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
+ /* Find the 0x02 block inside the 0x81 block. */
+ cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
+ /* Copy network name and cpc name. */
+ spin_lock(&sclp_ocf_lock);
+ if (netid) {
+ size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
+ memcpy(hmc_network, netid + 1, size);
+ EBCASC(hmc_network, size);
+ hmc_network[size] = 0;
+ }
+ if (cpc) {
+ size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
+ memcpy(cpc_name, cpc + 1, size);
+ EBCASC(cpc_name, size);
+ cpc_name[size] = 0;
+ }
+ spin_unlock(&sclp_ocf_lock);
+ schedule_work(&sclp_ocf_change_work);
+}
+
+static struct sclp_register sclp_ocf_event = {
+ .receive_mask = EVTYP_OCF_MASK,
+ .receiver_fn = sclp_ocf_handler,
+};
+
+static ssize_t cpc_name_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ int rc;
+
+ spin_lock_irq(&sclp_ocf_lock);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", cpc_name);
+ spin_unlock_irq(&sclp_ocf_lock);
+ return rc;
+}
+
+static struct kobj_attribute cpc_name_attr =
+ __ATTR(cpc_name, 0444, cpc_name_show, NULL);
+
+static ssize_t hmc_network_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ int rc;
+
+ spin_lock_irq(&sclp_ocf_lock);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
+ spin_unlock_irq(&sclp_ocf_lock);
+ return rc;
+}
+
+static struct kobj_attribute hmc_network_attr =
+ __ATTR(hmc_network, 0444, hmc_network_show, NULL);
+
+static struct attribute *ocf_attrs[] = {
+ &cpc_name_attr.attr,
+ &hmc_network_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ocf_attr_group = {
+ .attrs = ocf_attrs,
+};
+
+static int __init ocf_init(void)
+{
+ int rc;
+
+ INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
+ ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
+ if (!ocf_kset)
+ return -ENOMEM;
+
+ rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
+ if (rc) {
+ kset_unregister(ocf_kset);
+ return rc;
+ }
+
+ return sclp_register(&sclp_ocf_event);
+}
+
+device_initcall(ocf_init);