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