chsc.c 21.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
/*
 *  drivers/s390/cio/chsc.c
 *   S/390 common I/O routines -- channel subsystem call
 *
Cornelia Huck's avatar
Cornelia Huck committed
5
 *    Copyright IBM Corp. 1999,2008
Linus Torvalds's avatar
Linus Torvalds committed
6
 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
7
 *		 Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds's avatar
Linus Torvalds committed
8
9
10
11
12
13
14
15
16
 *		 Arnd Bergmann (arndb@de.ibm.com)
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>

#include <asm/cio.h>
17
#include <asm/chpid.h>
18
#include <asm/chsc.h>
Linus Torvalds's avatar
Linus Torvalds committed
19

20
#include "../s390mach.h"
Linus Torvalds's avatar
Linus Torvalds committed
21
22
23
24
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
#include "ioasm.h"
25
#include "chp.h"
Linus Torvalds's avatar
Linus Torvalds committed
26
27
28
29
#include "chsc.h"

static void *sei_page;

30
31
32
33
34
35
36
/**
 * chsc_error_from_response() - convert a chsc response to an error
 * @response: chsc response code
 *
 * Returns an appropriate Linux error code for @response.
 */
int chsc_error_from_response(int response)
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
	switch (response) {
	case 0x0001:
		return 0;
	case 0x0002:
	case 0x0003:
	case 0x0006:
	case 0x0007:
	case 0x0008:
	case 0x000a:
		return -EINVAL;
	case 0x0004:
		return -EOPNOTSUPP;
	default:
		return -EIO;
	}
}
54
EXPORT_SYMBOL_GPL(chsc_error_from_response);
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
struct chsc_ssd_area {
	struct chsc_header request;
	u16 :10;
	u16 ssid:2;
	u16 :4;
	u16 f_sch;	  /* first subchannel */
	u16 :16;
	u16 l_sch;	  /* last subchannel */
	u32 :32;
	struct chsc_header response;
	u32 :32;
	u8 sch_valid : 1;
	u8 dev_valid : 1;
	u8 st	     : 3; /* subchannel type */
	u8 zeroes    : 3;
	u8  unit_addr;	  /* unit address */
	u16 devno;	  /* device number */
	u8 path_mask;
	u8 fla_valid_mask;
	u16 sch;	  /* subchannel */
	u8 chpid[8];	  /* chpids 0-7 */
	u16 fla[8];	  /* full link addresses 0-7 */
} __attribute__ ((packed));

int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
Linus Torvalds's avatar
Linus Torvalds committed
81
{
82
83
84
85
86
87
	unsigned long page;
	struct chsc_ssd_area *ssd_area;
	int ccode;
	int ret;
	int i;
	int mask;
Linus Torvalds's avatar
Linus Torvalds committed
88

89
90
91
92
	page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
	if (!page)
		return -ENOMEM;
	ssd_area = (struct chsc_ssd_area *) page;
93
94
	ssd_area->request.length = 0x0010;
	ssd_area->request.code = 0x0004;
95
96
97
	ssd_area->ssid = schid.ssid;
	ssd_area->f_sch = schid.sch_no;
	ssd_area->l_sch = schid.sch_no;
Linus Torvalds's avatar
Linus Torvalds committed
98
99

	ccode = chsc(ssd_area);
100
	/* Check response. */
Linus Torvalds's avatar
Linus Torvalds committed
101
	if (ccode > 0) {
102
103
		ret = (ccode == 3) ? -ENODEV : -EBUSY;
		goto out_free;
Linus Torvalds's avatar
Linus Torvalds committed
104
	}
105
106
	ret = chsc_error_from_response(ssd_area->response.code);
	if (ret != 0) {
107
108
		CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
			      schid.ssid, schid.sch_no,
Linus Torvalds's avatar
Linus Torvalds committed
109
			      ssd_area->response.code);
110
		goto out_free;
Linus Torvalds's avatar
Linus Torvalds committed
111
	}
112
113
114
	if (!ssd_area->sch_valid) {
		ret = -ENODEV;
		goto out_free;
Linus Torvalds's avatar
Linus Torvalds committed
115
	}
