1 /*
2  * File...........: linux/drivers/s390/block/dasd_diag.c
3  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4  * Based on.......: linux/drivers/s390/block/mdisk.c
5  * ...............: by Hartmunt Penner <hpenner@de.ibm.com>
6  * Bugreports.to..: <Linux390@de.ibm.com>
7  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
8  *
9  * $Revision: 1.49 $
10  *
11  * History of changes
12  * 07/13/00 Added fixup sections for diagnoses ans saved some registers
13  * 07/14/00 fixed constraints in newly generated inline asm
14  * 10/05/00 adapted to 'new' DASD driver
15  *          fixed return codes of dia250()
16  *          fixed partition handling and HDIO_GETGEO
17  * 10/01/02 fixed Bugzilla 1341
18  */
19 
20 #include <linux/config.h>
21 #include <linux/stddef.h>
22 #include <linux/kernel.h>
23 #include <asm/debug.h>
24 
25 #include <linux/slab.h>
26 #include <linux/hdreg.h>
27 #include <linux/blk.h>
28 #include <asm/ccwcache.h>
29 #include <asm/dasd.h>
30 
31 #include <asm/ebcdic.h>
32 #include <asm/io.h>
33 #include <asm/irq.h>
34 #include <asm/s390dyn.h>
35 #include <asm/s390_ext.h>
36 
37 #include "dasd_int.h"
38 #include "dasd_diag.h"
39 
40 #ifdef PRINTK_HEADER
41 #undef PRINTK_HEADER
42 #endif				/* PRINTK_HEADER */
43 #define PRINTK_HEADER DASD_NAME"(diag):"
44 
45 #ifdef MODULE
46 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
47 MODULE_LICENSE("GPL");
48 #endif
49 #endif
50 
51 dasd_discipline_t dasd_diag_discipline;
52 
53 typedef struct
54     dasd_diag_private_t {
55 	diag210_t rdc_data;
56 	diag_rw_io_t iob;
57 	diag_init_io_t iib;
58 	unsigned long *label;
59 } dasd_diag_private_t;
60 
61 static __inline__ int
dia250(void * iob,int cmd)62 dia250 (void *iob, int cmd)
63 {
64         unsigned long addr;
65         int rc;
66 
67         addr = __pa(iob);
68 	__asm__ __volatile__ ("    lhi   %0,11\n"
69                               "    lr    0,%2\n"
70 			      "    diag  0,%1,0x250\n"
71 			      "0:  ipm   %0\n"
72 			      "    srl   %0,28\n"
73 			      "    or    %0,1\n"
74 			      "1:\n"
75 #ifndef CONFIG_ARCH_S390X
76 			      ".section __ex_table,\"a\"\n"
77 			      "    .align 4\n"
78 			      "    .long 0b,1b\n"
79                               ".previous\n"
80 #else
81 			      ".section __ex_table,\"a\"\n"
82 			      "    .align 8\n"
83 			      "    .quad  0b,1b\n"
84                               ".previous\n"
85 #endif
86                               : "=&d" (rc)
87                               : "d" (cmd), "d" (addr) : "0", "1", "cc");
88 	return rc;
89 }
90 
91 static __inline__ int
mdsk_init_io(dasd_device_t * device,int blocksize,int offset,int size)92 mdsk_init_io (dasd_device_t * device, int blocksize, int offset, int size)
93 {
94 	dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
95 	diag_init_io_t *iib = &private->iib;
96 	int rc;
97 
98 	memset (iib, 0, sizeof (diag_init_io_t));
99 
100 	iib->dev_nr = device->devinfo.devno;
101 	iib->block_size = blocksize;
102 	iib->offset = offset;
103 	iib->start_block = 0;
104 	iib->end_block = size;
105 
106 	rc = dia250 (iib, INIT_BIO);
107 
108 	return rc & 3;
109 }
110 
111 static __inline__ int
mdsk_term_io(dasd_device_t * device)112 mdsk_term_io (dasd_device_t * device)
113 {
114 	dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
115 	diag_init_io_t *iib = &private->iib;
116 	int rc;
117 
118 	memset (iib, 0, sizeof (diag_init_io_t));
119 	iib->dev_nr = device->devinfo.devno;
120 	rc = dia250 (iib, TERM_BIO);
121 	return rc & 3;
122 }
123 
124 int
dasd_start_diag(ccw_req_t * cqr)125 dasd_start_diag (ccw_req_t * cqr)
126 {
127 	int rc;
128 	dasd_device_t *device = cqr->device;
129 	dasd_diag_private_t *private;
130 	diag_rw_io_t *iob;
131 
132 	private = (dasd_diag_private_t *) device->private;
133 	iob = &private->iob;
134 
135 	iob->dev_nr = device->devinfo.devno;
136 	iob->key = 0;
137 	iob->flags = 2;
138 	iob->block_count = cqr->cplength >> 1;
139 	iob->interrupt_params = (u32)(addr_t) cqr;
140 	iob->bio_list = __pa (cqr->cpaddr);
141 
142 	cqr->startclk = get_clock ();
143 
144 	rc = dia250 (iob, RW_BIO);
145 	if (rc > 8) {
146 
147 		MESSAGE (KERN_WARNING,
148                          "dia250 returned CC %d",
149                          rc);
150 
151 		check_then_set (&cqr->status,
152 				CQR_STATUS_QUEUED, CQR_STATUS_ERROR);
153 	} else if (rc == 0) {
154 		check_then_set (&cqr->status,
155 				CQR_STATUS_QUEUED, CQR_STATUS_DONE);
156 		dasd_schedule_bh (device);
157 	} else {
158 		check_then_set (&cqr->status,
159 				CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
160 		rc = 0;
161 	}
162 	return rc;
163 }
164 
165 void
dasd_ext_handler(struct pt_regs * regs,__u16 code)166 dasd_ext_handler (struct pt_regs *regs, __u16 code)
167 {
168     	int cpu = smp_processor_id();
169 	ccw_req_t *cqr;
170 	int ip = S390_lowcore.ext_params;
171 	char status = *((char *) &S390_lowcore.ext_params + 5);
172 	dasd_device_t *device;
173 	int done_fast_io = 0;
174 	int devno;
175         unsigned long flags;
176 	int subcode;
177 
178 	/*
179 	 * Get the external interruption subcode. VM stores
180          * this in the 'cpu address' field associated with
181          * the external interrupt. For diag 250 the subcode
182          * needs to be 3.
183 	 */
184 	subcode = S390_lowcore.cpu_addr;
185 	if ((subcode & 0xff00) != 0x0300)
186 		return;
187 
188 	irq_enter(cpu, -1);
189 
190 	if (!ip) {		/* no intparm: unsolicited interrupt */
191 
192 		MESSAGE (KERN_DEBUG, "%s",
193                          "caught unsolicited interrupt");
194 
195 		irq_exit(cpu, -1);
196 		return;
197 	}
198 	if (ip & 0x80000001) {
199 
200 		MESSAGE (KERN_DEBUG,
201                          "caught spurious interrupt with parm %08x",
202                          ip);
203 
204 		irq_exit(cpu, -1);
205 		return;
206 	}
207 	cqr = (ccw_req_t *)(addr_t) ip;
208 	device = (dasd_device_t *) cqr->device;
209 
210 	devno = device->devinfo.devno;
211 
212 	if (device == NULL) {
213 
214 		DEV_MESSAGE (KERN_WARNING, device, "%s",
215                              " belongs to NULL device");
216 	}
217 
218 	if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
219 
220 		DEV_MESSAGE (KERN_WARNING, device,
221                              " magic number of ccw_req_t 0x%08X doesn't match"
222                              " discipline 0x%08X",
223                              cqr->magic,
224                              *(int *) (&device->discipline->name));
225 
226 		irq_exit(cpu, -1);
227 		return;
228 	}
229 
230         /* get irq lock to modify request queue */
231         s390irq_spin_lock_irqsave (device->devinfo.irq,
232                                    flags);
233 
234 	cqr->stopclk = get_clock ();
235 
236 	switch (status) {
237 	case 0x00:
238 		check_then_set (&cqr->status,
239 				CQR_STATUS_IN_IO, CQR_STATUS_DONE);
240 		if (cqr->next && (cqr->next->status == CQR_STATUS_QUEUED)) {
241 			if (dasd_start_diag (cqr->next) == 0) {
242 				done_fast_io = 1;
243 			}
244 		}
245 		break;
246 	case 0x01:
247 	case 0x02:
248 	case 0x03:
249 	default:
250 		check_then_set (&cqr->status,
251 				CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
252 		break;
253 	}
254 
255         s390irq_spin_unlock_irqrestore (device->devinfo.irq,
256                                         flags);
257 
258 	wake_up (&device->wait_q);
259 	dasd_schedule_bh (device);
260 	irq_exit(cpu, -1);
261 
262 }
263 
264 static int
dasd_diag_check_characteristics(struct dasd_device_t * device)265 dasd_diag_check_characteristics (struct dasd_device_t *device)
266 {
267 	int rc = 0;
268 	int bsize;
269 	dasd_diag_private_t *private;
270 	diag210_t *rdc_data;
271 	ccw_req_t *cqr;
272 	unsigned long *label;
273 	int sb;
274 
275 	if (device == NULL) {
276 
277 		MESSAGE (KERN_WARNING, "%s",
278                          "Null device pointer passed to characteristics "
279                          "checker");
280 
281 		return -ENODEV;
282 	}
283 
284         label = NULL;
285 	device->private = kmalloc (sizeof (dasd_diag_private_t), GFP_KERNEL);
286 
287 	if (device->private == NULL) {
288 
289 		MESSAGE (KERN_WARNING, "%s",
290                          "memory allocation failed for private data");
291 
292 		rc = -ENOMEM;
293                 goto fail;
294 	}
295 	private = (dasd_diag_private_t *) device->private;
296         memset (private, 0, sizeof(dasd_diag_private_t));
297 	rdc_data = (void *) &(private->rdc_data);
298 
299 	rdc_data->vrdcdvno = device->devinfo.devno;
300 	rdc_data->vrdclen = sizeof (diag210_t);
301 
302         rc = diag210 (rdc_data);
303 	if ( rc != 0) {
304 		goto fail;
305 	}
306 	if (rdc_data->vrdcvcla != DEV_CLASS_FBA &&
307 	    rdc_data->vrdcvcla != DEV_CLASS_ECKD &&
308 	    rdc_data->vrdcvcla != DEV_CLASS_CKD) {
309                 rc = -ENOTSUPP;
310 		goto fail;
311 	}
312 
313 	DBF_EVENT (DBF_INFO,
314                    "%04X: %04X on real %04X/%02X",
315                    rdc_data->vrdcdvno,
316                    rdc_data->vrdcvtyp,
317                    rdc_data->vrdccrty,
318                    rdc_data->vrdccrmd);
319 
320 
321 	/* Figure out position of label block */
322 	if (private->rdc_data.vrdcvcla == DEV_CLASS_FBA) {
323 		device->sizes.pt_block = 1;
324 	} else if (private->rdc_data.vrdcvcla == DEV_CLASS_ECKD ||
325 		   private->rdc_data.vrdcvcla == DEV_CLASS_CKD) {
326 		device->sizes.pt_block = 2;
327 	} else {
328                 BUG();
329 	}
330         label = (unsigned long *) get_free_page (GFP_KERNEL);
331         if (!label) {
332                 MESSAGE (KERN_WARNING, "%s",
333                          "No memory to allocate label struct");
334                 rc = -ENOMEM;
335                 goto fail;
336         }
337 	mdsk_term_io (device);	/* first terminate all outstanding operations */
338 	/* figure out blocksize of device */
339 	for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) {
340 		diag_bio_t *bio;
341 		diag_rw_io_t *iob = &private->iob;
342 
343 		rc = mdsk_init_io (device, bsize, 0, 64);
344 		if (rc > 4) {
345 			continue;
346 		}
347 		cqr = dasd_alloc_request (dasd_diag_discipline.name,
348                                          sizeof (diag_bio_t) / sizeof (ccw1_t),
349                                          0, device);
350 
351 		if (cqr == NULL) {
352 
353 			MESSAGE (KERN_WARNING, "%s",
354                                  "No memory to allocate initialization request");
355 
356                         rc = -ENOMEM;
357                         goto fail;
358 		}
359 		bio = (diag_bio_t *) (cqr->cpaddr);
360 		memset (bio, 0, sizeof (diag_bio_t));
361 		bio->type = MDSK_READ_REQ;
362 		bio->block_number = device->sizes.pt_block + 1;
363 		bio->buffer = __pa (label);
364 		cqr->device = device;
365 		cqr->status = CQR_STATUS_FILLED;
366 		memset (iob, 0, sizeof (diag_rw_io_t));
367 		iob->dev_nr = rdc_data->vrdcdvno;
368 		iob->block_count = 1;
369 		iob->interrupt_params = (u32)(addr_t) cqr;
370 		iob->bio_list = __pa (bio);
371 		rc = dia250 (iob, RW_BIO);
372 		dasd_free_request(cqr, device);
373 		if (rc == 0) {
374 			if (label[3] != bsize ||
375                             label[0] != 0xc3d4e2f1 ||	/* != CMS1 */
376                             label[13] == 0 ){
377                                 rc = -EMEDIUMTYPE;
378 				goto fail;
379 			}
380 			break;
381 		}
382 		mdsk_term_io (device);
383 	}
384         if (bsize > PAGE_SIZE) {
385                 rc = -EMEDIUMTYPE;
386 		goto fail;
387         }
388 	device->sizes.blocks = label[7];
389 	device->sizes.bp_block = bsize;
390 	device->sizes.s2b_shift = 0;	/* bits to shift 512 to get a block */
391 
392 	for (sb = 512; sb < bsize; sb = sb << 1)
393 
394 		device->sizes.s2b_shift++;
395 
396 	DEV_MESSAGE (KERN_INFO, device,
397                      "capacity (%dkB blks): %ldkB",
398                      (device->sizes.bp_block >> 10),
399                      (device->sizes.blocks << device->sizes.s2b_shift) >> 1);
400 
401         rc = 0;
402 	goto out;
403  fail:
404         if (device->private) {
405                 kfree (device->private);
406                 device->private = NULL;
407         }
408  out:
409         if (label) {
410                 free_page ((unsigned long) label);
411         }
412 	return rc;
413 }
414 
415 static int
dasd_diag_fill_geometry(struct dasd_device_t * device,struct hd_geometry * geo)416 dasd_diag_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
417 {
418 	int rc = 0;
419 	unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
420 	unsigned long tracks = sectors >> 6;
421 	unsigned long cyls = tracks >> 4;
422 
423 	switch (device->sizes.bp_block) {
424 	case 512:
425 	case 1024:
426 	case 2048:
427 	case 4096:
428 		break;
429 	default:
430 		return -EINVAL;
431 	}
432 	geo->cylinders = cyls;
433 	geo->heads = 16;
434 	geo->sectors = 128 >> device->sizes.s2b_shift;
435 	return rc;
436 }
437 
438 static dasd_era_t
dasd_diag_examine_error(ccw_req_t * cqr,devstat_t * stat)439 dasd_diag_examine_error (ccw_req_t * cqr, devstat_t * stat)
440 {
441 	return dasd_era_fatal;
442 }
443 
444 static ccw_req_t *
dasd_diag_build_cp_from_req(dasd_device_t * device,struct request * req)445 dasd_diag_build_cp_from_req (dasd_device_t * device, struct request *req)
446 {
447 	ccw_req_t *rw_cp = NULL;
448 	struct buffer_head *bh;
449 	int rw_cmd;
450 	int noblk = req->nr_sectors >> device->sizes.s2b_shift;
451 	int byt_per_blk = device->sizes.bp_block;
452 	int block;
453 	diag_bio_t *bio;
454 	int bhct;
455 	long size;
456         unsigned long reloc_sector = req->sector +
457                 device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
458 
459 	if (!noblk) {
460 
461 		MESSAGE (KERN_ERR, "%s",
462                          "No blocks to read/write...returning");
463 
464 		return ERR_PTR(-EINVAL);
465 	}
466 	if (req->cmd == READ) {
467 		rw_cmd = MDSK_READ_REQ;
468 	} else {
469 		rw_cmd = MDSK_WRITE_REQ;
470 	}
471 	bhct = 0;
472 	for (bh = req->bh; bh; bh = bh->b_reqnext) {
473 		if (bh->b_size < byt_per_blk)
474                         BUG();
475                 bhct += bh->b_size >> (device->sizes.s2b_shift+9);
476 	}
477 	/* Build the request */
478 	rw_cp = dasd_alloc_request (dasd_diag_discipline.name, bhct << 1, 0, device);
479 	if (!rw_cp) {
480 		return  ERR_PTR(-ENOMEM);
481 	}
482 	bio = (diag_bio_t *) (rw_cp->cpaddr);
483 
484 	block = reloc_sector >> device->sizes.s2b_shift;
485 	for (bh = req->bh; bh; bh = bh->b_reqnext) {
486                 memset (bio, 0, sizeof (diag_bio_t));
487                 for (size = 0; size < bh->b_size; size += byt_per_blk) {
488                         bio->type = rw_cmd;
489                         bio->block_number = block + 1;
490                         bio->buffer = __pa (bh->b_data + size);
491                         bio++;
492                         block++;
493                 }
494 	}
495 
496 	rw_cp->buildclk = get_clock ();
497 
498 	rw_cp->device = device;
499 	rw_cp->expires = 50 * TOD_SEC;	/* 50 seconds */
500 	rw_cp->req = req;
501 	check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
502 	return rw_cp;
503 }
504 
505 static int
dasd_diag_fill_info(dasd_device_t * device,dasd_information2_t * info)506 dasd_diag_fill_info (dasd_device_t * device, dasd_information2_t * info)
507 {
508 	int rc = 0;
509 	info->FBA_layout = 1;
510 	info->format = DASD_FORMAT_LDL;
511 	info->characteristics_size = sizeof (diag210_t);
512 	memcpy (info->characteristics,
513 		&((dasd_diag_private_t *) device->private)->rdc_data,
514 		sizeof (diag210_t));
515 	info->confdata_size = 0;
516 	return rc;
517 }
518 
519 
520 /*
521  * max_blocks: We want to fit one CP into one page of memory so that we can
522  * effectively use available resources.
523  * The ccw_req_t has less than 256 bytes (including safety)
524  * and diag_bio_t for each block has 16 bytes.
525  * That makes:
526  * (4096 - 256) / 16 = 240 blocks at maximum.
527  */
528 dasd_discipline_t dasd_diag_discipline = {
529         owner: THIS_MODULE,
530 	name:"DIAG",
531 	ebcname:"DIAG",
532 	max_blocks:240,
533 	check_characteristics:dasd_diag_check_characteristics,
534 	fill_geometry:dasd_diag_fill_geometry,
535 	start_IO:dasd_start_diag,
536 	examine_error:dasd_diag_examine_error,
537 	build_cp_from_req:dasd_diag_build_cp_from_req,
538 	int_handler:(void *) dasd_ext_handler,
539 	fill_info:dasd_diag_fill_info,
540 	list:LIST_HEAD_INIT(dasd_diag_discipline.list),
541 };
542 
543 int
dasd_diag_init(void)544 dasd_diag_init (void)
545 {
546 	int rc = 0;
547 
548 	if (MACHINE_IS_VM) {
549 
550 		MESSAGE (KERN_INFO,
551                          "%s discipline initializing",
552                          dasd_diag_discipline.name);
553 
554 		ASCEBC (dasd_diag_discipline.ebcname, 4);
555 		ctl_set_bit (0, 9);
556 		register_external_interrupt (0x2603, dasd_ext_handler);
557 		dasd_discipline_add (&dasd_diag_discipline);
558 	} else {
559 
560 		MESSAGE (KERN_INFO,
561 			"Machine is not VM: %s discipline not initializing",
562                          dasd_diag_discipline.name);
563 
564 		rc = -EINVAL;
565 	}
566 	return rc;
567 }
568 
569 void
dasd_diag_cleanup(void)570 dasd_diag_cleanup (void)
571 {
572 	if (MACHINE_IS_VM) {
573 
574 		MESSAGE (KERN_INFO,
575                          "%s discipline cleaning up",
576                          dasd_diag_discipline.name);
577 
578 		dasd_discipline_del (&dasd_diag_discipline);
579 		unregister_external_interrupt (0x2603, dasd_ext_handler);
580 		ctl_clear_bit (0, 9);
581 	} else {
582 
583 		MESSAGE (KERN_INFO,
584                          "Machine is not VM: %s discipline not initializing",
585                          dasd_diag_discipline.name);
586 	}
587 }
588 
589 #ifdef MODULE
590 int
init_module(void)591 init_module (void)
592 {
593 	int rc = 0;
594 	rc = dasd_diag_init ();
595 	return rc;
596 }
597 
598 void
cleanup_module(void)599 cleanup_module (void)
600 {
601 	dasd_diag_cleanup ();
602 	return;
603 }
604 #endif
605 
606 /*
607  * Overrides for Emacs so that we follow Linus's tabbing style.
608  * Emacs will notice this stuff at the end of the file and automatically
609  * adjust the settings for this buffer only.  This must remain at the end
610  * of the file.
611  * ---------------------------------------------------------------------------
612  * Local variables:
613  * c-indent-level: 4
614  * c-brace-imaginary-offset: 0
615  * c-brace-offset: -4
616  * c-argdecl-indent: 4
617  * c-label-offset: -4
618  * c-continued-statement-offset: 4
619  * c-continued-brace-offset: 0
620  * indent-tabs-mode: nil
621  * tab-width: 8
622  * End:
623  */
624