linux/drivers/s390/char/sclp_cpi_sys.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *    SCLP control program identification sysfs interface
   4 *
   5 *    Copyright IBM Corp. 2001, 2007
   6 *    Author(s): Martin Peschke <mpeschke@de.ibm.com>
   7 *               Michael Ernst <mernst@de.ibm.com>
   8 */
   9
  10#define KMSG_COMPONENT "sclp_cpi"
  11#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  12
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/stat.h>
  16#include <linux/device.h>
  17#include <linux/string.h>
  18#include <linux/ctype.h>
  19#include <linux/kmod.h>
  20#include <linux/timer.h>
  21#include <linux/err.h>
  22#include <linux/slab.h>
  23#include <linux/completion.h>
  24#include <linux/export.h>
  25#include <asm/ebcdic.h>
  26#include <asm/sclp.h>
  27
  28#include "sclp.h"
  29#include "sclp_rw.h"
  30#include "sclp_cpi_sys.h"
  31
  32#define CPI_LENGTH_NAME 8
  33#define CPI_LENGTH_LEVEL 16
  34
  35static DEFINE_MUTEX(sclp_cpi_mutex);
  36
  37struct cpi_evbuf {
  38        struct evbuf_header header;
  39        u8      id_format;
  40        u8      reserved0;
  41        u8      system_type[CPI_LENGTH_NAME];
  42        u64     reserved1;
  43        u8      system_name[CPI_LENGTH_NAME];
  44        u64     reserved2;
  45        u64     system_level;
  46        u64     reserved3;
  47        u8      sysplex_name[CPI_LENGTH_NAME];
  48        u8      reserved4[16];
  49} __attribute__((packed));
  50
  51struct cpi_sccb {
  52        struct sccb_header header;
  53        struct cpi_evbuf cpi_evbuf;
  54} __attribute__((packed));
  55
  56static struct sclp_register sclp_cpi_event = {
  57        .send_mask = EVTYP_CTLPROGIDENT_MASK,
  58};
  59
  60static char system_name[CPI_LENGTH_NAME + 1];
  61static char sysplex_name[CPI_LENGTH_NAME + 1];
  62static char system_type[CPI_LENGTH_NAME + 1];
  63static u64 system_level;
  64
  65static void set_data(char *field, char *data)
  66{
  67        memset(field, ' ', CPI_LENGTH_NAME);
  68        memcpy(field, data, strlen(data));
  69        sclp_ascebc_str(field, CPI_LENGTH_NAME);
  70}
  71
  72static void cpi_callback(struct sclp_req *req, void *data)
  73{
  74        struct completion *completion = data;
  75
  76        complete(completion);
  77}
  78
  79static struct sclp_req *cpi_prepare_req(void)
  80{
  81        struct sclp_req *req;
  82        struct cpi_sccb *sccb;
  83        struct cpi_evbuf *evb;
  84
  85        req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
  86        if (!req)
  87                return ERR_PTR(-ENOMEM);
  88        sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  89        if (!sccb) {
  90                kfree(req);
  91                return ERR_PTR(-ENOMEM);
  92        }
  93
  94        /* setup SCCB for Control-Program Identification */
  95        sccb->header.length = sizeof(struct cpi_sccb);
  96        sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
  97        sccb->cpi_evbuf.header.type = EVTYP_CTLPROGIDENT;
  98        evb = &sccb->cpi_evbuf;
  99
 100        /* set system type */
 101        set_data(evb->system_type, system_type);
 102
 103        /* set system name */
 104        set_data(evb->system_name, system_name);
 105
 106        /* set system level */
 107        evb->system_level = system_level;
 108
 109        /* set sysplex name */
 110        set_data(evb->sysplex_name, sysplex_name);
 111
 112        /* prepare request data structure presented to SCLP driver */
 113        req->command = SCLP_CMDW_WRITE_EVENT_DATA;
 114        req->sccb = sccb;
 115        req->status = SCLP_REQ_FILLED;
 116        req->callback = cpi_callback;
 117        return req;
 118}
 119
 120static void cpi_free_req(struct sclp_req *req)
 121{
 122        free_page((unsigned long) req->sccb);
 123        kfree(req);
 124}
 125
 126static int cpi_req(void)
 127{
 128        struct completion completion;
 129        struct sclp_req *req;
 130        int rc;
 131        int response;
 132
 133        rc = sclp_register(&sclp_cpi_event);
 134        if (rc)
 135                goto out;
 136        if (!(sclp_cpi_event.sclp_receive_mask & EVTYP_CTLPROGIDENT_MASK)) {
 137                rc = -EOPNOTSUPP;
 138                goto out_unregister;
 139        }
 140
 141        req = cpi_prepare_req();
 142        if (IS_ERR(req)) {
 143                rc = PTR_ERR(req);
 144                goto out_unregister;
 145        }
 146
 147        init_completion(&completion);
 148        req->callback_data = &completion;
 149
 150        /* Add request to sclp queue */
 151        rc = sclp_add_request(req);
 152        if (rc)
 153                goto out_free_req;
 154
 155        wait_for_completion(&completion);
 156
 157        if (req->status != SCLP_REQ_DONE) {
 158                pr_warn("request failed (status=0x%02x)\n", req->status);
 159                rc = -EIO;
 160                goto out_free_req;
 161        }
 162
 163        response = ((struct cpi_sccb *) req->sccb)->header.response_code;
 164        if (response != 0x0020) {
 165                pr_warn("request failed with response code 0x%x\n", response);
 166                rc = -EIO;
 167        }
 168
 169out_free_req:
 170        cpi_free_req(req);
 171
 172out_unregister:
 173        sclp_unregister(&sclp_cpi_event);
 174
 175out:
 176        return rc;
 177}
 178
 179static int check_string(const char *attr, const char *str)
 180{
 181        size_t len;
 182        size_t i;
 183
 184        len = strlen(str);
 185
 186        if ((len > 0) && (str[len - 1] == '\n'))
 187                len--;
 188
 189        if (len > CPI_LENGTH_NAME)
 190                return -EINVAL;
 191
 192        for (i = 0; i < len ; i++) {
 193                if (isalpha(str[i]) || isdigit(str[i]) ||
 194                    strchr("$@# ", str[i]))
 195                        continue;
 196                return -EINVAL;
 197        }
 198
 199        return 0;
 200}
 201
 202static void set_string(char *attr, const char *value)
 203{
 204        size_t len;
 205        size_t i;
 206
 207        len = strlen(value);
 208
 209        if ((len > 0) && (value[len - 1] == '\n'))
 210                len--;
 211
 212        for (i = 0; i < CPI_LENGTH_NAME; i++) {
 213                if (i < len)
 214                        attr[i] = toupper(value[i]);
 215                else
 216                        attr[i] = ' ';
 217        }
 218}
 219
 220static ssize_t system_name_show(struct kobject *kobj,
 221                                struct kobj_attribute *attr, char *page)
 222{
 223        int rc;
 224
 225        mutex_lock(&sclp_cpi_mutex);
 226        rc = snprintf(page, PAGE_SIZE, "%s\n", system_name);
 227        mutex_unlock(&sclp_cpi_mutex);
 228        return rc;
 229}
 230
 231static ssize_t system_name_store(struct kobject *kobj,
 232                                 struct kobj_attribute *attr,
 233                                 const char *buf,
 234        size_t len)
 235{
 236        int rc;
 237
 238        rc = check_string("system_name", buf);
 239        if (rc)
 240                return rc;
 241
 242        mutex_lock(&sclp_cpi_mutex);
 243        set_string(system_name, buf);
 244        mutex_unlock(&sclp_cpi_mutex);
 245
 246        return len;
 247}
 248
 249static struct kobj_attribute system_name_attr =
 250        __ATTR(system_name, 0644, system_name_show, system_name_store);
 251
 252static ssize_t sysplex_name_show(struct kobject *kobj,
 253                                 struct kobj_attribute *attr, char *page)
 254{
 255        int rc;
 256
 257        mutex_lock(&sclp_cpi_mutex);
 258        rc = snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
 259        mutex_unlock(&sclp_cpi_mutex);
 260        return rc;
 261}
 262
 263static ssize_t sysplex_name_store(struct kobject *kobj,
 264                                  struct kobj_attribute *attr,
 265                                  const char *buf,
 266        size_t len)
 267{
 268        int rc;
 269
 270        rc = check_string("sysplex_name", buf);
 271        if (rc)
 272                return rc;
 273
 274        mutex_lock(&sclp_cpi_mutex);
 275        set_string(sysplex_name, buf);
 276        mutex_unlock(&sclp_cpi_mutex);
 277
 278        return len;
 279}
 280
 281static struct kobj_attribute sysplex_name_attr =
 282        __ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store);
 283
 284static ssize_t system_type_show(struct kobject *kobj,
 285                                struct kobj_attribute *attr, char *page)
 286{
 287        int rc;
 288
 289        mutex_lock(&sclp_cpi_mutex);
 290        rc = snprintf(page, PAGE_SIZE, "%s\n", system_type);
 291        mutex_unlock(&sclp_cpi_mutex);
 292        return rc;
 293}
 294
 295static ssize_t system_type_store(struct kobject *kobj,
 296                                 struct kobj_attribute *attr,
 297                                 const char *buf,
 298        size_t len)
 299{
 300        int rc;
 301
 302        rc = check_string("system_type", buf);
 303        if (rc)
 304                return rc;
 305
 306        mutex_lock(&sclp_cpi_mutex);
 307        set_string(system_type, buf);
 308        mutex_unlock(&sclp_cpi_mutex);
 309
 310        return len;
 311}
 312
 313static struct kobj_attribute system_type_attr =
 314        __ATTR(system_type, 0644, system_type_show, system_type_store);
 315
 316static ssize_t system_level_show(struct kobject *kobj,
 317                                 struct kobj_attribute *attr, char *page)
 318{
 319        unsigned long long level;
 320
 321        mutex_lock(&sclp_cpi_mutex);
 322        level = system_level;
 323        mutex_unlock(&sclp_cpi_mutex);
 324        return snprintf(page, PAGE_SIZE, "%#018llx\n", level);
 325}
 326
 327static ssize_t system_level_store(struct kobject *kobj,
 328                                  struct kobj_attribute *attr,
 329                                  const char *buf,
 330        size_t len)
 331{
 332        unsigned long long level;
 333        char *endp;
 334
 335        level = simple_strtoull(buf, &endp, 16);
 336
 337        if (endp == buf)
 338                return -EINVAL;
 339        if (*endp == '\n')
 340                endp++;
 341        if (*endp)
 342                return -EINVAL;
 343
 344        mutex_lock(&sclp_cpi_mutex);
 345        system_level = level;
 346        mutex_unlock(&sclp_cpi_mutex);
 347        return len;
 348}
 349
 350static struct kobj_attribute system_level_attr =
 351        __ATTR(system_level, 0644, system_level_show, system_level_store);
 352
 353static ssize_t set_store(struct kobject *kobj,
 354                         struct kobj_attribute *attr,
 355                         const char *buf, size_t len)
 356{
 357        int rc;
 358
 359        mutex_lock(&sclp_cpi_mutex);
 360        rc = cpi_req();
 361        mutex_unlock(&sclp_cpi_mutex);
 362        if (rc)
 363                return rc;
 364
 365        return len;
 366}
 367
 368static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store);
 369
 370static struct attribute *cpi_attrs[] = {
 371        &system_name_attr.attr,
 372        &sysplex_name_attr.attr,
 373        &system_type_attr.attr,
 374        &system_level_attr.attr,
 375        &set_attr.attr,
 376        NULL,
 377};
 378
 379static struct attribute_group cpi_attr_group = {
 380        .attrs = cpi_attrs,
 381};
 382
 383static struct kset *cpi_kset;
 384
 385int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type,
 386                      const u64 level)
 387{
 388        int rc;
 389
 390        rc = check_string("system_name", system);
 391        if (rc)
 392                return rc;
 393        rc = check_string("sysplex_name", sysplex);
 394        if (rc)
 395                return rc;
 396        rc = check_string("system_type", type);
 397        if (rc)
 398                return rc;
 399
 400        mutex_lock(&sclp_cpi_mutex);
 401        set_string(system_name, system);
 402        set_string(sysplex_name, sysplex);
 403        set_string(system_type, type);
 404        system_level = level;
 405
 406        rc = cpi_req();
 407        mutex_unlock(&sclp_cpi_mutex);
 408
 409        return rc;
 410}
 411EXPORT_SYMBOL(sclp_cpi_set_data);
 412
 413static int __init cpi_init(void)
 414{
 415        int rc;
 416
 417        cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj);
 418        if (!cpi_kset)
 419                return -ENOMEM;
 420
 421        rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group);
 422        if (rc)
 423                kset_unregister(cpi_kset);
 424
 425        return rc;
 426}
 427
 428__initcall(cpi_init);
 429