18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Deliver z/VM CP special messages (SMSG) as uevents. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The driver registers for z/VM CP special messages with the 68c2ecf20Sopenharmony_ci * "APP" prefix. Incoming messages are delivered to user space 78c2ecf20Sopenharmony_ci * as uevents. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2010 108c2ecf20Sopenharmony_ci * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "smsgiucv_app" 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/ctype.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/list.h> 208c2ecf20Sopenharmony_ci#include <linux/kobject.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 248c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 258c2ecf20Sopenharmony_ci#include <net/iucv/iucv.h> 268c2ecf20Sopenharmony_ci#include "smsgiucv.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* prefix used for SMSG registration */ 298c2ecf20Sopenharmony_ci#define SMSG_PREFIX "APP" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* SMSG related uevent environment variables */ 328c2ecf20Sopenharmony_ci#define ENV_SENDER_STR "SMSG_SENDER=" 338c2ecf20Sopenharmony_ci#define ENV_SENDER_LEN (strlen(ENV_SENDER_STR) + 8 + 1) 348c2ecf20Sopenharmony_ci#define ENV_PREFIX_STR "SMSG_ID=" 358c2ecf20Sopenharmony_ci#define ENV_PREFIX_LEN (strlen(ENV_PREFIX_STR) + \ 368c2ecf20Sopenharmony_ci strlen(SMSG_PREFIX) + 1) 378c2ecf20Sopenharmony_ci#define ENV_TEXT_STR "SMSG_TEXT=" 388c2ecf20Sopenharmony_ci#define ENV_TEXT_LEN(msg) (strlen(ENV_TEXT_STR) + strlen((msg)) + 1) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* z/VM user ID which is permitted to send SMSGs 418c2ecf20Sopenharmony_ci * If the value is undefined or empty (""), special messages are 428c2ecf20Sopenharmony_ci * accepted from any z/VM user ID. */ 438c2ecf20Sopenharmony_cistatic char *sender; 448c2ecf20Sopenharmony_cimodule_param(sender, charp, 0400); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* SMSG device representation */ 488c2ecf20Sopenharmony_cistatic struct device *smsg_app_dev; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* list element for queuing received messages for delivery */ 518c2ecf20Sopenharmony_cistruct smsg_app_event { 528c2ecf20Sopenharmony_ci struct list_head list; 538c2ecf20Sopenharmony_ci char *buf; 548c2ecf20Sopenharmony_ci char *envp[4]; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* queue for outgoing uevents */ 588c2ecf20Sopenharmony_cistatic LIST_HEAD(smsg_event_queue); 598c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(smsg_event_queue_lock); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void smsg_app_event_free(struct smsg_app_event *ev) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci kfree(ev->buf); 648c2ecf20Sopenharmony_ci kfree(ev); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic struct smsg_app_event *smsg_app_event_alloc(const char *from, 688c2ecf20Sopenharmony_ci const char *msg) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct smsg_app_event *ev; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_ATOMIC); 738c2ecf20Sopenharmony_ci if (!ev) 748c2ecf20Sopenharmony_ci return NULL; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN + 778c2ecf20Sopenharmony_ci ENV_TEXT_LEN(msg), GFP_ATOMIC); 788c2ecf20Sopenharmony_ci if (!ev->buf) { 798c2ecf20Sopenharmony_ci kfree(ev); 808c2ecf20Sopenharmony_ci return NULL; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* setting up environment pointers into buf */ 848c2ecf20Sopenharmony_ci ev->envp[0] = ev->buf; 858c2ecf20Sopenharmony_ci ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN; 868c2ecf20Sopenharmony_ci ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN; 878c2ecf20Sopenharmony_ci ev->envp[3] = NULL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* setting up environment: sender, prefix name, and message text */ 908c2ecf20Sopenharmony_ci snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from); 918c2ecf20Sopenharmony_ci snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX); 928c2ecf20Sopenharmony_ci snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return ev; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void smsg_event_work_fn(struct work_struct *work) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci LIST_HEAD(event_queue); 1008c2ecf20Sopenharmony_ci struct smsg_app_event *p, *n; 1018c2ecf20Sopenharmony_ci struct device *dev; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci dev = get_device(smsg_app_dev); 1048c2ecf20Sopenharmony_ci if (!dev) 1058c2ecf20Sopenharmony_ci return; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci spin_lock_bh(&smsg_event_queue_lock); 1088c2ecf20Sopenharmony_ci list_splice_init(&smsg_event_queue, &event_queue); 1098c2ecf20Sopenharmony_ci spin_unlock_bh(&smsg_event_queue_lock); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &event_queue, list) { 1128c2ecf20Sopenharmony_ci list_del(&p->list); 1138c2ecf20Sopenharmony_ci kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp); 1148c2ecf20Sopenharmony_ci smsg_app_event_free(p); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci put_device(dev); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_cistatic DECLARE_WORK(smsg_event_work, smsg_event_work_fn); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void smsg_app_callback(const char *from, char *msg) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct smsg_app_event *se; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* check if the originating z/VM user ID matches 1268c2ecf20Sopenharmony_ci * the configured sender. */ 1278c2ecf20Sopenharmony_ci if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0) 1288c2ecf20Sopenharmony_ci return; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* get start of message text (skip prefix and leading blanks) */ 1318c2ecf20Sopenharmony_ci msg += strlen(SMSG_PREFIX); 1328c2ecf20Sopenharmony_ci while (*msg && isspace(*msg)) 1338c2ecf20Sopenharmony_ci msg++; 1348c2ecf20Sopenharmony_ci if (*msg == '\0') 1358c2ecf20Sopenharmony_ci return; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* allocate event list element and its environment */ 1388c2ecf20Sopenharmony_ci se = smsg_app_event_alloc(from, msg); 1398c2ecf20Sopenharmony_ci if (!se) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* queue event and schedule work function */ 1438c2ecf20Sopenharmony_ci spin_lock(&smsg_event_queue_lock); 1448c2ecf20Sopenharmony_ci list_add_tail(&se->list, &smsg_event_queue); 1458c2ecf20Sopenharmony_ci spin_unlock(&smsg_event_queue_lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci schedule_work(&smsg_event_work); 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int __init smsgiucv_app_init(void) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct device_driver *smsgiucv_drv; 1548c2ecf20Sopenharmony_ci int rc; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!MACHINE_IS_VM) 1578c2ecf20Sopenharmony_ci return -ENODEV; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!smsg_app_dev) 1618c2ecf20Sopenharmony_ci return -ENOMEM; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus); 1648c2ecf20Sopenharmony_ci if (!smsgiucv_drv) { 1658c2ecf20Sopenharmony_ci kfree(smsg_app_dev); 1668c2ecf20Sopenharmony_ci return -ENODEV; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT); 1708c2ecf20Sopenharmony_ci if (rc) { 1718c2ecf20Sopenharmony_ci kfree(smsg_app_dev); 1728c2ecf20Sopenharmony_ci goto fail; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci smsg_app_dev->bus = &iucv_bus; 1758c2ecf20Sopenharmony_ci smsg_app_dev->parent = iucv_root; 1768c2ecf20Sopenharmony_ci smsg_app_dev->release = (void (*)(struct device *)) kfree; 1778c2ecf20Sopenharmony_ci smsg_app_dev->driver = smsgiucv_drv; 1788c2ecf20Sopenharmony_ci rc = device_register(smsg_app_dev); 1798c2ecf20Sopenharmony_ci if (rc) { 1808c2ecf20Sopenharmony_ci put_device(smsg_app_dev); 1818c2ecf20Sopenharmony_ci goto fail; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* convert sender to uppercase characters */ 1858c2ecf20Sopenharmony_ci if (sender) { 1868c2ecf20Sopenharmony_ci int len = strlen(sender); 1878c2ecf20Sopenharmony_ci while (len--) 1888c2ecf20Sopenharmony_ci sender[len] = toupper(sender[len]); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* register with the smsgiucv device driver */ 1928c2ecf20Sopenharmony_ci rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback); 1938c2ecf20Sopenharmony_ci if (rc) { 1948c2ecf20Sopenharmony_ci device_unregister(smsg_app_dev); 1958c2ecf20Sopenharmony_ci goto fail; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci rc = 0; 1998c2ecf20Sopenharmony_cifail: 2008c2ecf20Sopenharmony_ci return rc; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_cimodule_init(smsgiucv_app_init); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void __exit smsgiucv_app_exit(void) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci /* unregister callback */ 2078c2ecf20Sopenharmony_ci smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* cancel pending work and flush any queued event work */ 2108c2ecf20Sopenharmony_ci cancel_work_sync(&smsg_event_work); 2118c2ecf20Sopenharmony_ci smsg_event_work_fn(&smsg_event_work); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci device_unregister(smsg_app_dev); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_cimodule_exit(smsgiucv_app_exit); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents"); 2198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>"); 220