116
117
118
	/* Copy data */
	ret = 0;
	memset(ssd, 0, sizeof(struct chsc_ssd_info));
119
120
	if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
	    (ssd_area->st != SUBCHANNEL_TYPE_MSG))
121
122
123
124
125
126
127
128
		goto out_free;
	ssd->path_mask = ssd_area->path_mask;
	ssd->fla_valid_mask = ssd_area->fla_valid_mask;
	for (i = 0; i < 8; i++) {
		mask = 0x80 >> i;
		if (ssd_area->path_mask & mask) {
			chp_id_init(&ssd->chpid[i]);
			ssd->chpid[i].id = ssd_area->chpid[i];
Linus Torvalds's avatar
Linus Torvalds committed
129
		}
130
131
		if (ssd_area->fla_valid_mask & mask)
			ssd->fla[i] = ssd_area->fla[i];
Linus Torvalds's avatar
Linus Torvalds committed
132
	}
133
134
out_free:
	free_page(page);
Linus Torvalds's avatar
Linus Torvalds committed
135
136
137
	return ret;
}

138
static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
139
{
140
	spin_lock_irq(sch->lock);
Cornelia Huck's avatar
Cornelia Huck committed
141
142
	if (sch->driver && sch->driver->chp_event)
		if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
143
			goto out_unreg;
144
	spin_unlock_irq(sch->lock);
Linus Torvalds's avatar
Linus Torvalds committed
145
	return 0;
146

Linus Torvalds's avatar
Linus Torvalds committed
147
148
out_unreg:
	sch->lpm = 0;
149
	spin_unlock_irq(sch->lock);
150
	css_schedule_eval(sch->schid);
Linus Torvalds's avatar
Linus Torvalds committed
151
152
153
	return 0;
}

154
void chsc_chp_offline(struct chp_id chpid)
Linus Torvalds's avatar
Linus Torvalds committed
155
156
{
	char dbf_txt[15];
157
	struct chp_link link;
Linus Torvalds's avatar
Linus Torvalds committed
158

159
	sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds's avatar
Linus Torvalds committed
160
161
	CIO_TRACE_EVENT(2, dbf_txt);

162
	if (chp_get_status(chpid) <= 0)
Linus Torvalds's avatar
Linus Torvalds committed
163
		return;
164
165
	memset(&link, 0, sizeof(struct chp_link));
	link.chpid = chpid;
166
167
	/* Wait until previous actions have settled. */
	css_wait_for_slow_path();
168
	for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link);
Linus Torvalds's avatar
Linus Torvalds committed
169
170
}

171
static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
172
173
174
175
176
177
178
179
180
181
{
	struct schib schib;
	/*
	 * We don't know the device yet, but since a path
	 * may be available now to the device we'll have
	 * to do recognition again.
	 * Since we don't have any idea about which chpid
	 * that beast may be on we'll have to do a stsch
	 * on all devices, grr...
	 */
182
	if (stsch_err(schid, &schib))
183
		/* We're through */
184
		return -ENXIO;
185
186

	/* Put it on the slow path. */
187
	css_schedule_eval(schid);
188
189
190
	return 0;
}

191
static int __s390_process_res_acc(struct subchannel *sch, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
192
{
193
	spin_lock_irq(sch->lock);
Cornelia Huck's avatar
Cornelia Huck committed
194
195
	if (sch->driver && sch->driver->chp_event)
		sch->driver->chp_event(sch, data, CHP_ONLINE);
196
	spin_unlock_irq(sch->lock);
197

198
	return 0;
199
200
}

201
static void s390_process_res_acc(struct chp_link *link)
202
{
Linus Torvalds's avatar
Linus Torvalds committed
203
204
	char dbf_txt[15];

205
206
	sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid,
		link->chpid.id);
Linus Torvalds's avatar
Linus Torvalds committed
207
	CIO_TRACE_EVENT( 2, dbf_txt);
208
209
	if (link->fla != 0) {
		sprintf(dbf_txt, "fla%x", link->fla);
Linus Torvalds's avatar
Linus Torvalds committed
210
211
		CIO_TRACE_EVENT( 2, dbf_txt);
	}
212
213
	/* Wait until previous actions have settled. */
	css_wait_for_slow_path();
Linus Torvalds's avatar
Linus Torvalds committed
214
215
216
217
218
219
220
	/*
	 * I/O resources may have become accessible.
	 * Scan through all subchannels that may be concerned and
	 * do a validation on those.
	 * The more information we have (info), the less scanning
	 * will we have to do.
	 */
221
	for_each_subchannel_staged(__s390_process_res_acc,
222
				   s390_process_res_acc_new_sch, link);
Linus Torvalds's avatar
Linus Torvalds committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
}

