18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SCLP OCF communication parameters sysfs interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2011 68c2ecf20Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sclp_ocf" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/stat.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/ctype.h> 188c2ecf20Sopenharmony_ci#include <linux/kmod.h> 198c2ecf20Sopenharmony_ci#include <linux/timer.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 228c2ecf20Sopenharmony_ci#include <asm/sclp.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "sclp.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define OCF_LENGTH_HMC_NETWORK 8UL 278c2ecf20Sopenharmony_ci#define OCF_LENGTH_CPC_NAME 8UL 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic char hmc_network[OCF_LENGTH_HMC_NETWORK + 1]; 308c2ecf20Sopenharmony_cistatic char cpc_name[OCF_LENGTH_CPC_NAME]; /* in EBCDIC */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sclp_ocf_lock); 338c2ecf20Sopenharmony_cistatic struct work_struct sclp_ocf_change_work; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct kset *ocf_kset; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void sclp_ocf_change_notify(struct work_struct *work) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Handler for OCF event. Look for the CPC image name. */ 438c2ecf20Sopenharmony_cistatic void sclp_ocf_handler(struct evbuf_header *evbuf) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct gds_vector *v; 468c2ecf20Sopenharmony_ci struct gds_subvector *sv, *netid, *cpc; 478c2ecf20Sopenharmony_ci size_t size; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* Find the 0x9f00 block. */ 508c2ecf20Sopenharmony_ci v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length, 518c2ecf20Sopenharmony_ci 0x9f00); 528c2ecf20Sopenharmony_ci if (!v) 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci /* Find the 0x9f22 block inside the 0x9f00 block. */ 558c2ecf20Sopenharmony_ci v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22); 568c2ecf20Sopenharmony_ci if (!v) 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci /* Find the 0x81 block inside the 0x9f22 block. */ 598c2ecf20Sopenharmony_ci sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81); 608c2ecf20Sopenharmony_ci if (!sv) 618c2ecf20Sopenharmony_ci return; 628c2ecf20Sopenharmony_ci /* Find the 0x01 block inside the 0x81 block. */ 638c2ecf20Sopenharmony_ci netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1); 648c2ecf20Sopenharmony_ci /* Find the 0x02 block inside the 0x81 block. */ 658c2ecf20Sopenharmony_ci cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2); 668c2ecf20Sopenharmony_ci /* Copy network name and cpc name. */ 678c2ecf20Sopenharmony_ci spin_lock(&sclp_ocf_lock); 688c2ecf20Sopenharmony_ci if (netid) { 698c2ecf20Sopenharmony_ci size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length); 708c2ecf20Sopenharmony_ci memcpy(hmc_network, netid + 1, size); 718c2ecf20Sopenharmony_ci EBCASC(hmc_network, size); 728c2ecf20Sopenharmony_ci hmc_network[size] = 0; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci if (cpc) { 758c2ecf20Sopenharmony_ci size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length); 768c2ecf20Sopenharmony_ci memset(cpc_name, 0, OCF_LENGTH_CPC_NAME); 778c2ecf20Sopenharmony_ci memcpy(cpc_name, cpc + 1, size); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci spin_unlock(&sclp_ocf_lock); 808c2ecf20Sopenharmony_ci schedule_work(&sclp_ocf_change_work); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct sclp_register sclp_ocf_event = { 848c2ecf20Sopenharmony_ci .receive_mask = EVTYP_OCF_MASK, 858c2ecf20Sopenharmony_ci .receiver_fn = sclp_ocf_handler, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_civoid sclp_ocf_cpc_name_copy(char *dst) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci spin_lock_irq(&sclp_ocf_lock); 918c2ecf20Sopenharmony_ci memcpy(dst, cpc_name, OCF_LENGTH_CPC_NAME); 928c2ecf20Sopenharmony_ci spin_unlock_irq(&sclp_ocf_lock); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_ocf_cpc_name_copy); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic ssize_t cpc_name_show(struct kobject *kobj, 978c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *page) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci char name[OCF_LENGTH_CPC_NAME + 1]; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci sclp_ocf_cpc_name_copy(name); 1028c2ecf20Sopenharmony_ci name[OCF_LENGTH_CPC_NAME] = 0; 1038c2ecf20Sopenharmony_ci EBCASC(name, OCF_LENGTH_CPC_NAME); 1048c2ecf20Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%s\n", name); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic struct kobj_attribute cpc_name_attr = 1088c2ecf20Sopenharmony_ci __ATTR(cpc_name, 0444, cpc_name_show, NULL); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic ssize_t hmc_network_show(struct kobject *kobj, 1118c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *page) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci int rc; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci spin_lock_irq(&sclp_ocf_lock); 1168c2ecf20Sopenharmony_ci rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network); 1178c2ecf20Sopenharmony_ci spin_unlock_irq(&sclp_ocf_lock); 1188c2ecf20Sopenharmony_ci return rc; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic struct kobj_attribute hmc_network_attr = 1228c2ecf20Sopenharmony_ci __ATTR(hmc_network, 0444, hmc_network_show, NULL); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic struct attribute *ocf_attrs[] = { 1258c2ecf20Sopenharmony_ci &cpc_name_attr.attr, 1268c2ecf20Sopenharmony_ci &hmc_network_attr.attr, 1278c2ecf20Sopenharmony_ci NULL, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct attribute_group ocf_attr_group = { 1318c2ecf20Sopenharmony_ci .attrs = ocf_attrs, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int __init ocf_init(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int rc; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify); 1398c2ecf20Sopenharmony_ci ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj); 1408c2ecf20Sopenharmony_ci if (!ocf_kset) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group); 1448c2ecf20Sopenharmony_ci if (rc) { 1458c2ecf20Sopenharmony_ci kset_unregister(ocf_kset); 1468c2ecf20Sopenharmony_ci return rc; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return sclp_register(&sclp_ocf_event); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cidevice_initcall(ocf_init); 153