forked from SDL-Hercules-390/hyperion
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ckddasd.c
6200 lines (5421 loc) · 230 KB
/
ckddasd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
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
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
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
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
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
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* CKDDASD.C (C) Copyright Roger Bowler, 1999-2012 */
/* ESA/390 CKD Direct Access Storage Device Handler */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/*-------------------------------------------------------------------*/
/* This module contains device handling functions for emulated */
/* count-key-data direct access storage devices. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Reference information: */
/* GA32-0099 IBM 3990 Storage Control Reference (Models 1, 2, and 3) */
/* GA32-0274 IBM 3990,9390 Storage Control Reference */
/* GC26-7006 IBM RAMAC Array Subsystem Reference */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Additional credits: */
/* Write Update Key and Data CCW by Jan Jaeger */
/* Track overflow support added by Jay Maynard */
/* Track overflow fixes by Jay Maynard, suggested by Valery */
/* Pogonchenko */
/* Track overflow write fix by Roger Bowler, thanks to Valery */
/* Pogonchenko and Volker Bandke V1.71 16/01/2001 */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#define _CKDDASD_C_
#define _HDASD_DLL_
#include "hercules.h"
#include "devtype.h"
#include "sr.h"
#include "dasdblks.h"
#include "cckddasd.h"
#include "ccwarn.h"
/*-------------------------------------------------------------------*/
/* Bit definitions for File Mask */
/*-------------------------------------------------------------------*/
#define CKDMASK_WRCTL 0xC0 /* Write control bits... */
#define CKDMASK_WRCTL_INHWR0 0x00 /* ...inhibit write HA/R0 */
#define CKDMASK_WRCTL_INHWRT 0x40 /* ...inhibit all writes */
#define CKDMASK_WRCTL_ALLWRU 0x80 /* ...write update only */
#define CKDMASK_WRCTL_ALLWRT 0xC0 /* ...allow all writes */
#define CKDMASK_RESV 0x20 /* Reserved bits - must be 0 */
#define CKDMASK_SKCTL 0x18 /* Seek control bits... */
#define CKDMASK_SKCTL_ALLSKR 0x00 /* ...allow all seek/recalib */
#define CKDMASK_SKCTL_CYLHD 0x08 /* ...allow seek cyl/head */
#define CKDMASK_SKCTL_HEAD 0x10 /* ...allow seek head only */
#define CKDMASK_SKCTL_INHSMT 0x18 /* ...inhibit seek and MT */
#define CKDMASK_AAUTH 0x06 /* Access auth bits... */
#define CKDMASK_AAUTH_NORMAL 0x00 /* ...normal authorization */
#define CKDMASK_AAUTH_DSF 0x02 /* ...device support auth */
#define CKDMASK_AAUTH_DIAG 0x04 /* ...diagnostic auth */
#define CKDMASK_AAUTH_DSFNCR 0x06 /* ...device support with no
correction or retry */
#define CKDMASK_PCI_FETCH 0x01 /* PCI fetch mode */
/*-------------------------------------------------------------------*/
/* Bit definitions for Define Extent global attributes byte */
/*-------------------------------------------------------------------*/
#define CKDGATR_ARCH 0xC0 /* Architecture mode... */
#define CKDGATR_ARCH_ECKD 0xC0 /* ...extended CKD mode */
#define CKDGATR_CKDCONV 0x20 /* CKD conversion mode */
#define CKDGATR_SSOP 0x1C /* Subsystem operation mode..*/
#define CKDGATR_SSOP_NORMAL 0x00 /* ...normal cache */
#define CKDGATR_SSOP_BYPASS 0x04 /* ...bypass cache */
#define CKDGATR_SSOP_INHIBIT 0x08 /* ...inhibit cache loading */
#define CKDGATR_SSOP_SEQACC 0x0C /* ...sequential access */
#define CKDGATR_SSOP_LOGGING 0x10 /* ...logging mode */
#define CKDGATR_USE_CACHE_FW 0x02 /* Use cache fast write */
#define CKDGATR_INH_DASD_FW 0x01 /* Inhibit DASD fast write */
/*-------------------------------------------------------------------*/
/* Bit definitions for Locate operation byte */
/*-------------------------------------------------------------------*/
#define CKDOPER_ORIENTATION 0xC0 /* Orientation bits... */
#define CKDOPER_ORIENT_COUNT 0x00 /* ...orient to count */
#define CKDOPER_ORIENT_HOME 0x40 /* ...orient to home address */
#define CKDOPER_ORIENT_DATA 0x80 /* ...orient to data area */
#define CKDOPER_ORIENT_INDEX 0xC0 /* ...orient to index */
#define CKDOPER_CODE 0x3F /* Operation code bits... */
#define CKDOPER_ORIENT 0x00 /* ...orient */
#define CKDOPER_WRITE 0x01 /* ...write data */
#define CKDOPER_FORMAT 0x03 /* ...format write */
#define CKDOPER_RDDATA 0x06 /* ...read data */
#define CKDOPER_WRTANY 0x09 /* ...write any */
#define CKDOPER_RDANY 0x0A /* ...read any */
#define CKDOPER_WRTTRK 0x0B /* ...write track */
#define CKDOPER_RDTRKS 0x0C /* ...read tracks */
#define CKDOPER_RDTSET 0x0E /* ...read track set */
#define CKDOPER_READ 0x16 /* ...read */
#define CKDOPER_EXTOP 0x3F /* ...extended operation */
/*-------------------------------------------------------------------*/
/* Bit definitions for Locate auxiliary byte */
/*-------------------------------------------------------------------*/
#define CKDLAUX_TLFVALID 0x80 /* TLF field is valid */
#define CKDLAUX_RESV 0x7E /* Reserved bits - must be 0 */
#define CKDLAUX_RDCNTSUF 0x01 /* Suffixed read count CCW */
/*-------------------------------------------------------------------*/
/* Definitions for ckdorient field in device block */
/*-------------------------------------------------------------------*/
#define CKDORIENT_NONE 0 /* Orientation unknown */
#define CKDORIENT_INDEX 1 /* Oriented after track hdr */
#define CKDORIENT_COUNT 2 /* Oriented after count field*/
#define CKDORIENT_KEY 3 /* Oriented after key field */
#define CKDORIENT_DATA 4 /* Oriented after data field */
#define CKDORIENT_EOT 5 /* Oriented after end of trk */
/* Path state byte for Sense Path Group ID command */
#define SPG_PATHSTAT 0xC0 /* Pathing status bits... */
#define SPG_PATHSTAT_RESET 0x00 /* ...reset */
#define SPG_PATHSTAT_RESV 0x40 /* ...reserved bit setting */
#define SPG_PATHSTAT_UNGROUPED 0x80 /* ...ungrouped */
#define SPG_PATHSTAT_GROUPED 0xC0 /* ...grouped */
#define SPG_PATHSTAT_RESERVED 0x30 /* ...to channel + pg members*/
#define SPG_PARTSTAT 0x30 /* Partitioning status bits..*/
#define SPG_PARTSTAT_IENABLED 0x00 /* ...implicitly enabled */
#define SPG_PARTSTAT_RESV 0x10 /* ...reserved bit setting */
#define SPG_PARTSTAT_DISABLED 0x20 /* ...disabled */
#define SPG_PARTSTAT_XENABLED 0x30 /* ...explicitly enabled */
#define SPG_PATHMODE 0x08 /* Path mode bit... */
#define SPG_PATHMODE_SINGLE 0x00 /* ...single path mode */
#define SPG_PATHMODE_RESV 0x08 /* ...reserved bit setting */
#define SPG_RESERVED 0x07 /* Reserved bits, must be 0 */
/* Function control byte for Set Path Group ID command */
#define SPG_SET_MULTIPATH 0x80 /* Set multipath mode */
#define SPG_SET_COMMAND 0x60 /* Set path command bits... */
#define SPG_SET_ESTABLISH 0x00 /* ...establish group */
#define SPG_SET_DISBAND 0x20 /* ...disband group */
#define SPG_SET_RESIGN 0x40 /* ...resign from group */
#define SPG_SET_COMMAND_RESV 0x60 /* ...reserved bit setting */
#define SPG_SET_RESV 0x1F /* Reserved bits, must be 0 */
/*-------------------------------------------------------------------*/
/* Bit definitions for Diagnostic Control subcommand byte */
/*-------------------------------------------------------------------*/
#define DIAGCTL_INHIBIT_WRITE 0x02 /* Inhibit Write */
#define DIAGCTL_SET_GUAR_PATH 0x04 /* Set Guaranteed Path */
#define DIAGCTL_ENABLE_WRITE 0x08 /* Enable Write */
#define DIAGCTL_3380_TC_MODE 0x09 /* 3380 Track Compat Mode */
#define DIAGCTL_INIT_SUBSYS 0x0B /* Diagnostic Init Subsys */
#define DIAGCTL_UNFENCE 0x0C /* Unfence */
#define DIAGCTL_ACCDEV_UNKCOND 0x0F /* Access Device Unknown Cond*/
#define DIAGCTL_MAINT_RESERVE 0x10 /* Media Maintenance Reserve */
#define DIAGCTL_MAINT_RELEASE 0x11 /* Media Maintenance Release */
#define DIAGCTL_MAINT_QUERY 0x12 /* Media Maintenance Query */
/*-------------------------------------------------------------------*/
/* Definitions for Read Configuration Data command */
/*-------------------------------------------------------------------*/
#define CONFIG_DATA_SIZE 256 /* Number of bytes returned
by Read Config Data CCW */
/*
* ISW20060207
* EXTENT_CHECK0 is just to shut up a stupid gcc 4 warning..
* It doesn't hurt otherwise
* EXTENT_CHECK0(dev) is the same as EXTENT_CHECK(dev,0,0)
*/
#define EXTENT_CHECK0(_dev) ((_dev)->ckdxbcyl > 0 \
|| ((_dev)->ckdxbcyl==0 && (_dev)->ckdxbhead>0))
#define EXTENT_CHECK(_dev, _cyl, _head) \
( (_cyl) < (_dev)->ckdxbcyl || (_cyl) > (_dev)->ckdxecyl \
|| ((_cyl) == (_dev)->ckdxbcyl && (_head) < (_dev)->ckdxbhead) \
|| ((_cyl) == (_dev)->ckdxecyl && (_head) > (_dev)->ckdxehead) )
/*-------------------------------------------------------------------*/
/* Static data areas */
/*-------------------------------------------------------------------*/
static const BYTE eighthex00[] = {0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00};
/*-------------------------------------------------------------------*/
/* Initialize the device handler */
/*-------------------------------------------------------------------*/
int ckd_dasd_init_handler ( DEVBLK *dev, int argc, char *argv[] )
{
int rc; /* Return code */
struct stat statbuf; /* File information */
CKD_DEVHDR devhdr; /* Device header */
CCKD_DEVHDR cdevhdr; /* Compressed device header */
int i; /* Loop index */
int fileseq; /* File sequence number */
char *sfxptr; /* -> Last char of file name */
char sfxchar; /* Last char of file name */
int heads; /* #of heads in CKD file */
int trksize; /* Track size of CKD file */
int trks; /* #of tracks in CKD file */
int cyls; /* #of cylinders in CKD file */
int highcyl; /* Highest cyl# in CKD file */
char *cu = NULL; /* Specified control unit */
int cckd=0; /* 1 if compressed CKD */
char filename[FILENAME_MAX+3]; /* work area for display */
BYTE serial[12+1] = {0}; /* Dasd serial number */
dev->rcd = &dasd_build_ckd_config_data;
/* For re-initialisation, close the existing file, if any */
if (dev->fd >= 0)
(dev->hnd->close)(dev);
if(!sscanf(dev->typname,"%hx",&(dev->devtype)))
dev->devtype = 0x3380;
/* The first argument is the file name */
if (argc == 0 || strlen(argv[0]) >= sizeof(dev->filename))
{
// "%1d:%04X CKD file: name missing or invalid filename length"
WRMSG( HHC00400, "E", LCSS_DEVNUM );
return -1;
}
/* reset excps count */
dev->excps = 0;
/* Save the file name in the device block */
hostpath(dev->filename, argv[0], sizeof(dev->filename));
if (strchr(dev->filename, SPACE) == NULL)
{
MSGBUF(filename, "%s", dev->filename);
}
else
{
MSGBUF(filename, "'%s'", dev->filename);
}
#if defined( OPTION_SHARED_DEVICES )
/* Device is shareable */
dev->shareable = 1;
#endif // defined( OPTION_SHARED_DEVICES )
/* Check for possible remote device */
if (stat(dev->filename, &statbuf) < 0)
{
rc = shared_ckd_init ( dev, argc, argv);
if (rc < 0)
{
// "%1d:%04X CKD file %s: open error: not found"
WRMSG( HHC00401, "E", LCSS_DEVNUM, filename );
return -1;
}
else
return rc;
}
/* No active track or cache entry */
dev->bufcur = dev->cache = -1;
/* Locate and save the last character of the file name */
sfxptr = strrchr (dev->filename, PATHSEPC);
if (sfxptr == NULL) sfxptr = dev->filename + 1;
sfxptr = strchr (sfxptr, '.');
if (sfxptr == NULL) sfxptr = dev->filename + strlen(dev->filename);
sfxptr--;
sfxchar = *sfxptr;
/* process the remaining arguments */
for (i = 1; i < argc; i++)
{
if (strcasecmp ("lazywrite", argv[i]) == 0)
{
dev->ckdnolazywr = 0;
continue;
}
if (strcasecmp ("nolazywrite", argv[i]) == 0)
{
dev->ckdnolazywr = 1;
continue;
}
if (strcasecmp ("fulltrackio", argv[i]) == 0 ||
strcasecmp ("fulltrkio", argv[i]) == 0 ||
strcasecmp ("ftio", argv[i]) == 0)
{
dev->ckdnolazywr = 0;
continue;
}
if (strcasecmp ("nofulltrackio", argv[i]) == 0 ||
strcasecmp ("nofulltrkio", argv[i]) == 0 ||
strcasecmp ("noftio", argv[i]) == 0)
{
dev->ckdnolazywr = 1;
continue;
}
if (strcasecmp ("readonly", argv[i]) == 0 ||
strcasecmp ("rdonly", argv[i]) == 0 ||
strcasecmp ("ro", argv[i]) == 0)
{
dev->ckdrdonly = 1;
continue;
}
if (strcasecmp ("fakewrite", argv[i]) == 0 ||
strcasecmp ("fakewrt", argv[i]) == 0 ||
strcasecmp ("fw", argv[i]) == 0)
{
dev->ckdfakewr = 1;
continue;
}
if (strlen (argv[i]) > 3 &&
memcmp ("sf=", argv[i], 3) == 0)
{
/* Parse the shadow file name parameter */
cckd_sf_parse_sfn( dev, argv[i]+3 );
continue;
}
if (strlen (argv[i]) > 3
&& memcmp("cu=", argv[i], 3) == 0)
{
cu = argv[i]+3;
continue;
}
if (strlen (argv[i]) > 4
&& memcmp("ser=", argv[i], 4) == 0)
{
if (1
&& is_numeric( argv[i]+4 )
&& strlen( argv[i]+4 ) == sizeof( dev->serial )
)
{
memcpy( serial, argv[i]+4, sizeof( dev->serial ));
continue;
}
}
// "%1d:%04X CKD file: parameter %s in argument %d is invalid"
WRMSG( HHC00402, "E", LCSS_DEVNUM, argv[i], i + 1 );
return -1;
}
/* Initialize the total tracks and cylinders */
dev->ckdtrks = 0;
dev->ckdcyls = 0;
/* Open all of the CKD image files which comprise this volume */
for (fileseq = 1;;)
{
/* Open the CKD image file */
dev->fd = HOPEN (dev->filename, dev->ckdrdonly ?
O_RDONLY|O_BINARY : O_RDWR|O_BINARY);
if (dev->fd < 0)
{ /* Try read-only if shadow file present */
if (!dev->ckdrdonly && dev->dasdsfn != NULL)
dev->fd = HOPEN (dev->filename, O_RDONLY|O_BINARY);
if (dev->fd < 0)
{
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
filename, "open()", strerror( errno ));
return -1;
}
}
/* If shadow file, only one base file is allowed */
if (fileseq > 1 && dev->dasdsfn != NULL)
{
// "%1d:%04X CKD file %s: only one base file is allowed"
WRMSG( HHC00405, "E", LCSS_DEVNUM, filename );
return -1;
}
/* Determine the device size */
rc = fstat (dev->fd, &statbuf);
if (rc < 0)
{
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM, filename, "fstat()", strerror( errno ));
return -1;
}
/* Read the device header */
rc = read (dev->fd, &devhdr, CKD_DEVHDR_SIZE);
if (rc < (int)CKD_DEVHDR_SIZE)
{
if (rc < 0)
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
filename, "read()", strerror( errno ));
else
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
filename, "read()", "CKD header incomplete" );
return -1;
}
/* Save the serial number */
if (serial[0]) // (override?)
memcpy( dev->serial, serial, sizeof( dev->serial ));
else
memcpy( dev->serial, devhdr.dh_serial, sizeof( dev->serial ));
{
static const BYTE nulls[12] = {0};
if (memcmp( dev->serial, nulls, 12 ) == 0)
gen_dasd_serial( dev->serial );
}
/* Check the device header identifier */
dev->cckd64 = 0;
if (!is_dh_devid_typ( devhdr.dh_devid, CKD32_CMP_OR_NML_TYP ))
{
if (is_dh_devid_typ( devhdr.dh_devid, CKD64_CMP_OR_NML_TYP ))
{
dev->cckd64 = 1;
close( dev->fd );
dev->fd = -1;
return ckd64_dasd_init_handler( dev, argc, argv );
}
// "%1d:%04X CKD file %s: ckd header invalid"
WRMSG( HHC00406, "E", LCSS_DEVNUM, filename );
return -1;
}
if (is_dh_devid_typ( devhdr.dh_devid, CKD_C370_TYP ))
{
cckd = 1;
if (fileseq != 1)
{
// "%1d:%04X CKD file %s: only 1 CCKD file allowed"
WRMSG( HHC00407, "E", LCSS_DEVNUM, filename );
return -1;
}
}
if (dev->ckdrdonly)
if (!dev->quiet)
// "%1d:%04X CKD file %s: opened r/o%s"
WRMSG( HHC00403, "I", LCSS_DEVNUM,
filename, dev->ckdfakewr ? " with fake writing" : "" );
/* Read the compressed device header */
if (cckd)
{
rc = read (dev->fd, &cdevhdr, CCKD_DEVHDR_SIZE);
if (rc < (int) CCKD_DEVHDR_SIZE)
{
if (rc < 0)
{
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
filename, "read()", strerror( errno ));
}
else
{
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
filename, "read()", "CCKD header incomplete" );
}
return -1;
}
}
/* Extract fields from device header */
FETCH_LE_FW( heads, devhdr.dh_heads );
FETCH_LE_FW( trksize, devhdr.dh_trksize );
FETCH_LE_HW( highcyl, devhdr.dh_highcyl );
if (cckd == 0)
{
if (dev->dasdcopy == 0)
{
trks = (int)((statbuf.st_size - CKD_DEVHDR_SIZE) / trksize);
cyls = trks / heads;
if (fileseq == 1 && highcyl == cyls)
{
devhdr.dh_fileseq = 0;
highcyl = 0;
}
}
else
{
/*
* For dasdcopy we get the number of cylinders and tracks from
* the highcyl in the device header. The last file will have
* a sequence number of 0xFF.
*/
cyls = highcyl - dev->ckdcyls + 1;
trks = cyls * heads;
if (devhdr.dh_fileseq == 0xFF)
{
devhdr.dh_fileseq = (fileseq == 1 ? 0 : fileseq);
highcyl = 0;
store_hw( devhdr.dh_highcyl, 0 );
lseek (dev->fd, 0, SEEK_SET);
rc = write (dev->fd, &devhdr, CKD_DEVHDR_SIZE);
}
}
}
else
{
FETCH_LE_FW( cyls, cdevhdr.cdh_cyls );
trks = cyls * heads;
}
/* Check for correct file sequence number */
if (devhdr.dh_fileseq != fileseq
&& !(devhdr.dh_fileseq == 0 && fileseq == 1))
{
// "%1d:%04X CKD file %s: ckd file out of sequence or bad size"
WRMSG( HHC00408, "E", LCSS_DEVNUM, filename );
return -1;
}
if (devhdr.dh_fileseq > 0)
{
if (!dev->quiet)
// "%1d:%04X CKD file %s: seq %02d cyls %6d-%-6d"
WRMSG( HHC00409, "I", LCSS_DEVNUM,
filename, devhdr.dh_fileseq, dev->ckdcyls,
(highcyl > 0 ? highcyl : dev->ckdcyls + cyls - 1));
}
/* Save device geometry of first file, or check that device
geometry of subsequent files matches that of first file */
if (fileseq == 1)
{
dev->ckdheads = heads;
dev->ckdtrksz = trksize;
}
else if (heads != dev->ckdheads || trksize != dev->ckdtrksz)
{
// "%1d:%04X CKD file %s: found heads %d trklen %d, expected heads %d trklen %d"
WRMSG( HHC00410, "E", LCSS_DEVNUM,
filename, heads, trksize, dev->ckdheads, dev->ckdtrksz );
return -1;
}
/* Consistency check device header */
if (cckd == 0 && dev->dasdcopy == 0 && (cyls * heads != trks
|| ((off_t)trks * trksize) + CKD_DEVHDR_SIZE
!= statbuf.st_size
|| (highcyl != 0 && highcyl != dev->ckdcyls + cyls - 1)))
{
// "%1d:%04X CKD file %s: ckd header inconsistent with file size"
WRMSG( HHC00411, "E", LCSS_DEVNUM, filename );
return -1;
}
/* Check for correct high cylinder number */
if (highcyl != 0 && highcyl != dev->ckdcyls + cyls - 1)
{
// "%1d:%04X CKD file %s: ckd header high cylinder incorrect"
WRMSG( HHC00412, "E", LCSS_DEVNUM, filename );
return -1;
}
/* Accumulate total volume size */
dev->ckdtrks += trks;
dev->ckdcyls += cyls;
/* Save file descriptor and high track number */
dev->ckdfd[fileseq-1] = dev->fd;
dev->ckdhitrk[fileseq-1] = dev->ckdtrks;
dev->ckdnumfd = fileseq;
/* Exit loop if this is the last file */
if (highcyl == 0) break;
/* Increment the file sequence number */
fileseq++;
/* Alter the file name suffix ready for the next file */
if ( fileseq <= 9 )
*sfxptr = '0' + fileseq;
else
*sfxptr = 'A' - 10 + fileseq;
/* Check that maximum files has not been exceeded */
if (fileseq > CKD_MAXFILES)
{
// "%1d:%04X CKD file %s: maximum CKD files exceeded: %d"
WRMSG( HHC00413, "E", LCSS_DEVNUM, filename, CKD_MAXFILES );
return -1;
}
} /* end for(fileseq) */
/* Restore the last character of the file name */
*sfxptr = sfxchar;
/* Locate the CKD dasd table entry */
dev->ckdtab = dasd_lookup (DASD_CKDDEV, NULL, dev->devtype, dev->ckdcyls);
if (dev->ckdtab == NULL)
{
// "%1d:%04X CKD file %s: device type %4.4X not found in dasd table"
WRMSG( HHC00415, "E", LCSS_DEVNUM,
filename, dev->devtype );
return -1;
}
/* Log the device geometry */
if (!dev->quiet)
// "%1d:%04X CKD file %s: model %s cyls %d heads %d tracks %d trklen %d"
WRMSG( HHC00414, "I", LCSS_DEVNUM, filename, dev->ckdtab->name,
dev->ckdcyls, dev->ckdheads, dev->ckdtrks, dev->ckdtrksz );
/* Locate the CKD control unit dasd table entry */
dev->ckdcu = dasd_lookup (DASD_CKDCU, cu ? cu : dev->ckdtab->cu, 0, 0);
if (dev->ckdcu == NULL)
{
// "%1d:%04X CKD file %s: control unit %s not found in dasd table"
WRMSG( HHC00416, "E", LCSS_DEVNUM,
filename, cu ? cu : dev->ckdtab->cu );
return -1;
}
/* Set number of sense bytes according to controller specification */
dev->numsense = dev->ckdcu->senselength;
/* Set flag bit if 3990 controller */
if (dev->ckdcu->devt == 0x3990)
dev->ckd3990 = 1;
/* Build the dh_devid area */
dev->numdevid = dasd_build_ckd_devid (dev->ckdtab, dev->ckdcu,
(BYTE *)&dev->devid);
/* Build the devchar area */
dev->numdevchar = dasd_build_ckd_devchar (dev->ckdtab, dev->ckdcu,
(BYTE *)&dev->devchar, dev->ckdcyls);
/* Clear the DPA */
memset(dev->pgid, 0, sizeof(dev->pgid));
/* Activate I/O tracing */
// dev->ccwtrace = 1;
/* Request the channel to merge data chained write CCWs into
a single buffer before passing data to the device handler */
dev->cdwmerge = 1;
/* default for device cache is on */
dev->devcache = TRUE;
if (!cckd) return 0;
else return cckd_dasd_init_handler(dev, argc, argv);
} /* end function ckd_dasd_init_handler */
/*-------------------------------------------------------------------*/
/* Query the device definition */
/*-------------------------------------------------------------------*/
void ckd_dasd_query_device (DEVBLK *dev, char **devclass,
int buflen, char *buffer)
{
char filename[ PATH_MAX + 1 ];/* full path or just name */
CCKD_EXT* cckd; /* CCKD Extension Block */
BEGIN_DEVICE_CLASS_QUERY( "DASD", dev, devclass, buflen, buffer );
cckd = dev->cckd_ext;
if (!cckd)
{
if ( dev->ckdnumfd > 1)
{
snprintf( buffer, buflen, "%s%s [%d cyls] [%d segs] IO[%"PRIu64"]",
dev->cckd64 ? "*64* " : "",
filename,
dev->ckdcyls,
dev->ckdnumfd,
dev->excps );
}
else
{
snprintf( buffer, buflen, "%s%s [%d cyls] IO[%"PRIu64"]",
dev->cckd64 ? "*64* " : "",
filename,
dev->ckdcyls,
dev->excps );
}
}
else
{
snprintf( buffer, buflen, "%s%s [%d cyls] [%d sfs] IO[%"PRIu64"]",
dev->cckd64 ? "*64* " : "",
filename,
dev->ckdcyls,
cckd->sfn,
dev->excps );
}
} /* end function ckd_dasd_query_device */
/*-------------------------------------------------------------------*/
/* Release cache entries */
/*-------------------------------------------------------------------*/
int ckddasd_purge_cache (int *answer, int ix, int i, void *data)
{
U16 devnum; /* Cached device number */
int trk; /* Cached track */
DEVBLK *dev = data; /* -> device block */
UNREFERENCED(answer);
UNREFERENCED( trk ); // (silence "set but not used" warning)
CKD_CACHE_GETKEY(i, devnum, trk);
if (dev->devnum == devnum)
cache_release (ix, i, CACHE_FREEBUF);
return 0;
}
static int ckd_dasd_read_track (DEVBLK *dev, int trk, BYTE *unitstat);
/*-------------------------------------------------------------------*/
/* Close the device */
/*-------------------------------------------------------------------*/
int ckd_dasd_close_device ( DEVBLK *dev )
{
int i; /* Index */
BYTE unitstat; /* Unit Status */
/* Write the last track image if it's modified */
(dev->hnd->read) (dev, -1, &unitstat);
/* Free the cache */
cache_lock(CACHE_DEVBUF);
cache_scan(CACHE_DEVBUF, ckddasd_purge_cache, dev);
cache_unlock(CACHE_DEVBUF);
if (!dev->batch)
if (!dev->quiet)
// "%1d:%04X CKD file %s: cache hits %d, misses %d, waits %d"
WRMSG( HHC00417, "I", LCSS_DEVNUM,
dev->filename, dev->cachehits, dev->cachemisses, dev->cachewaits );
/* Close all of the CKD image files */
for (i = 0; i < dev->ckdnumfd; i++)
if (dev->ckdfd[i] > 2)
close (dev->ckdfd[i]);
dev->buf = NULL;
dev->bufsize = 0;
return 0;
} /* end function ckd_dasd_close_device */
/*-------------------------------------------------------------------*/
/* Read a track image at CCHH */
/*-------------------------------------------------------------------*/
static
int ckd_read_cchh (DEVBLK *dev, int cyl, int head, BYTE *unitstat)
{
int rc; /* Return code */
int trk; /* Track number */
/* Command reject if seek position is outside volume */
if (cyl >= dev->ckdcyls || head >= dev->ckdheads)
{
ckd_build_sense (dev, SENSE_CR, 0, 0,
FORMAT_0, MESSAGE_4);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
return -1;
}
/* Calculate the track number */
trk = cyl * dev->ckdheads + head;
/* Call the read exit */
rc = (dev->hnd->read) (dev, trk, unitstat);
return rc;
} /* end function ckd_read_cchh */
/*-------------------------------------------------------------------*/
/* Return adjusted track image length */
/*-------------------------------------------------------------------*/
int ckd_trklen (DEVBLK *dev, BYTE *buf)
{
int sz = ckd_tracklen( dev, buf );
if (sz > dev->ckdtrksz)
sz = dev->ckdtrksz;
return sz;
}
/*-------------------------------------------------------------------*/
/* Read a track image */
/*-------------------------------------------------------------------*/
static
int ckd_dasd_read_track (DEVBLK *dev, int trk, BYTE *unitstat)
{
int rc; /* Return code */
int cyl = 0; /* Cylinder */
int head = 0; /* Head */
U64 offset; /* File offsets */
int i,o,f; /* Indexes */
CKD_TRKHDR *trkhdr; /* -> New track header */
// "%1d:%04X CKD file %s: read trk %d cur trk %d"
LOGDEVTR( HHC00424, "I", dev->filename, trk, dev->bufcur );
/* Calculate cylinder and head */
if (dev->ckdheads) /* (might be 0 if init failed!) */
{
cyl = trk / dev->ckdheads;
head = trk % dev->ckdheads;
}
/* Reset buffer offsets */
dev->bufoff = 0;
dev->bufoffhi = dev->ckdtrksz;
/* Return if reading the same track image */
if (trk >= 0 && trk == dev->bufcur)
return 0;
/* Write the previous track image if modified */
if (dev->bufupd)
{
// "%1d:%04X CKD file %s: read track updating track %d"
LOGDEVTR( HHC00425, "I", dev->filename, dev->bufcur );
dev->bufupd = 0;
/* Seek to the old track image offset */
offset = (dev->ckdtrkoff + dev->bufupdlo);
offset = lseek (dev->fd, offset, SEEK_SET);
if ((S64)offset < 0)
{
/* Handle seek error condition */
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
dev->filename, "lseek()", strerror( errno ));
ckd_build_sense (dev, SENSE_EC, 0, 0,
FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
cache_lock(CACHE_DEVBUF);
cache_setflag(CACHE_DEVBUF, dev->cache, ~CKD_CACHE_ACTIVE, 0);
cache_unlock(CACHE_DEVBUF);
dev->bufupdlo = dev->bufupdhi = 0;
dev->bufcur = dev->cache = -1;
return -1;
}
/* Write the portion of the track image that was modified */
rc = write (dev->fd, &dev->buf[dev->bufupdlo],
dev->bufupdhi - dev->bufupdlo);
if (rc < dev->bufupdhi - dev->bufupdlo)
{
/* Handle write error condition */
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
dev->filename, "write()", strerror( errno ));
ckd_build_sense (dev, SENSE_EC, 0, 0,
FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
cache_lock(CACHE_DEVBUF);
cache_setflag(CACHE_DEVBUF, dev->cache, ~CKD_CACHE_ACTIVE, 0);
cache_unlock(CACHE_DEVBUF);
dev->bufupdlo = dev->bufupdhi = 0;
dev->bufcur = dev->cache = -1;
return -1;
}
dev->bufupdlo = dev->bufupdhi = 0;
}
cache_lock (CACHE_DEVBUF);
/* Make the previous cache entry inactive */
if (dev->cache >= 0)
cache_setflag(CACHE_DEVBUF, dev->cache, ~CKD_CACHE_ACTIVE, 0);
dev->bufcur = dev->cache = -1;
/* Return on special case when called by the close handler */
if (trk < 0)
{
cache_unlock (CACHE_DEVBUF);
return 0;
}
ckd_read_track_retry:
/* Search the cache */
i = cache_lookup (CACHE_DEVBUF, CKD_CACHE_SETKEY(dev->devnum, trk), &o);
/* Cache hit */
if (i >= 0)
{
cache_setflag(CACHE_DEVBUF, i, ~0, CKD_CACHE_ACTIVE);
cache_setage(CACHE_DEVBUF, i);
cache_unlock(CACHE_DEVBUF);
// "%1d:%04X CKD file %s: read trk %d cache hit, using cache[%d]"
LOGDEVTR( HHC00426, "I", dev->filename, trk, i );
dev->cachehits++;
dev->cache = i;
dev->buf = cache_getbuf(CACHE_DEVBUF, dev->cache, 0);
dev->bufcur = trk;
dev->bufoff = 0;
dev->bufoffhi = dev->ckdtrksz;
dev->buflen = ckd_trklen (dev, dev->buf);
dev->bufsize = cache_getlen(CACHE_DEVBUF, dev->cache);
/* Set the file descriptor */
for (f = 0; f < dev->ckdnumfd; f++)
if (trk < dev->ckdhitrk[f]) break;
dev->fd = dev->ckdfd[f];
/* Calculate the track offset */
dev->ckdtrkoff = (U64)(CKD_DEVHDR_SIZE +
((U64)(trk - (f ? dev->ckdhitrk[f-1] : 0))) * dev->ckdtrksz);
return 0;
}
/* Wait if no available cache entry */
if (o < 0)
{
// "%1d:%04X CKD file %s: read trk %d no available cache entry, waiting"
LOGDEVTR( HHC00427, "I", dev->filename, trk );
dev->cachewaits++;
cache_wait(CACHE_DEVBUF);
goto ckd_read_track_retry;
}
/* Cache miss */
// "%1d:%04X CKD file %s: read trk %d cache miss, using cache[%d]"
LOGDEVTR( HHC00428, "I", dev->filename, trk, o );
dev->cachemisses++;
/* Make this cache entry active */
cache_setkey (CACHE_DEVBUF, o, CKD_CACHE_SETKEY(dev->devnum, trk));
cache_setflag(CACHE_DEVBUF, o, 0, CKD_CACHE_ACTIVE|DEVBUF_TYPE_CKD);
cache_setage (CACHE_DEVBUF, o);
dev->buf = cache_getbuf(CACHE_DEVBUF, o, dev->ckdtrksz);
cache_unlock (CACHE_DEVBUF);
/* Set the file descriptor */
for (f = 0; f < dev->ckdnumfd; f++)
if (trk < dev->ckdhitrk[f]) break;
dev->fd = dev->ckdfd[f];
/* Calculate the track offset */
dev->ckdtrkoff = (U64)(CKD_DEVHDR_SIZE +
((U64)(trk - (f ? dev->ckdhitrk[f-1] : 0))) * dev->ckdtrksz);
// "%1d:%04X CKD file %s: read trk %d reading file %d offset %"PRId64" len %d"
LOGDEVTR( HHC00429, "I", dev->filename, trk, f+1, dev->ckdtrkoff, dev->ckdtrksz );
/* Seek to the track image offset */
offset = dev->ckdtrkoff;
offset = lseek (dev->fd, offset, SEEK_SET);
if ((S64)offset < 0)
{
/* Handle seek error condition */
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
dev->filename, "lseek()", strerror( errno ));
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
dev->bufcur = dev->cache = -1;
cache_lock(CACHE_DEVBUF);
cache_release(CACHE_DEVBUF, o, 0);
cache_unlock(CACHE_DEVBUF);
return -1;
}
/* Read the track image */
if (dev->dasdcopy == 0)
{
rc = read (dev->fd, dev->buf, dev->ckdtrksz);
if (rc < dev->ckdtrksz)
{
/* Handle read error condition */
// "%1d:%04X CKD file %s: error in function %s: %s"
WRMSG( HHC00404, "E", LCSS_DEVNUM,
dev->filename, "read()", (rc < 0 ? strerror( errno ) : "unexpected end of file" ));
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
*unitstat = CSW_CE | CSW_DE | CSW_UC;
dev->bufcur = dev->cache = -1;
cache_lock(CACHE_DEVBUF);
cache_release(CACHE_DEVBUF, o, 0);
cache_unlock(CACHE_DEVBUF);
return -1;
}
}
else
{
trkhdr = (CKD_TRKHDR*)dev->buf;
trkhdr->bin = 0;
store_hw( trkhdr->cyl, cyl );
store_hw( trkhdr->head, head );