static int
__get_chpid_from_lir(void *data)
{
	struct lir {
		u8  iq;
		u8  ic;
		u16 sci;
		/* incident-node descriptor */
		u32 indesc[28];
		/* attached-node descriptor */
		u32 andesc[28];
		/* incident-specific information */
		u32 isinfo[28];
238
	} __attribute__ ((packed)) *lir;
Linus Torvalds's avatar
Linus Torvalds committed
239

240
	lir = data;
Linus Torvalds's avatar
Linus Torvalds committed
241
242
243
244
245
246
247
248
249
250
251
252
253
254
	if (!(lir->iq&0x80))
		/* NULL link incident record */
		return -EINVAL;
	if (!(lir->indesc[0]&0xc0000000))
		/* node descriptor not valid */
		return -EINVAL;
	if (!(lir->indesc[0]&0x10000000))
		/* don't handle device-type nodes - FIXME */
		return -EINVAL;
	/* Byte 3 contains the chpid. Could also be CTCA, but we don't care */

	return (u16) (lir->indesc[0]&0x000000ff);
}

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
struct chsc_sei_area {
	struct chsc_header request;
	u32 reserved1;
	u32 reserved2;
	u32 reserved3;
	struct chsc_header response;
	u32 reserved4;
	u8  flags;
	u8  vf;		/* validity flags */
	u8  rs;		/* reporting source */
	u8  cc;		/* content code */
	u16 fla;	/* full link address */
	u16 rsid;	/* reporting source id */
	u32 reserved5;
	u32 reserved6;
	u8 ccdf[4096 - 16 - 24];	/* content-code dependent field */
	/* ccdf has to be big enough for a link-incident record */
} __attribute__ ((packed));

274
static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
275
{
276
277
	struct chp_id chpid;
	int id;
278
279
280
281

	CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
		      sei_area->rs, sei_area->rsid);
	if (sei_area->rs != 4)
282
		return;
283
284
	id = __get_chpid_from_lir(sei_area->ccdf);
	if (id < 0)
285
		CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
286
287
288
	else {
		chp_id_init(&chpid);
		chpid.id = id;
289
		chsc_chp_offline(chpid);
290
	}
291
292
}

293
static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
Linus Torvalds's avatar
Linus Torvalds committed
294
{
295
	struct chp_link link;
296
	struct chp_id chpid;
297
298
299
300
301
	int status;

	CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
		      "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
	if (sei_area->rs != 4)
302
		return;
303
304
	chp_id_init(&chpid);
	chpid.id = sei_area->rsid;
305
	/* allocate a new channel path structure, if needed */
306
	status = chp_get_status(chpid);
307
	if (status < 0)
308
		chp_new(chpid);
309
	else if (!status)
310
		return;
311
312
	memset(&link, 0, sizeof(struct chp_link));
	link.chpid = chpid;
313
	if ((sei_area->vf & 0xc0) != 0) {
314
		link.fla = sei_area->fla;
315
316
		if ((sei_area->vf & 0xc0) == 0xc0)
			/* full link address */
317
			link.fla_mask = 0xffff;
318
319
		else
			/* link address */
320
			link.fla_mask = 0xff00;
321
	}
322
	s390_process_res_acc(&link);
323
324
}

325
326
327
328
329
330
struct chp_config_data {
	u8 map[32];
	u8 op;
	u8 pc;
};

