1 /* CCKDDIAG.C   (c) Copyright Greg Smith, 2000-2009                  */
2 /*              (c) Copyright James M. Morrison, 2003                */
3 /*              CCKD diagnostic tool                                 */
4 
5 /* 2003-02-07 James M. Morrison initial implementation               */
6 /* portions borrowed from cckdcdsk & other CCKD code                 */
7 
8 /*-------------------------------------------------------------------*/
9 /* Diagnostic tool to display various CCKD data                      */
10 /*-------------------------------------------------------------------*/
11 
12 #include "hstdinc.h"
13 
14 /* TODO: add FBA support or write cfbadiag                           */
15 
16 #include "hercules.h"
17 #include "dasdblks.h"                   /* data_dump                 */
18 
19 typedef struct _CKD_RECSTAT {     /* CKD DASD record stats           */
20         int    cc;                /* CC cylinder # (relative zero)   */
21         int    hh;                /* HH head # (relative zero)       */
22         int    r;                 /* Record # (relative zero)        */
23         int    kl;                /* key length                      */
24         int    dl;                /* data length                     */
25 } CKD_RECSTAT;
26 
27 /*-------------------------------------------------------------------*/
28 /* Global data areas                                                 */
29 /*-------------------------------------------------------------------*/
30 CCKD_L1ENT     *l1 = NULL;              /* L1TAB                     */
31 CCKD_L2ENT     *l2 = NULL;              /* L2TAB                     */
32 void           *tbuf = NULL;            /* track header & data       */
33 void           *bulk = NULL;            /* bulk data buffer          */
34 int             fd = 0;                 /* File descriptor           */
35 static  BYTE eighthexFF[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
36 
37 #ifdef DEBUG
38         int     debug = 1;              // enable debug code
39 #else
40         int     debug = 0;              // disable debug code
41 #endif
42         int     pausesnap = 0;          // 1 = pause after snap (getc)
43 
44 /*-------------------------------------------------------------------*/
45 /* print syntax                                                      */
46 /*-------------------------------------------------------------------*/
syntax()47 int syntax()
48 {
49     fprintf (stdout, "\n"
50         "cckddiag [options] file-name\n"
51         "Valid options are one or more of the following:\n"
52         "  -v        display version and exit\n"
53         "  -d        display DEVHDR\n"
54         "  -c        display CDEVHDR\n"
55         "  -1        display L1TAB (numeric one)\n"
56         "  -g        enable debug output\n"
57         "CKD track related options:\n"
58         "  -a cc hh  display absolute CCHH data\n"
59         "  -r tt     display relative TT data\n"
60         "  -2        display L2TAB related to -a or -r\n"
61         "  -t        display track data\n"
62         "  -x        hex display track key/data\n"
63         "Offset option:\n"
64         "  -o oo ll  hex display data at offset oo of length ll\n"
65         "Further information: man 1 cckddiag\n"
66         );
67     return -1;
68 }
69 
70 /*-------------------------------------------------------------------*/
71 /* snap - display msg, dump data                                     */
72 /*-------------------------------------------------------------------*/
73 /* Newline appended to message                                       */
snap(char * msg,void * data,int len)74 void snap(char *msg, void *data, int len) {
75 int    x;
76 
77     if (msg != NULL)
78         fprintf(stdout, "%s\n", msg);
79     data_dump(data, len);
80     if (pausesnap) {
81         fprintf(stdout, "Press enter to continue\n");
82         x = getc(stdin);
83     }
84 }
85 
86 /*-------------------------------------------------------------------*/
87 /* clean - cleanup before exit                                       */
88 /*-------------------------------------------------------------------*/
clean(void)89 void clean(void) {
90     close(fd);
91     free(l1);                          /* L1TAB buffer               */
92     free(l2);                          /* L2TAB buffer               */
93     free(tbuf);                        /* track and header buffer    */
94     free(bulk);                        /* offset data buffer         */
95 }
96 
97 /*-------------------------------------------------------------------*/
98 /* makbuf - allocate buffer, handle errors (exit if any)             */
99 /*-------------------------------------------------------------------*/
makbuf(int len,char * label)100 void *makbuf(int len, char *label) {
101     void *p;
102 
103     p = malloc(len);
104     if (p == NULL) {
105         fprintf(stdout, "malloc %s of length %d failed\n",
106                 label, len);
107         clean();
108         exit(4);
109     }
110     if (debug) fprintf(stdout, "\n"
111                "MAKBUF malloc %s buffer of %d bytes at %p\n",
112                label, len, p);
113     return p;
114 }
115 
116 /*-------------------------------------------------------------------*/
117 /* readpos - common lseek and read invocation with error handling    */
118 /*-------------------------------------------------------------------*/
119 /* This code exits on error rather than return to caller.            */
readpos(int fd,void * buf,off_t offset,size_t len)120 int readpos(
121             int fd,               /* opened CCKD image file          */
122             void *buf,            /* buffer of size len              */
123             off_t offset,         /* offset into CCKD image to read  */
124             size_t len            /* length of data to read          */
125             ) {
126     if (debug)
127         fprintf(stdout, "\nREADPOS seeking %d (0x%8.8X)\n",
128                         (int)offset, (unsigned int)offset);
129     if (lseek(fd, offset, SEEK_SET) < 0) {
130         fprintf(stdout, _("lseek to pos 0x%8.8x error: %s\n"),
131                         (unsigned int) offset, strerror(errno));
132         clean();
133         exit (1);
134     }
135     if (debug)
136         fprintf(stdout,
137                 "READPOS reading buf addr "PTR_FMTx" length %"SIZE_T_FMT"d (0x"SIZE_T_FMTX")\n",
138                 (uintptr_t)buf, len, len);
139     if (read(fd, buf, len) < (ssize_t)len) {
140         fprintf(stdout, _("cckddiag: read error: %s\n"),
141                         strerror(errno));
142         clean();
143         exit (2);
144     }
145     return 0;
146 }
147 
148 /*-------------------------------------------------------------------*/
149 /* decomptrk - decompress track data                                 */
150 /*-------------------------------------------------------------------*/
decomptrk(BYTE * ibuf,int ibuflen,BYTE * obuf,int obuflen,int heads,int trk,char * msg)151 int decomptrk(
152               BYTE *ibuf,         /* input buffer address            */
153               int ibuflen,        /* input buffer length             */
154               BYTE *obuf,         /* output buffer address           */
155               int obuflen,        /* output buffer length            */
156               int heads,          /* >=0 means CKD, else FBA         */
157               int trk,            /* relative track or block number  */
158               char *msg           /* addr of 80 byte msg buf or NULL */
159              )
160 /* ibuf points at CKDDASD_TRKHDR header followed by track data       */
161 /* ibuflen specifies length of TRKHDR and data                       */
162 /* This code based on decompression logic in cdsk_valid_trk.         */
163 /* Returns length of decompressed data or -1 on error.               */
164 {
165 #if defined( HAVE_LIBZ ) || defined( CCKD_BZIP2 )
166 int             rc;                     /* Return code               */
167 BYTE           *bufp;                   /* Buffer pointer            */
168 #endif
169 size_t          bufl;                   /* Buffer length             */
170 #ifdef CCKD_BZIP2
171 unsigned int    ubufl;                  /* when size_t != unsigned int */
172 #endif
173 
174 #if !defined( HAVE_LIBZ ) && !defined( CCKD_BZIP2 )
175     UNREFERENCED(heads);
176     UNREFERENCED(trk);
177     UNREFERENCED(msg);
178 #endif
179 
180     memset(obuf, 0x00, obuflen);  /* clear output buffer             */
181 
182     /* Uncompress the track/block image */
183     switch (ibuf[0] & CCKD_COMPRESS_MASK) {
184 
185     case CCKD_COMPRESS_NONE:
186         bufl = (ibuflen < obuflen) ? ibuflen : obuflen;
187         memcpy (obuf, ibuf, bufl);
188         break;
189 
190 #ifdef HAVE_LIBZ
191     case CCKD_COMPRESS_ZLIB:
192         bufp = (BYTE *)obuf;
193         memcpy (obuf, ibuf, CKDDASD_TRKHDR_SIZE);
194         bufl = obuflen - CKDDASD_TRKHDR_SIZE;
195         rc = uncompress(&obuf[CKDDASD_TRKHDR_SIZE],
196                          (void *)&bufl,
197                          &ibuf[CKDDASD_TRKHDR_SIZE],
198                          ibuflen);
199         if (rc != Z_OK) {
200             if (msg)
201                 snprintf(msg, 80, "%s %d uncompress error, rc=%d;"
202                          "%2.2x%2.2x%2.2x%2.2x%2.2x",
203                          heads >= 0 ? "trk" : "blk", trk, rc,
204                          ibuf[0], ibuf[1], ibuf[2], ibuf[3], ibuf[4]);
205             return -1;
206         }
207         bufl += CKDDASD_TRKHDR_SIZE;
208         break;
209 #endif
210 
211 #ifdef CCKD_BZIP2
212     case CCKD_COMPRESS_BZIP2:
213         bufp = obuf;
214         memcpy(obuf, ibuf, CKDDASD_TRKHDR_SIZE);
215         ubufl = obuflen - CKDDASD_TRKHDR_SIZE;
216         rc = BZ2_bzBuffToBuffDecompress (
217                  (char *)&obuf[CKDDASD_TRKHDR_SIZE],
218                  &ubufl,
219                  (char *)&ibuf[CKDDASD_TRKHDR_SIZE],
220                  ibuflen, 0, 0);
221         if (rc != BZ_OK) {
222             if (msg)
223                 snprintf(msg, 80, "%s %d decompress error, rc=%d;"
224                          "%2.2x%2.2x%2.2x%2.2x%2.2x",
225                          heads >= 0 ? "trk" : "blk", trk, rc,
226                          ibuf[0], ibuf[1], ibuf[2], ibuf[3], ibuf[4]);
227             return -1;
228         }
229         bufl=ubufl;
230         bufl += CKDDASD_TRKHDR_SIZE;
231         break;
232 #endif
233 
234     default:
235         return -1;
236 
237     } /* switch (buf[0] & CCKD_COMPRESS_MASK) */
238     return bufl;
239 }
240 
241 /*-------------------------------------------------------------------*/
242 /* show_ckd_count - display CKD dasd record COUNT field              */
243 /*-------------------------------------------------------------------*/
244 /* RECHDR is stored in big-endian byte order.                        */
show_ckd_count(CKDDASD_RECHDR * rh,int trk)245 BYTE *show_ckd_count(CKDDASD_RECHDR *rh, int trk) {
246 int     cc, hh, r, kl, dl;
247 BYTE    *past;
248 
249     cc = (rh->cyl[0] << 8) | (rh->cyl[1]);
250     hh = (rh->head[0] << 8) | (rh->head[1]);
251     r  = rh->rec;
252     kl = rh->klen;
253     dl = (rh->dlen[0] << 8) | (rh->dlen[1]);
254     fprintf(stdout, "\n"
255             "Track %d COUNT "
256             "CC=%d HH=%d R=%d KL=%d DL=%d\n",
257             trk, cc, hh, r, kl, dl);
258     past = (BYTE *)rh + sizeof(CKDDASD_RECHDR);
259     return past;
260 }
261 
262 /*-------------------------------------------------------------------*/
263 /* show_ckd_key - display CKD dasd record KEY field                  */
264 /*-------------------------------------------------------------------*/
show_ckd_key(CKDDASD_RECHDR * rh,BYTE * buf,int trk,int xop)265 BYTE *show_ckd_key(CKDDASD_RECHDR *rh, BYTE *buf, int trk, int xop) {
266 
267     if (rh->klen && xop) {
268         fprintf(stdout,
269                 "\nTrack %d R%d KEY (%d bytes)\n",
270                 trk, rh->rec, rh->klen);
271         data_dump(buf, rh->klen);
272     }
273     return (BYTE *)buf + rh->klen;          /* skip past KEY field */
274 }
275 
276 /*-------------------------------------------------------------------*/
277 /* show_ckd_data - display CKD dasd record DATA field                */
278 /*-------------------------------------------------------------------*/
show_ckd_data(CKDDASD_RECHDR * rh,BYTE * buf,int trk,int xop)279 BYTE *show_ckd_data(CKDDASD_RECHDR *rh, BYTE *buf, int trk, int xop) {
280 int     dl;
281 
282     dl = (rh->dlen[0] << 8) | (rh->dlen[1]);
283     if (dl && xop) {
284         fprintf(stdout,
285                 "\nTrack %d R%d DATA (%d bytes)\n",
286                 trk, rh->rec, dl);
287         data_dump(buf, dl);
288     }
289     return buf + dl;                        /* skip past DATA field */
290 }
291 
292 /*-------------------------------------------------------------------*/
293 /* showtrk - display track data                                      */
294 /*-------------------------------------------------------------------*/
295 /* This code mimics selected logic in cdsk_valid_trk.                */
showtrk(CKDDASD_TRKHDR * buf,int imglen,int trk,int xop)296 void showtrk(
297              CKDDASD_TRKHDR *buf, /* track header ptr                */
298              int imglen,          /* TRKHDR + track user data length */
299              int trk,             /* relative track number           */
300              int xop              /* 1=dump key & data blks; 0=don't */
301              ) {
302 BYTE            buf2[64*1024];         /* max uncompressed buffer    */
303 char            msg[81];               /* error message buffer       */
304 CKDDASD_RECHDR  *rh;                   /* CCKD COUNT field           */
305 BYTE            *bufp;
306 int             len;
307 
308     if (debug)
309        snap("\nSHOWTRK Compressed track header and data", buf, imglen);
310     len = decomptrk(
311               (BYTE *)buf,        /* input buffer address            */
312               imglen,             /* input buffer length             */
313               buf2,               /* output buffer address           */
314               sizeof(buf2),       /* output buffer length            */
315               1,                  /* >=0 means CKD, else FBA         */
316               trk,                /* relative track or block number  */
317               msg                 /* addr of message buffer          */
318               );
319     if (debug)
320        snap("\nSHOWTRK Decompressed track header and data", buf2, len);
321     bufp = &buf2[sizeof(CKDDASD_TRKHDR)];
322     while (bufp < &buf2[sizeof(buf2)]) {
323         rh = (CKDDASD_RECHDR *)bufp;
324         if (memcmp((BYTE *)rh, &eighthexFF, 8) == 0) {
325             fprintf(stdout, "\nEnd of Track\n");
326             break;
327         }
328         bufp = show_ckd_count(rh, trk);
329         bufp = show_ckd_key(rh, bufp, trk, xop);
330         bufp = show_ckd_data(rh, bufp, trk, xop);
331     }
332 }
333 
334 /*-------------------------------------------------------------------*/
335 /* offtify - given decimal or hex input string, return off_t         */
336 /* Locale independent, does not check for overflow                   */
337 /* References <ctype.h> and <string.h>                               */
338 /*-------------------------------------------------------------------*/
339 /* Based on code in P. J. Plauger's "The Standard C Library"         */
340 /* See page 34, in Chapter 2 (ctype.h)                               */
offtify(char * s)341 off_t offtify(char *s) {
342 
343 static const char  xd[] = {"0123456789abcdefABCDEF"};
344 static const char  xv[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
345                            10, 11, 12, 13, 14, 15,
346                            10, 11, 12, 13, 14, 15};
347 off_t              v;
348 char               *p;
349 
350         p = s;
351         if ( (*s == '0') && (*(s+1) == 'x') ) {
352             s = s + 2;
353             for (v = 0; isxdigit(*s); ++s)
354                 v = (v << 4) + xv[strchr(xd, *s) - xd];
355             if (debug)
356                 fprintf(stdout,
357                         "OFFTIFY string %s hex %8.8" I64_FMT "X decimal %" I64_FMT "d\n",
358                         p, (U64)v, (U64)v);
359             return v;
360         } else {                                 /* decimal input */
361             v = (off_t) atoll(s);
362             if (debug)
363                 fprintf(stdout,
364                         "OFFTIFY string %s decimal %" I64_FMT "X %" I64_FMT "d\n",
365                         p, (U64)v, (U64)v);
366             return v;
367         }
368 }
369 
370 /*-------------------------------------------------------------------*/
371 /* Main function for CCKD diagnostics                                */
372 /*-------------------------------------------------------------------*/
main(int argc,char * argv[])373 int main (int argc, char *argv[])
374 {
375 int             cckd_diag_rc = 0;       /* Program return code       */
376 char           *fn;                     /* File name                 */
377 
378 CKDDASD_DEVHDR  devhdr;                 /* [C]CKD device hdr         */
379 CCKDDASD_DEVHDR cdevhdr;                /* Compressed CKD device hdr */
380 CKDDEV         *ckd=0;                  /* CKD DASD table entry      */
381 FBADEV         *fba=0;                  /* FBA DASD table entry      */
382 
383 int             cmd_devhdr = 0;         /* display DEVHDR            */
384 int             cmd_cdevhdr = 0;        /* display CDEVHDR           */
385 int             cmd_l1tab = 0;          /* display L1TAB             */
386 int             cmd_l2tab = 0;          /* display L2TAB             */
387 int             cmd_trkdata = 0;        /* display track data        */
388 int             cmd_hexdump = 0;        /* display track data (hex)  */
389 
390 int             cmd_offset = 0;         /* 1 = display data at       */
391 int             op_offset = 0;          /* op_offset of length       */
392 int             op_length = 0;          /* op_length                 */
393 
394 int             cmd_cchh = 0;           /* 1 = display CCHH data     */
395 int             op_cc = 0;              /* CC = cylinder             */
396 int             op_hh = 0;              /* HH = head                 */
397 
398 int             cmd_tt = 0;             /* 1 = display TT data       */
399 int             op_tt = 0;              /* relative track #          */
400 
401 int             swapend;                /* 1 = New endianess doesn't
402                                              match machine endianess */
403 int             n, trk=0, l1ndx=0, l2ndx=0;
404 off_t           l2taboff=0;             /* offset to assoc. L2 table */
405 int             ckddasd;                /* 1=CKD dasd  0=FBA dasd    */
406 int             heads=0;                /* Heads per cylinder        */
407 int             blks;                   /* Number fba blocks         */
408 off_t           trkhdroff=0;            /* offset to assoc. trk hdr  */
409 int             imglen=0;               /* track length              */
410 char            pathname[MAX_PATH];     /* file path in host format  */
411 
412     INITIALIZE_UTILITY("cckddiag");
413 
414     /* parse the arguments */
415     argc--;
416     argv++ ;
417     while (argc > 0) {
418         if(**argv != '-') break;
419 
420         switch(argv[0][1]) {
421             case 'v':  if (argv[0][2] != '\0') return syntax ();
422                        display_version (stdout,
423                               "Hercules CCKD diagnostic program\n", FALSE);
424                        return 0;
425             case 'd':  if (argv[0][2] != '\0') return syntax ();
426                        cmd_devhdr = 1;
427                        break;
428             case 'c':  if (argv[0][2] != '\0') return syntax ();
429                        cmd_cdevhdr = 1;
430                        break;
431             case '1':  if (argv[0][2] != '\0') return syntax ();
432                        cmd_l1tab = 1;
433                        break;
434             case '2':  if (argv[0][2] != '\0') return syntax ();
435                        cmd_l2tab = 1;
436                        break;
437             case 'a':  if (argv[0][2] != '\0') return syntax ();
438                        cmd_cchh = 1;
439                        argc--; argv++;
440                        op_cc = offtify(*argv);
441                        argc--; argv++;
442                        op_hh = offtify(*argv);
443                        break;
444             case 'r':  if (argv[0][2] != '\0') return syntax ();
445                        cmd_tt = 1;
446                        argc--; argv++;
447                        op_tt = offtify(*argv);
448                        break;
449             case 'o':  if (argv[0][2] != '\0') return syntax ();
450                        cmd_offset = 1;
451                        argc--;
452                        argv++;
453                        op_offset = offtify(*argv);
454                        argc--;
455                        argv++;
456                        op_length = offtify(*argv);
457                        break;
458             case 't':  if (argv[0][2] != '\0') return syntax ();
459                        cmd_trkdata = 1;
460                        break;
461             case 'x':  if (argv[0][2] != '\0') return syntax ();
462                        cmd_hexdump = 1;
463                        cmd_trkdata = 1;
464                        break;
465             case 'g':  if (argv[0][2] != '\0') return syntax ();
466                        debug = 1;
467                        break;
468             default:   return syntax ();
469         }
470         argc--;
471         argv++;
472     }
473     if (argc != 1) return syntax ();
474     fn = argv[0];
475 
476     /* open the file */
477     hostpath(pathname, fn, sizeof(pathname));
478     fd = hopen(pathname, O_RDONLY | O_BINARY);
479     if (fd < 0) {
480         fprintf(stdout,
481                 _("cckddiag: error opening file %s: %s\n"),
482                 fn, strerror(errno));
483         return -1;
484     }
485 
486     /*---------------------------------------------------------------*/
487     /* display DEVHDR - first 512 bytes of dasd image                */
488     /*---------------------------------------------------------------*/
489     readpos(fd, &devhdr, 0, sizeof(devhdr));
490     if (cmd_devhdr) {
491         fprintf(stdout, "\nDEVHDR - %"SIZE_T_FMT"d (decimal) bytes:\n",
492                 sizeof(devhdr));
493         data_dump(&devhdr, sizeof(devhdr));
494     }
495 
496     /*---------------------------------------------------------------*/
497     /* Determine CKD or FBA device type                              */
498     /*---------------------------------------------------------------*/
499     if (memcmp(devhdr.devid, "CKD_C370", 8) == 0
500        || memcmp(devhdr.devid, "CKD_S370", 8) == 0) {
501         ckddasd = 1;
502         ckd = dasd_lookup(DASD_CKDDEV, NULL, devhdr.devtype, 0);
503         if (ckd == NULL) {
504             fprintf(stdout,
505                     "DASD table entry not found for devtype 0x%2.2X\n",
506                     devhdr.devtype);
507             clean();
508             exit(5);
509         }
510     }
511     else
512         if (memcmp(devhdr.devid, "FBA_C370", 8) == 0
513            || memcmp(devhdr.devid, "FBA_S370", 8) == 0) {
514         ckddasd = 0;
515         fba = dasd_lookup(DASD_FBADEV, NULL, devhdr.devtype, 0);
516         if (fba == NULL) {
517             fprintf(stdout,
518                     "DASD table entry not found for "
519                     "devtype 0x%2.2X\n",
520                     DEFAULT_FBA_TYPE);
521             clean();
522             exit(6);
523         }
524     }
525     else {
526         fprintf(stdout, "incorrect header id\n");
527         clean();
528         return -1;
529     }
530 
531     /*---------------------------------------------------------------*/
532     /* Set up device characteristics                                 */
533     /*---------------------------------------------------------------*/
534     if (ckddasd) {
535         heads = ((U32)(devhdr.heads[3]) << 24)
536               | ((U32)(devhdr.heads[2]) << 16)
537               | ((U32)(devhdr.heads[1]) << 8)
538               | (U32)(devhdr.heads[0]);
539         if (debug)
540             fprintf(stdout,
541                 "\n%s device has %d heads/cylinder\n",
542                 ckd->name, heads);
543     } else {
544         blks  = 0;
545       #if 0 /* cdevhdr is uninitialized and blks is never referenced... */
546         blks  = ((U32)(cdevhdr.cyls[0]) << 24)
547               | ((U32)(cdevhdr.cyls[2]) << 16)
548               | ((U32)(cdevhdr.cyls[1]) << 8)
549               | (U32)(cdevhdr.cyls[0]);
550       #endif
551     }
552 
553     /*---------------------------------------------------------------*/
554     /* display CDEVHDR - follows DEVHDR                              */
555     /*---------------------------------------------------------------*/
556     readpos(fd, &cdevhdr, CKDDASD_DEVHDR_SIZE, sizeof(cdevhdr));
557     if (cmd_cdevhdr) {
558         fprintf(stdout, "\nCDEVHDR - %"SIZE_T_FMT"d (decimal) bytes:\n",
559                 sizeof(cdevhdr));
560         data_dump(&cdevhdr, sizeof(cdevhdr));
561     }
562 
563     /*---------------------------------------------------------------*/
564     /* Find machine endian-ness                                      */
565     /*---------------------------------------------------------------*/
566     /* cckd_endian() returns 1 for big-endian machines               */
567     swapend = (cckd_endian() !=
568                ((cdevhdr.options & CCKD_BIGENDIAN) != 0));
569 
570     /*---------------------------------------------------------------*/
571     /* display L1TAB - follows CDEVHDR                               */
572     /*---------------------------------------------------------------*/
573     /* swap numl1tab if needed */
574     n = cdevhdr.numl1tab;
575     if (swapend) cckd_swapend4((char *)&n);
576 
577     l1 = makbuf(n * CCKD_L1ENT_SIZE, "L1TAB");
578     readpos(fd, l1, CCKD_L1TAB_POS, n * CCKD_L1ENT_SIZE);
579     /* L1TAB itself is not adjusted for endian-ness                  */
580     if (cmd_l1tab) {
581         fprintf(stdout, "\nL1TAB - %"SIZE_T_FMT"d (0x"SIZE_T_FMTX") bytes:\n",
582                 (n * CCKD_L1ENT_SIZE), (n * CCKD_L1ENT_SIZE));
583         data_dump(l1, n * CCKD_L1ENT_SIZE);
584     }
585 
586     /*---------------------------------------------------------------*/
587     /* display OFFSET, LENGTH data                                   */
588     /*---------------------------------------------------------------*/
589     if (cmd_offset) {
590         bulk = makbuf(op_length, "BULK");
591         readpos(fd, bulk, op_offset, op_length);
592         fprintf(stdout,
593             "\nIMAGE OFFSET %d (0x%8.8X) "
594             "of length %d (0x%8.8X) bytes:\n",
595             op_offset, op_offset, op_length, op_length);
596         data_dump(bulk, op_length);
597         free(bulk);
598         bulk = NULL;
599     }
600 
601     /*---------------------------------------------------------------*/
602     /* FBA isn't supported here because I don't know much about FBA  */
603     /*---------------------------------------------------------------*/
604     if ( (!ckddasd) && ((cmd_cchh) || (cmd_tt)) ) {
605         fprintf(stdout, "CCHH/reltrk not supported for FBA\n");
606         clean();
607         exit(3);
608     }
609 
610     /*---------------------------------------------------------------*/
611     /* Setup CCHH or relative track request                          */
612     /*---------------------------------------------------------------*/
613     if (ckddasd) {
614         if (cmd_tt) {
615             trk = op_tt;
616             op_cc = trk / heads;
617             op_hh = trk % heads;
618         } else {
619             trk = (op_cc * heads) + op_hh;
620         }
621         l1ndx = trk / cdevhdr.numl2tab;
622         l2ndx = trk % cdevhdr.numl2tab;
623         l2taboff = l1[l1ndx];
624         if (swapend) cckd_swapend4((char *)&l2taboff);
625     }
626 
627     /*---------------------------------------------------------------*/
628     /* display CKD CCHH or relative track data                       */
629     /*---------------------------------------------------------------*/
630     if ((cmd_cchh) || (cmd_tt)) {
631         fprintf(stdout,
632                 "CC %d HH %d = reltrk %d; "
633                 "L1 index = %d, L2 index = %d\n"
634                 "L1 index %d = L2TAB offset %d (0x%8.8X)\n",
635                 op_cc, op_hh, trk,
636                 l1ndx, l2ndx,
637                 l1ndx, (int)l2taboff, (int)l2taboff);
638         l2 = makbuf(cdevhdr.numl2tab * sizeof(CCKD_L2ENT), "L2TAB");
639         readpos(fd, l2, l2taboff,
640                 cdevhdr.numl2tab * sizeof(CCKD_L2ENT));
641         if (cmd_l2tab) {
642             fprintf(stdout,
643                    "\nL2TAB - %"SIZE_T_FMT"d (decimal) bytes\n",
644                    (cdevhdr.numl2tab * sizeof(CCKD_L2ENT)));
645             data_dump(l2, (cdevhdr.numl2tab * sizeof(CCKD_L2ENT)) );
646         }
647         fprintf(stdout, "\nL2 index %d = L2TAB entry %"SIZE_T_FMT"d bytes\n",
648                l2ndx, sizeof(CCKD_L2ENT) );
649         data_dump(&l2[l2ndx], sizeof(CCKD_L2ENT) );
650         trkhdroff = l2[l2ndx].pos;
651         imglen = l2[l2ndx].len;
652         if (swapend) {
653             cckd_swapend4((char *)&trkhdroff);
654             cckd_swapend4((char *)&imglen);
655         }
656         fprintf(stdout, "\nTRKHDR offset %d (0x%8.8X); "
657                 "length %d (0x%4.4X)\n",
658                 (int)trkhdroff, (int)trkhdroff, imglen, imglen);
659         tbuf = makbuf(imglen, "TRKHDR+DATA");
660         readpos(fd, tbuf, trkhdroff, imglen);
661         fprintf(stdout, "\nTRKHDR track %d\n", trk);
662         data_dump(tbuf, sizeof(CKDDASD_TRKHDR) );
663         if (cmd_trkdata) showtrk(tbuf, imglen, trk, cmd_hexdump);
664         free(l2); free(tbuf);
665         l2 = NULL; tbuf = NULL;
666     }
667 
668     /* Close file, exit */
669     fprintf(stdout, "\n");
670     clean();
671     return cckd_diag_rc;
672 }
673 
674