1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Deliver z/VM CP special messages (SMSG) as uevents.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * The driver registers for z/VM CP special messages with the
6*4882a593Smuzhiyun * "APP" prefix. Incoming messages are delivered to user space
7*4882a593Smuzhiyun * as uevents.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Copyright IBM Corp. 2010
10*4882a593Smuzhiyun * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun #define KMSG_COMPONENT "smsgiucv_app"
14*4882a593Smuzhiyun #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/ctype.h>
17*4882a593Smuzhiyun #include <linux/err.h>
18*4882a593Smuzhiyun #include <linux/device.h>
19*4882a593Smuzhiyun #include <linux/list.h>
20*4882a593Smuzhiyun #include <linux/kobject.h>
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/spinlock.h>
24*4882a593Smuzhiyun #include <linux/workqueue.h>
25*4882a593Smuzhiyun #include <net/iucv/iucv.h>
26*4882a593Smuzhiyun #include "smsgiucv.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* prefix used for SMSG registration */
29*4882a593Smuzhiyun #define SMSG_PREFIX "APP"
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* SMSG related uevent environment variables */
32*4882a593Smuzhiyun #define ENV_SENDER_STR "SMSG_SENDER="
33*4882a593Smuzhiyun #define ENV_SENDER_LEN (strlen(ENV_SENDER_STR) + 8 + 1)
34*4882a593Smuzhiyun #define ENV_PREFIX_STR "SMSG_ID="
35*4882a593Smuzhiyun #define ENV_PREFIX_LEN (strlen(ENV_PREFIX_STR) + \
36*4882a593Smuzhiyun strlen(SMSG_PREFIX) + 1)
37*4882a593Smuzhiyun #define ENV_TEXT_STR "SMSG_TEXT="
38*4882a593Smuzhiyun #define ENV_TEXT_LEN(msg) (strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun /* z/VM user ID which is permitted to send SMSGs
41*4882a593Smuzhiyun * If the value is undefined or empty (""), special messages are
42*4882a593Smuzhiyun * accepted from any z/VM user ID. */
43*4882a593Smuzhiyun static char *sender;
44*4882a593Smuzhiyun module_param(sender, charp, 0400);
45*4882a593Smuzhiyun MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /* SMSG device representation */
48*4882a593Smuzhiyun static struct device *smsg_app_dev;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun /* list element for queuing received messages for delivery */
51*4882a593Smuzhiyun struct smsg_app_event {
52*4882a593Smuzhiyun struct list_head list;
53*4882a593Smuzhiyun char *buf;
54*4882a593Smuzhiyun char *envp[4];
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /* queue for outgoing uevents */
58*4882a593Smuzhiyun static LIST_HEAD(smsg_event_queue);
59*4882a593Smuzhiyun static DEFINE_SPINLOCK(smsg_event_queue_lock);
60*4882a593Smuzhiyun
smsg_app_event_free(struct smsg_app_event * ev)61*4882a593Smuzhiyun static void smsg_app_event_free(struct smsg_app_event *ev)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun kfree(ev->buf);
64*4882a593Smuzhiyun kfree(ev);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
smsg_app_event_alloc(const char * from,const char * msg)67*4882a593Smuzhiyun static struct smsg_app_event *smsg_app_event_alloc(const char *from,
68*4882a593Smuzhiyun const char *msg)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun struct smsg_app_event *ev;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
73*4882a593Smuzhiyun if (!ev)
74*4882a593Smuzhiyun return NULL;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
77*4882a593Smuzhiyun ENV_TEXT_LEN(msg), GFP_ATOMIC);
78*4882a593Smuzhiyun if (!ev->buf) {
79*4882a593Smuzhiyun kfree(ev);
80*4882a593Smuzhiyun return NULL;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* setting up environment pointers into buf */
84*4882a593Smuzhiyun ev->envp[0] = ev->buf;
85*4882a593Smuzhiyun ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
86*4882a593Smuzhiyun ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
87*4882a593Smuzhiyun ev->envp[3] = NULL;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /* setting up environment: sender, prefix name, and message text */
90*4882a593Smuzhiyun snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
91*4882a593Smuzhiyun snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
92*4882a593Smuzhiyun snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return ev;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
smsg_event_work_fn(struct work_struct * work)97*4882a593Smuzhiyun static void smsg_event_work_fn(struct work_struct *work)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun LIST_HEAD(event_queue);
100*4882a593Smuzhiyun struct smsg_app_event *p, *n;
101*4882a593Smuzhiyun struct device *dev;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun dev = get_device(smsg_app_dev);
104*4882a593Smuzhiyun if (!dev)
105*4882a593Smuzhiyun return;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun spin_lock_bh(&smsg_event_queue_lock);
108*4882a593Smuzhiyun list_splice_init(&smsg_event_queue, &event_queue);
109*4882a593Smuzhiyun spin_unlock_bh(&smsg_event_queue_lock);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun list_for_each_entry_safe(p, n, &event_queue, list) {
112*4882a593Smuzhiyun list_del(&p->list);
113*4882a593Smuzhiyun kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
114*4882a593Smuzhiyun smsg_app_event_free(p);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun put_device(dev);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
120*4882a593Smuzhiyun
smsg_app_callback(const char * from,char * msg)121*4882a593Smuzhiyun static void smsg_app_callback(const char *from, char *msg)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun struct smsg_app_event *se;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /* check if the originating z/VM user ID matches
126*4882a593Smuzhiyun * the configured sender. */
127*4882a593Smuzhiyun if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
128*4882a593Smuzhiyun return;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /* get start of message text (skip prefix and leading blanks) */
131*4882a593Smuzhiyun msg += strlen(SMSG_PREFIX);
132*4882a593Smuzhiyun while (*msg && isspace(*msg))
133*4882a593Smuzhiyun msg++;
134*4882a593Smuzhiyun if (*msg == '\0')
135*4882a593Smuzhiyun return;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun /* allocate event list element and its environment */
138*4882a593Smuzhiyun se = smsg_app_event_alloc(from, msg);
139*4882a593Smuzhiyun if (!se)
140*4882a593Smuzhiyun return;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /* queue event and schedule work function */
143*4882a593Smuzhiyun spin_lock(&smsg_event_queue_lock);
144*4882a593Smuzhiyun list_add_tail(&se->list, &smsg_event_queue);
145*4882a593Smuzhiyun spin_unlock(&smsg_event_queue_lock);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun schedule_work(&smsg_event_work);
148*4882a593Smuzhiyun return;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
smsgiucv_app_init(void)151*4882a593Smuzhiyun static int __init smsgiucv_app_init(void)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun struct device_driver *smsgiucv_drv;
154*4882a593Smuzhiyun int rc;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun if (!MACHINE_IS_VM)
157*4882a593Smuzhiyun return -ENODEV;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
160*4882a593Smuzhiyun if (!smsg_app_dev)
161*4882a593Smuzhiyun return -ENOMEM;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
164*4882a593Smuzhiyun if (!smsgiucv_drv) {
165*4882a593Smuzhiyun kfree(smsg_app_dev);
166*4882a593Smuzhiyun return -ENODEV;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
170*4882a593Smuzhiyun if (rc) {
171*4882a593Smuzhiyun kfree(smsg_app_dev);
172*4882a593Smuzhiyun goto fail;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun smsg_app_dev->bus = &iucv_bus;
175*4882a593Smuzhiyun smsg_app_dev->parent = iucv_root;
176*4882a593Smuzhiyun smsg_app_dev->release = (void (*)(struct device *)) kfree;
177*4882a593Smuzhiyun smsg_app_dev->driver = smsgiucv_drv;
178*4882a593Smuzhiyun rc = device_register(smsg_app_dev);
179*4882a593Smuzhiyun if (rc) {
180*4882a593Smuzhiyun put_device(smsg_app_dev);
181*4882a593Smuzhiyun goto fail;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun /* convert sender to uppercase characters */
185*4882a593Smuzhiyun if (sender) {
186*4882a593Smuzhiyun int len = strlen(sender);
187*4882a593Smuzhiyun while (len--)
188*4882a593Smuzhiyun sender[len] = toupper(sender[len]);
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun /* register with the smsgiucv device driver */
192*4882a593Smuzhiyun rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
193*4882a593Smuzhiyun if (rc) {
194*4882a593Smuzhiyun device_unregister(smsg_app_dev);
195*4882a593Smuzhiyun goto fail;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun rc = 0;
199*4882a593Smuzhiyun fail:
200*4882a593Smuzhiyun return rc;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun module_init(smsgiucv_app_init);
203*4882a593Smuzhiyun
smsgiucv_app_exit(void)204*4882a593Smuzhiyun static void __exit smsgiucv_app_exit(void)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun /* unregister callback */
207*4882a593Smuzhiyun smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun /* cancel pending work and flush any queued event work */
210*4882a593Smuzhiyun cancel_work_sync(&smsg_event_work);
211*4882a593Smuzhiyun smsg_event_work_fn(&smsg_event_work);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun device_unregister(smsg_app_dev);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun module_exit(smsgiucv_app_exit);
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
218*4882a593Smuzhiyun MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
219*4882a593Smuzhiyun MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");
220