331
static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
332
333
334
335
336
337
338
{
	struct chp_config_data *data;
	struct chp_id chpid;
	int num;

	CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
	if (sei_area->rs != 0)
339
		return;
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
	data = (struct chp_config_data *) &(sei_area->ccdf);
	chp_id_init(&chpid);
	for (num = 0; num <= __MAX_CHPID; num++) {
		if (!chp_test_bit(data->map, num))
			continue;
		chpid.id = num;
		printk(KERN_WARNING "cio: processing configure event %d for "
		       "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
		switch (data->op) {
		case 0:
			chp_cfg_schedule(chpid, 1);
			break;
		case 1:
			chp_cfg_schedule(chpid, 0);
			break;
		case 2:
			chp_cfg_cancel_deconfigure(chpid);
			break;
		}
	}
}

362
static void chsc_process_sei(struct chsc_sei_area *sei_area)
363
364
{
	/* Check if we might have lost some information. */
365
	if (sei_area->flags & 0x40) {
366
		CIO_CRW_EVENT(2, "chsc: event overflow\n");
367
368
		css_schedule_eval_all();
	}
369
370
371
	/* which kind of information was stored? */
	switch (sei_area->cc) {
	case 1: /* link incident*/
372
		chsc_process_sei_link_incident(sei_area);
373
374
		break;
	case 2: /* i/o resource accessibiliy */
375
		chsc_process_sei_res_acc(sei_area);
376
		break;
377
	case 8: /* channel-path-configuration notification */
378
		chsc_process_sei_chp_config(sei_area);
379
		break;
380
381
382
383
384
385
386
	default: /* other stuff */
		CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
			      sei_area->cc);
		break;
	}
}

387
static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
388
389
{
	struct chsc_sei_area *sei_area;
Linus Torvalds's avatar
Linus Torvalds committed
390

391
392
393
394
395
396
397
398
	if (overflow) {
		css_schedule_eval_all();
		return;
	}
	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
		      crw0->erc, crw0->rsid);
Linus Torvalds's avatar
Linus Torvalds committed
399
	if (!sei_page)
400
		return;
401
402
	/* Access to sei_page is serialized through machine check handler
	 * thread, so no need for locking. */
Linus Torvalds's avatar
Linus Torvalds committed
403
404
	sei_area = sei_page;

405
	CIO_TRACE_EVENT(2, "prcss");
Linus Torvalds's avatar
Linus Torvalds committed
406
407
	do {
		memset(sei_area, 0, sizeof(*sei_area));
408
409
		sei_area->request.length = 0x0010;
		sei_area->request.code = 0x000e;
410
411
		if (chsc(sei_area))
			break;
Linus Torvalds's avatar
Linus Torvalds committed
412

413
414
		if (sei_area->response.code == 0x0001) {
			CIO_CRW_EVENT(4, "chsc: sei successful\n");
415
			chsc_process_sei(sei_area);
416
417
		} else {
			CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
Linus Torvalds's avatar
Linus Torvalds committed
418
419
420
421
422
423
				      sei_area->response.code);
			break;
		}
	} while (sei_area->flags & 0x80);
}

424
void chsc_chp_online(struct chp_id chpid)
425
{
Linus Torvalds's avatar
Linus Torvalds committed
426
	char dbf_txt[15];
427
	struct chp_link link;
Linus Torvalds's avatar
Linus Torvalds committed
428

429
	sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
Linus Torvalds's avatar
Linus Torvalds committed
430
431
	CIO_TRACE_EVENT(2, dbf_txt);

432
	if (chp_get_status(chpid) != 0) {
433
434
		memset(&link, 0, sizeof(struct chp_link));
		link.chpid = chpid;
435
436
		/* Wait until previous actions have settled. */
		css_wait_for_slow_path();
Cornelia Huck's avatar
Cornelia Huck committed
437
		for_each_subchannel_staged(__s390_process_res_acc, NULL,
438
					   &link);
439
	}
Linus Torvalds's avatar
Linus Torvalds committed
440
441
}

442
443
static void __s390_subchannel_vary_chpid(struct subchannel *sch,
					 struct chp_id chpid, int on)
