sclp_cpi_sys.c 8.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 *    SCLP control program identification sysfs interface
 *
 *    Copyright IBM Corp. 2001, 2007
 *    Author(s): Martin Peschke <mpeschke@de.ibm.com>
 *		 Michael Ernst <mernst@de.ibm.com>
 */

9 10 11
#define KMSG_COMPONENT "sclp_cpi"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

12 13 14 15 16 17 18 19 20 21 22
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/kmod.h>
#include <linux/timer.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/completion.h>
23
#include <linux/export.h>
24 25
#include <asm/ebcdic.h>
#include <asm/sclp.h>
26

27 28 29 30 31 32 33
#include "sclp.h"
#include "sclp_rw.h"
#include "sclp_cpi_sys.h"

#define CPI_LENGTH_NAME 8
#define CPI_LENGTH_LEVEL 16

34 35
static DEFINE_MUTEX(sclp_cpi_mutex);

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
struct cpi_evbuf {
	struct evbuf_header header;
	u8	id_format;
	u8	reserved0;
	u8	system_type[CPI_LENGTH_NAME];
	u64	reserved1;
	u8	system_name[CPI_LENGTH_NAME];
	u64	reserved2;
	u64	system_level;
	u64	reserved3;
	u8	sysplex_name[CPI_LENGTH_NAME];
	u8	reserved4[16];
} __attribute__((packed));

struct cpi_sccb {
	struct sccb_header header;
	struct cpi_evbuf cpi_evbuf;
} __attribute__((packed));

static struct sclp_register sclp_cpi_event = {
	.send_mask = EVTYP_CTLPROGIDENT_MASK,
};

static char system_name[CPI_LENGTH_NAME + 1];
static char sysplex_name[CPI_LENGTH_NAME + 1];
static char system_type[CPI_LENGTH_NAME + 1];
static u64 system_level;

static void set_data(char *field, char *data)
{
	memset(field, ' ', CPI_LENGTH_NAME);
	memcpy(field, data, strlen(data));
	sclp_ascebc_str(field, CPI_LENGTH_NAME);
}

static void cpi_callback(struct sclp_req *req, void *data)
{
	struct completion *completion = data;

	complete(completion);
}

static struct sclp_req *cpi_prepare_req(void)
{
	struct sclp_req *req;
	struct cpi_sccb *sccb;
	struct cpi_evbuf *evb;

	req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
	if (!req)
		return ERR_PTR(-ENOMEM);
	sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
	if (!sccb) {
		kfree(req);
		return ERR_PTR(-ENOMEM);
	}

	/* setup SCCB for Control-Program Identification */
	sccb->header.length = sizeof(struct cpi_sccb);
	sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
	sccb->cpi_evbuf.header.type = 0x0b;
	evb = &sccb->cpi_evbuf;

	/* set system type */
	set_data(evb->system_type, system_type);

	/* set system name */
	set_data(evb->system_name, system_name);

T
Thomas Weber 已提交
105
	/* set system level */
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	evb->system_level = system_level;

	/* set sysplex name */
	set_data(evb->sysplex_name, sysplex_name);

	/* prepare request data structure presented to SCLP driver */
	req->command = SCLP_CMDW_WRITE_EVENT_DATA;
	req->sccb = sccb;
	req->status = SCLP_REQ_FILLED;
	req->callback = cpi_callback;
	return req;
}

static void cpi_free_req(struct sclp_req *req)
{
	free_page((unsigned long) req->sccb);
	kfree(req);
}

static int cpi_req(void)
{
	struct completion completion;
	struct sclp_req *req;
	int rc;
	int response;

	rc = sclp_register(&sclp_cpi_event);
133
	if (rc)
134
		goto out;
135
	if (!(sclp_cpi_event.sclp_receive_mask & EVTYP_CTLPROGIDENT_MASK)) {
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
		rc = -EOPNOTSUPP;
		goto out_unregister;
	}

	req = cpi_prepare_req();
	if (IS_ERR(req)) {
		rc = PTR_ERR(req);
		goto out_unregister;
	}

	init_completion(&completion);
	req->callback_data = &completion;

	/* Add request to sclp queue */
	rc = sclp_add_request(req);
151
	if (rc)
152 153 154 155 156
		goto out_free_req;

	wait_for_completion(&completion);

	if (req->status != SCLP_REQ_DONE) {
157
		pr_warn("request failed (status=0x%02x)\n", req->status);
158 159 160 161 162 163
		rc = -EIO;
		goto out_free_req;
	}

	response = ((struct cpi_sccb *) req->sccb)->header.response_code;
	if (response != 0x0020) {
164
		pr_warn("request failed with response code 0x%x\n", response);
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
		rc = -EIO;
	}

out_free_req:
	cpi_free_req(req);

out_unregister:
	sclp_unregister(&sclp_cpi_event);

out:
	return rc;
}

static int check_string(const char *attr, const char *str)
{
	size_t len;
	size_t i;

	len = strlen(str);

	if ((len > 0) && (str[len - 1] == '\n'))
		len--;

	if (len > CPI_LENGTH_NAME)
		return -EINVAL;

	for (i = 0; i < len ; i++) {
		if (isalpha(str[i]) || isdigit(str[i]) ||
		    strchr("$@# ", str[i]))
			continue;
		return -EINVAL;
	}

	return 0;
}

static void set_string(char *attr, const char *value)
{
	size_t len;
	size_t i;

	len = strlen(value);

	if ((len > 0) && (value[len - 1] == '\n'))
		len--;

	for (i = 0; i < CPI_LENGTH_NAME; i++) {
		if (i < len)
			attr[i] = toupper(value[i]);
		else
			attr[i] = ' ';
	}
}

