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