Linus Torvalds's avatar
Linus Torvalds committed
444
445
{
	unsigned long flags;
446
	struct chp_link link;
Linus Torvalds's avatar
Linus Torvalds committed
447

448
449
	memset(&link, 0, sizeof(struct chp_link));
	link.chpid = chpid;
450
	spin_lock_irqsave(sch->lock, flags);
Cornelia Huck's avatar
Cornelia Huck committed
451
	if (sch->driver && sch->driver->chp_event)
452
		sch->driver->chp_event(sch, &link,
Cornelia Huck's avatar
Cornelia Huck committed
453
				       on ? CHP_VARY_ON : CHP_VARY_OFF);
454
	spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
455
456
}

457
static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
458
{
459
	struct chp_id *chpid = data;
Linus Torvalds's avatar
Linus Torvalds committed
460
461
462
463
464

	__s390_subchannel_vary_chpid(sch, *chpid, 0);
	return 0;
}

465
static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
466
{
467
	struct chp_id *chpid = data;
Linus Torvalds's avatar
Linus Torvalds committed
468
469
470
471
472

	__s390_subchannel_vary_chpid(sch, *chpid, 1);
	return 0;
}

473
474
475
476
477
static int
__s390_vary_chpid_on(struct subchannel_id schid, void *data)
{
	struct schib schib;

478
	if (stsch_err(schid, &schib))
479
480
481
		/* We're through */
		return -ENXIO;
	/* Put it on the slow path. */
482
	css_schedule_eval(schid);
483
484
485
	return 0;
}

486
487
488
489
/**
 * chsc_chp_vary - propagate channel-path vary operation to subchannels
 * @chpid: channl-path ID
 * @on: non-zero for vary online, zero for vary offline
Linus Torvalds's avatar
Linus Torvalds committed
490
 */
491
int chsc_chp_vary(struct chp_id chpid, int on)
Linus Torvalds's avatar
Linus Torvalds committed
492
{
493
494
495
496
	struct chp_link link;

	memset(&link, 0, sizeof(struct chp_link));
	link.chpid = chpid;
497
498
	/* Wait until previous actions have settled. */
	css_wait_for_slow_path();
Linus Torvalds's avatar
Linus Torvalds committed
499
500
501
502
	/*
	 * Redo PathVerification on the devices the chpid connects to
	 */

503
	if (on)
504
		for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
505
					   __s390_vary_chpid_on, &link);
506
507
	else
		for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
508
					   NULL, &link);
509

Linus Torvalds's avatar
Linus Torvalds committed
510
511
512
	return 0;
}

513
514
515
516
517
518
519
520
static void
chsc_remove_cmg_attr(struct channel_subsystem *css)
{
	int i;

	for (i = 0; i <= __MAX_CHPID; i++) {
		if (!css->chps[i])
			continue;
521
		chp_remove_cmg_attr(css->chps[i]);
522
523
524
525
526
527
528
529
530
531
532
533
	}
}

static int
chsc_add_cmg_attr(struct channel_subsystem *css)
{
	int i, ret;

	ret = 0;
	for (i = 0; i <= __MAX_CHPID; i++) {
		if (!css->chps[i])
			continue;
534
		ret = chp_add_cmg_attr(css->chps[i]);
535
536
537
538
539
540
541
542
		if (ret)
			goto cleanup;
	}
	return ret;
cleanup:
	for (--i; i >= 0; i--) {
		if (!css->chps[i])
			continue;
543
		chp_remove_cmg_attr(css->chps[i]);
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
	}
	return ret;
}

static int
__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
{
	struct {
		struct chsc_header request;
		u32 operation_code : 2;
		u32 : 30;
		u32 key : 4;
		u32 : 28;
		u32 zeroes1;
		u32 cub_addr1;
		u32 zeroes2;
		u32 cub_addr2;
		u32 reserved[13];
		struct chsc_header response;
		u32 status : 8;
		u32 : 4;
		u32 fmt : 4;
		u32 : 16;
567
	} __attribute__ ((packed)) *secm_area;
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
	int ret, ccode;

	secm_area = page;
	secm_area->request.length = 0x0050;
	secm_area->request.code = 0x0016;

	secm_area->key = PAGE_DEFAULT_KEY;
	secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
	secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;

	secm_area->operation_code = enable ? 0 : 1;

	ccode = chsc(secm_area);
	if (ccode > 0)
		return (ccode == 3) ? -ENODEV : -EBUSY;

	switch (secm_area->response.code) {
585
586
	case 0x0102:
	case 0x0103:
587
588
		ret = -EINVAL;
	default:
589
		ret = chsc_error_from_response(secm_area->response.code);
590
	}
591
592
593
	if (ret != 0)
		CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
			      secm_area->response.code);
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
	return ret;
}

