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