162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCLP OCF communication parameters sysfs interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2011 662306a36Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define KMSG_COMPONENT "sclp_ocf" 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/stat.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/ctype.h> 1862306a36Sopenharmony_ci#include <linux/kmod.h> 1962306a36Sopenharmony_ci#include <linux/timer.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <asm/ebcdic.h> 2262306a36Sopenharmony_ci#include <asm/sclp.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "sclp.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define OCF_LENGTH_HMC_NETWORK 8UL 2762306a36Sopenharmony_ci#define OCF_LENGTH_CPC_NAME 8UL 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic char hmc_network[OCF_LENGTH_HMC_NETWORK + 1]; 3062306a36Sopenharmony_cistatic char cpc_name[OCF_LENGTH_CPC_NAME]; /* in EBCDIC */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sclp_ocf_lock); 3362306a36Sopenharmony_cistatic struct work_struct sclp_ocf_change_work; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct kset *ocf_kset; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void sclp_ocf_change_notify(struct work_struct *work) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Handler for OCF event. Look for the CPC image name. */ 4362306a36Sopenharmony_cistatic void sclp_ocf_handler(struct evbuf_header *evbuf) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct gds_vector *v; 4662306a36Sopenharmony_ci struct gds_subvector *sv, *netid, *cpc; 4762306a36Sopenharmony_ci size_t size; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Find the 0x9f00 block. */ 5062306a36Sopenharmony_ci v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length, 5162306a36Sopenharmony_ci 0x9f00); 5262306a36Sopenharmony_ci if (!v) 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci /* Find the 0x9f22 block inside the 0x9f00 block. */ 5562306a36Sopenharmony_ci v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22); 5662306a36Sopenharmony_ci if (!v) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci /* Find the 0x81 block inside the 0x9f22 block. */ 5962306a36Sopenharmony_ci sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81); 6062306a36Sopenharmony_ci if (!sv) 6162306a36Sopenharmony_ci return; 6262306a36Sopenharmony_ci /* Find the 0x01 block inside the 0x81 block. */ 6362306a36Sopenharmony_ci netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1); 6462306a36Sopenharmony_ci /* Find the 0x02 block inside the 0x81 block. */ 6562306a36Sopenharmony_ci cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2); 6662306a36Sopenharmony_ci /* Copy network name and cpc name. */ 6762306a36Sopenharmony_ci spin_lock(&sclp_ocf_lock); 6862306a36Sopenharmony_ci if (netid) { 6962306a36Sopenharmony_ci size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length); 7062306a36Sopenharmony_ci memcpy(hmc_network, netid + 1, size); 7162306a36Sopenharmony_ci EBCASC(hmc_network, size); 7262306a36Sopenharmony_ci hmc_network[size] = 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci if (cpc) { 7562306a36Sopenharmony_ci size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length); 7662306a36Sopenharmony_ci memset(cpc_name, 0, OCF_LENGTH_CPC_NAME); 7762306a36Sopenharmony_ci memcpy(cpc_name, cpc + 1, size); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci spin_unlock(&sclp_ocf_lock); 8062306a36Sopenharmony_ci schedule_work(&sclp_ocf_change_work); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct sclp_register sclp_ocf_event = { 8462306a36Sopenharmony_ci .receive_mask = EVTYP_OCF_MASK, 8562306a36Sopenharmony_ci .receiver_fn = sclp_ocf_handler, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_civoid sclp_ocf_cpc_name_copy(char *dst) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci spin_lock_irq(&sclp_ocf_lock); 9162306a36Sopenharmony_ci memcpy(dst, cpc_name, OCF_LENGTH_CPC_NAME); 9262306a36Sopenharmony_ci spin_unlock_irq(&sclp_ocf_lock); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL(sclp_ocf_cpc_name_copy); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic ssize_t cpc_name_show(struct kobject *kobj, 9762306a36Sopenharmony_ci struct kobj_attribute *attr, char *page) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci char name[OCF_LENGTH_CPC_NAME + 1]; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci sclp_ocf_cpc_name_copy(name); 10262306a36Sopenharmony_ci name[OCF_LENGTH_CPC_NAME] = 0; 10362306a36Sopenharmony_ci EBCASC(name, OCF_LENGTH_CPC_NAME); 10462306a36Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%s\n", name); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct kobj_attribute cpc_name_attr = 10862306a36Sopenharmony_ci __ATTR(cpc_name, 0444, cpc_name_show, NULL); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic ssize_t hmc_network_show(struct kobject *kobj, 11162306a36Sopenharmony_ci struct kobj_attribute *attr, char *page) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci int rc; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_lock_irq(&sclp_ocf_lock); 11662306a36Sopenharmony_ci rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network); 11762306a36Sopenharmony_ci spin_unlock_irq(&sclp_ocf_lock); 11862306a36Sopenharmony_ci return rc; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct kobj_attribute hmc_network_attr = 12262306a36Sopenharmony_ci __ATTR(hmc_network, 0444, hmc_network_show, NULL); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct attribute *ocf_attrs[] = { 12562306a36Sopenharmony_ci &cpc_name_attr.attr, 12662306a36Sopenharmony_ci &hmc_network_attr.attr, 12762306a36Sopenharmony_ci NULL, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct attribute_group ocf_attr_group = { 13162306a36Sopenharmony_ci .attrs = ocf_attrs, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int __init ocf_init(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int rc; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify); 13962306a36Sopenharmony_ci ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj); 14062306a36Sopenharmony_ci if (!ocf_kset) 14162306a36Sopenharmony_ci return -ENOMEM; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group); 14462306a36Sopenharmony_ci if (rc) { 14562306a36Sopenharmony_ci kset_unregister(ocf_kset); 14662306a36Sopenharmony_ci return rc; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return sclp_register(&sclp_ocf_event); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cidevice_initcall(ocf_init); 153