int
chsc_secm(struct channel_subsystem *css, int enable)
{
	void  *secm_area;
	int ret;

	secm_area = (void *)get_zeroed_page(GFP_KERNEL |  GFP_DMA);
	if (!secm_area)
		return -ENOMEM;

	if (enable && !css->cm_enabled) {
		css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
		css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
		if (!css->cub_addr1 || !css->cub_addr2) {
			free_page((unsigned long)css->cub_addr1);
			free_page((unsigned long)css->cub_addr2);
			free_page((unsigned long)secm_area);
			return -ENOMEM;
		}
	}
	ret = __chsc_do_secm(css, enable, secm_area);
	if (!ret) {
		css->cm_enabled = enable;
		if (css->cm_enabled) {
			ret = chsc_add_cmg_attr(css);
			if (ret) {
				memset(secm_area, 0, PAGE_SIZE);
				__chsc_do_secm(css, 0, secm_area);
				css->cm_enabled = 0;
			}
		} else
			chsc_remove_cmg_attr(css);
	}
630
	if (!css->cm_enabled) {
631
632
633
634
635
636
637
		free_page((unsigned long)css->cub_addr1);
		free_page((unsigned long)css->cub_addr2);
	}
	free_page((unsigned long)secm_area);
	return ret;
}

638
639
640
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
				     int c, int m,
				     struct chsc_response_struct *resp)
Linus Torvalds's avatar
Linus Torvalds committed
641
642
643
644
645
{
	int ccode, ret;

	struct {
		struct chsc_header request;
646
647
648
649
650
651
652
		u32 : 2;
		u32 m : 1;
		u32 c : 1;
		u32 fmt : 4;
		u32 cssid : 8;
		u32 : 4;
		u32 rfmt : 4;
Linus Torvalds's avatar
Linus Torvalds committed
653
654
655
656
657
		u32 first_chpid : 8;
		u32 : 24;
		u32 last_chpid : 8;
		u32 zeroes1;
		struct chsc_header response;
658
		u8 data[PAGE_SIZE - 20];
659
	} __attribute__ ((packed)) *scpd_area;
Linus Torvalds's avatar
Linus Torvalds committed
660

661
662
663
664
	if ((rfmt == 1) && !css_general_characteristics.fcs)
		return -EINVAL;
	if ((rfmt == 2) && !css_general_characteristics.cib)
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
665
666
667
668
	scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
	if (!scpd_area)
		return -ENOMEM;

669
670
	scpd_area->request.length = 0x0010;
	scpd_area->request.code = 0x0002;
Linus Torvalds's avatar
Linus Torvalds committed
671

672
	scpd_area->cssid = chpid.cssid;
673
674
	scpd_area->first_chpid = chpid.id;
	scpd_area->last_chpid = chpid.id;
675
676
677
678
	scpd_area->m = m;
	scpd_area->c = c;
	scpd_area->fmt = fmt;
	scpd_area->rfmt = rfmt;
Linus Torvalds's avatar
Linus Torvalds committed
679
680
681
682
683
684
685

	ccode = chsc(scpd_area);
	if (ccode > 0) {
		ret = (ccode == 3) ? -ENODEV : -EBUSY;
		goto out;
	}

686
687
688
	ret = chsc_error_from_response(scpd_area->response.code);
	if (ret == 0)
		/* Success. */
689
		memcpy(resp, &scpd_area->response, scpd_area->response.length);
690
691
	else
		CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
Linus Torvalds's avatar
Linus Torvalds committed
692
693
694
695
696
			      scpd_area->response.code);
out:
	free_page((unsigned long)scpd_area);
	return ret;
}
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);

int chsc_determine_base_channel_path_desc(struct chp_id chpid,
					  struct channel_path_desc *desc)
{
	struct chsc_response_struct *chsc_resp;
	int ret;

	chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL);
	if (!chsc_resp)
		return -ENOMEM;
	ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp);
	if (ret)
		goto out_free;
	memcpy(desc, &chsc_resp->data, chsc_resp->length);
out_free:
	kfree(chsc_resp);
	return ret;
}
Linus Torvalds's avatar
Linus Torvalds committed
716

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
static void
chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
			  struct cmg_chars *chars)
{
	switch (chp->cmg) {
	case 2:
	case 3:
		chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
					 GFP_KERNEL);
		if (chp->cmg_chars) {
			int i, mask;
			struct cmg_chars *cmg_chars;

			cmg_chars = chp->cmg_chars;
			for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
				mask = 0x80 >> (i + 3);
				if (cmcv & mask)
					cmg_chars->values[i] = chars->values[i];
				else
					cmg_chars->values[i] = 0;
			}
		}
		break;
	default:
		/* No cmg-dependent data. */
		break;
	}
}

746
int chsc_get_channel_measurement_chars(struct channel_path *chp)
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
{
	int ccode, ret;

	struct {
		struct chsc_header request;
		u32 : 24;
		u32 first_chpid : 8;
		u32 : 24;
		u32 last_chpid : 8;
		u32 zeroes1;
		struct chsc_header response;
		u32 zeroes2;
		u32 not_valid : 1;
		u32 shared : 1;
		u32 : 22;
		u32 chpid : 8;
		u32 cmcv : 5;
		u32 : 11;
		u32 cmgq : 8;
		u32 cmg : 8;
		u32 zeroes3;
		u32 data[NR_MEASUREMENT_CHARS];
769
	} __attribute__ ((packed)) *scmc_area;
770
771
772
773
774
775
776
777

	scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
	if (!scmc_area)
		return -ENOMEM;

	scmc_area->request.length = 0x0010;
	scmc_area->request.code = 0x0022;

778
779
	scmc_area->first_chpid = chp->chpid.id;
	scmc_area->last_chpid = chp->chpid.id;
780
781
782
783
784
785
786

	ccode = chsc(scmc_area);
	if (ccode > 0) {
		ret = (ccode == 3) ? -ENODEV : -EBUSY;
		goto out;
	}

787
788
789
	ret = chsc_error_from_response(scmc_area->response.code);
	if (ret == 0) {
		/* Success. */
790
791
792
793
794
795
796
797
798
799
		if (!scmc_area->not_valid) {
			chp->cmg = scmc_area->cmg;
			chp->shared = scmc_area->shared;
			chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
						  (struct cmg_chars *)
						  &scmc_area->data);
		} else {
			chp->cmg = -1;
			chp->shared = -1;
		}
800
801
	} else {
		CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n",
802
803
804
805
806
807
808
			      scmc_area->response.code);
	}
out:
	free_page((unsigned long)scmc_area);
	return ret;
}

809
int __init chsc_alloc_sei_area(void)
Linus Torvalds's avatar
Linus Torvalds committed
810
{
811
812
	int ret;

Linus Torvalds's avatar
Linus Torvalds committed
813
	sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
814
	if (!sei_page) {
Cornelia Huck's avatar
Cornelia Huck committed
815
816
		CIO_MSG_EVENT(0, "Can't allocate page for processing of "
			      "chsc machine checks!\n");
817
818
819
820
821
822
		return -ENOMEM;
	}
	ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw);
	if (ret)
		kfree(sei_page);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
823
824
}

825
826
void __init chsc_free_sei_area(void)
{
827
	s390_unregister_crw_handler(CRW_RSC_CSS);
828
829
830
	kfree(sei_page);
}