static ssize_t system_name_show(struct kobject *kobj,
				struct kobj_attribute *attr, char *page)
{
222 223 224 225 226 227
	int rc;

	mutex_lock(&sclp_cpi_mutex);
	rc = snprintf(page, PAGE_SIZE, "%s\n", system_name);
	mutex_unlock(&sclp_cpi_mutex);
	return rc;
228 229 230 231 232 233 234 235 236 237 238 239 240
}

static ssize_t system_name_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf,
	size_t len)
{
	int rc;

	rc = check_string("system_name", buf);
	if (rc)
		return rc;

241
	mutex_lock(&sclp_cpi_mutex);
242
	set_string(system_name, buf);
243
	mutex_unlock(&sclp_cpi_mutex);
244 245 246 247 248 249 250 251 252 253

	return len;
}

static struct kobj_attribute system_name_attr =
	__ATTR(system_name, 0644, system_name_show, system_name_store);

static ssize_t sysplex_name_show(struct kobject *kobj,
				 struct kobj_attribute *attr, char *page)
{
254 255 256 257 258 259
	int rc;

	mutex_lock(&sclp_cpi_mutex);
	rc = snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
	mutex_unlock(&sclp_cpi_mutex);
	return rc;
260 261 262 263 264 265 266 267 268 269 270 271 272
}

static ssize_t sysplex_name_store(struct kobject *kobj,
				  struct kobj_attribute *attr,
				  const char *buf,
	size_t len)
{
	int rc;

	rc = check_string("sysplex_name", buf);
	if (rc)
		return rc;

273
	mutex_lock(&sclp_cpi_mutex);
274
	set_string(sysplex_name, buf);
275
	mutex_unlock(&sclp_cpi_mutex);
276 277 278 279 280 281 282 283 284 285

	return len;
}

static struct kobj_attribute sysplex_name_attr =
	__ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store);

static ssize_t system_type_show(struct kobject *kobj,
				struct kobj_attribute *attr, char *page)
{
286 287 288 289 290 291
	int rc;

	mutex_lock(&sclp_cpi_mutex);
	rc = snprintf(page, PAGE_SIZE, "%s\n", system_type);
	mutex_unlock(&sclp_cpi_mutex);
	return rc;
292 293 294 295 296 297 298 299 300 301 302 303 304
}

static ssize_t system_type_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf,
	size_t len)
{
	int rc;

	rc = check_string("system_type", buf);
	if (rc)
		return rc;

305
	mutex_lock(&sclp_cpi_mutex);
306
	set_string(system_type, buf);
307
	mutex_unlock(&sclp_cpi_mutex);
308 309 310 311 312 313 314 315 316 317

	return len;
}

static struct kobj_attribute system_type_attr =
	__ATTR(system_type, 0644, system_type_show, system_type_store);

static ssize_t system_level_show(struct kobject *kobj,
				 struct kobj_attribute *attr, char *page)
{
318
	unsigned long long level;
319

320 321 322
	mutex_lock(&sclp_cpi_mutex);
	level = system_level;
	mutex_unlock(&sclp_cpi_mutex);
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
	return snprintf(page, PAGE_SIZE, "%#018llx\n", level);
}

static ssize_t system_level_store(struct kobject *kobj,
				  struct kobj_attribute *attr,
				  const char *buf,
	size_t len)
{
	unsigned long long level;
	char *endp;

	level = simple_strtoull(buf, &endp, 16);

	if (endp == buf)
		return -EINVAL;
	if (*endp == '\n')
		endp++;
	if (*endp)
		return -EINVAL;

343
	mutex_lock(&sclp_cpi_mutex);
344
	system_level = level;
345
	mutex_unlock(&sclp_cpi_mutex);
346 347 348 349 350 351 352 353 354 355 356 357
	return len;
}

static struct kobj_attribute system_level_attr =
	__ATTR(system_level, 0644, system_level_show, system_level_store);

static ssize_t set_store(struct kobject *kobj,
			 struct kobj_attribute *attr,
			 const char *buf, size_t len)
{
	int rc;

358
	mutex_lock(&sclp_cpi_mutex);
359
	rc = cpi_req();
360
	mutex_unlock(&sclp_cpi_mutex);
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
	if (rc)
		return rc;

	return len;
}

static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store);

static struct attribute *cpi_attrs[] = {
	&system_name_attr.attr,
	&sysplex_name_attr.attr,
	&system_type_attr.attr,
	&system_level_attr.attr,
	&set_attr.attr,
	NULL,
};

static struct attribute_group cpi_attr_group = {
	.attrs = cpi_attrs,
};

static struct kset *cpi_kset;

int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type,
		      const u64 level)
{
	int rc;

	rc = check_string("system_name", system);
	if (rc)
		return rc;
	rc = check_string("sysplex_name", sysplex);
	if (rc)
		return rc;
	rc = check_string("system_type", type);
	if (rc)
		return rc;

399
	mutex_lock(&sclp_cpi_mutex);
400 401 402 403 404
	set_string(system_name, system);
	set_string(sysplex_name, sysplex);
	set_string(system_type, type);
	system_level = level;

405 406 407 408
	rc = cpi_req();
	mutex_unlock(&sclp_cpi_mutex);

	return rc;
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
}
EXPORT_SYMBOL(sclp_cpi_set_data);

static int __init cpi_init(void)
{
	int rc;

	cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj);
	if (!cpi_kset)
		return -ENOMEM;

	rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group);
	if (rc)
		kset_unregister(cpi_kset);

	return rc;
}

__initcall(cpi_init);