831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
int __init
chsc_enable_facility(int operation_code)
{
	int ret;
	struct {
		struct chsc_header request;
		u8 reserved1:4;
		u8 format:4;
		u8 reserved2;
		u16 operation_code;
		u32 reserved3;
		u32 reserved4;
		u32 operation_data_area[252];
		struct chsc_header response;
		u32 reserved5:4;
		u32 format2:4;
		u32 reserved6:24;
848
	} __attribute__ ((packed)) *sda_area;
849
850
851
852

	sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
	if (!sda_area)
		return -ENOMEM;
853
854
	sda_area->request.length = 0x0400;
	sda_area->request.code = 0x0031;
855
856
857
858
859
860
861
	sda_area->operation_code = operation_code;

	ret = chsc(sda_area);
	if (ret > 0) {
		ret = (ret == 3) ? -ENODEV : -EBUSY;
		goto out;
	}
862

863
	switch (sda_area->response.code) {
864
	case 0x0101:
865
866
		ret = -EOPNOTSUPP;
		break;
867
868
	default:
		ret = chsc_error_from_response(sda_area->response.code);
869
	}
870
871
872
	if (ret != 0)
		CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
			      operation_code, sda_area->response.code);
873
874
875
876
877
 out:
	free_page((unsigned long)sda_area);
	return ret;
}

Linus Torvalds's avatar
Linus Torvalds committed
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
struct css_general_char css_general_characteristics;
struct css_chsc_char css_chsc_characteristics;

int __init
chsc_determine_css_characteristics(void)
{
	int result;
	struct {
		struct chsc_header request;
		u32 reserved1;
		u32 reserved2;
		u32 reserved3;
		struct chsc_header response;
		u32 reserved4;
		u32 general_char[510];
		u32 chsc_char[518];
894
	} __attribute__ ((packed)) *scsc_area;
Linus Torvalds's avatar
Linus Torvalds committed
895
896

	scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
897
	if (!scsc_area)
Linus Torvalds's avatar
Linus Torvalds committed
898
899
		return -ENOMEM;

900
901
	scsc_area->request.length = 0x0010;
	scsc_area->request.code = 0x0010;
Linus Torvalds's avatar
Linus Torvalds committed
902
903
904

	result = chsc(scsc_area);
	if (result) {
905
		result = (result == 3) ? -ENODEV : -EBUSY;
Linus Torvalds's avatar
Linus Torvalds committed
906
907
908
		goto exit;
	}

909
910
911
912
913
914
915
916
917
	result = chsc_error_from_response(scsc_area->response.code);
	if (result == 0) {
		memcpy(&css_general_characteristics, scsc_area->general_char,
		       sizeof(css_general_characteristics));
		memcpy(&css_chsc_characteristics, scsc_area->chsc_char,
		       sizeof(css_chsc_characteristics));
	} else
		CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
			      scsc_area->response.code);
Linus Torvalds's avatar
Linus Torvalds committed
918
919
920
921
922
923
924
exit:
	free_page ((unsigned long) scsc_area);
	return result;
}

EXPORT_SYMBOL_GPL(css_general_characteristics);
EXPORT_SYMBOL_GPL(css_chsc_characteristics);
Martin Schwidefsky's avatar
Martin Schwidefsky committed
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973

int chsc_sstpc(void *page, unsigned int op, u16 ctrl)
{
	struct {
		struct chsc_header request;
		unsigned int rsvd0;
		unsigned int op : 8;
		unsigned int rsvd1 : 8;
		unsigned int ctrl : 16;
		unsigned int rsvd2[5];
		struct chsc_header response;
		unsigned int rsvd3[7];
	} __attribute__ ((packed)) *rr;
	int rc;

	memset(page, 0, PAGE_SIZE);
	rr = page;
	rr->request.length = 0x0020;
	rr->request.code = 0x0033;
	rr->op = op;
	rr->ctrl = ctrl;
	rc = chsc(rr);
	if (rc)
		return -EIO;
	rc = (rr->response.code == 0x0001) ? 0 : -EIO;
	return rc;
}

int chsc_sstpi(void *page, void *result, size_t size)
{
	struct {
		struct chsc_header request;
		unsigned int rsvd0[3];
		struct chsc_header response;
		char data[size];
	} __attribute__ ((packed)) *rr;
	int rc;

	memset(page, 0, PAGE_SIZE);
	rr = page;
	rr->request.length = 0x0010;
	rr->request.code = 0x0038;
	rc = chsc(rr);
	if (rc)
		return -EIO;
	memcpy(result, &rr->data, size);
	return (rr->response.code == 0x0001) ? 0 : -EIO;
}