|
|
1.1 root 1: /*
2: ** format disk on VDDC / SMD_E controller - (fsd/smd/xfd/xsd) type
3: **
4: ** Author: John R. Franks
5: **
6: ** This program is used to maintain drives attached to a VDDC controller.
7: ** The basic functions are 1) format media, 2) verify media, 3) relocate
8: ** bad blocks that have appeared on the media, and 4) Format and initialize the
9: ** maintenance cylinders on the drive.
10: **
11: ** For in depth information on the structure of this program please refer
12: ** to the comments above the individual subroutines in addition to the design
13: ** notes in vdutil.doc.
14: */
15:
16: #include <setjmp.h>
17: #include "../machine/mtpr.h"
18: #include "../h/param.h"
19: #include "../h/inode.h"
20: #include "../h/fs.h"
21: #include "../vba/vddc.h"
22: #include "../stand/saio.h"
23:
24: #define MAXCONTROLLER 4
25: #define MAXDRIVE 16
26:
27: #define NUMMNT 2
28: #define NUMREL 3
29: #define NUMSYS (NUMREL+NUMMNT)
30:
31: #define TRUE 1
32: #define FALSE 0
33:
34: #define MAXTRKS 24
35: #define MAXSECS_PER_TRK 48
36: #define MAXERR 1000
37: #define SECSIZ 512
38: #define TRKSIZ ((SECSIZ/sizeof(long)) * MAXSECS_PER_TRK)
39:
40: #define HARD_ERROR (DRVNRDY | INVDADR | DNEMEM | PARERR | OPABRT | \
41: WPTERR | DSEEKERR | NOTCYLERR)
42: #define DATA_ERROR (CTLRERR | UCDATERR | DCOMPERR | DSERLY | DSLATE | \
43: TOPLUS | TOMNUS | CPDCRT | \
44: HRDERR | SFTERR)
45: #define HEADER_ERROR (HCRCERR | HCMPERR)
46: #define NRM (short)0
47: #define BAD (short)VDUF
48: #define WPT (short)(NRM | VDWPT)
49: #define RELOC_SECTOR (short)(VDALT)
50: #define ALT_SECTOR (short)(VDALT)
51:
52: static jmp_buf environ;
53:
54: /* Free bad block allocation bit map */
55: typedef struct {
56: long error;
57: enum {ALLOCATED, NOTALLOCATED} freestatus;
58: } fmt_free;
59:
60:
61:
62: typedef enum {SINGLE_SECTOR, FULL_TRACK} rel_type;
63:
64: typedef struct {
65: dskadr err_adr;
66: long err_stat;
67: } fmt_err;
68:
69: static fmt_free free[NUMREL*MAXTRKS][MAXSECS_PER_TRK];
70: static fmt_err dsk_err[MAXERR];/* Disk error table */
71: static fmt_mdcb mdcb; /* Master device control block */
72: static fmt_dcb dcb; /* Device control blocks */
73: static int bad_secs = 0; /* Sequence # of last error detected */
74: static cdr *controller_address; /* controller physical address */
75: static int ctlr_num = 0; /* ctlr number */
76: static int unit_number = 0; /* unit number */
77: static int drive_type; /* disk type index */
78: static char *drive_name;
79: static int verify_count = 16;
80: static char *stars = "***************";
81: static char *sure_question = "This is DESTRUCTIVE! Are you sure";
82: static char *operation_string = 0;
83: static char *format_string = "Formatting";
84: static char *scan_string = "Scanning";
85: static char *relocate_string = "Relocation";
86: static char drives_to_do[MAXCONTROLLER][MAXDRIVE];
87: static char ctlr_type[MAXCONTROLLER] =
88: {UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN};
89: static char *controller_name[MAXCONTROLLER] =
90: {"Unknown","Unknown","Unknown","Unknown"};
91:
92: static long pattern_0[TRKSIZ], pattern_1[TRKSIZ];
93: static long pattern_2[TRKSIZ], pattern_3[TRKSIZ];
94: static long pattern_4[TRKSIZ], pattern_5[TRKSIZ];
95: static long pattern_6[TRKSIZ], pattern_7[TRKSIZ];
96: static long pattern_8[TRKSIZ], pattern_9[TRKSIZ];
97: static long pattern_10[TRKSIZ], pattern_11[TRKSIZ];
98: static long pattern_12[TRKSIZ], pattern_13[TRKSIZ];
99: static long pattern_14[TRKSIZ], pattern_15[TRKSIZ];
100:
101: static long *pattern_address[] = {
102: pattern_0, pattern_1, pattern_2, pattern_3,
103: pattern_4, pattern_5, pattern_6, pattern_7,
104: pattern_8, pattern_9, pattern_10, pattern_11,
105: pattern_12, pattern_13, pattern_14, pattern_15
106: };
107:
108: /* Double buffer for scanning existing file systems and general scratch */
109: static long scratch[TRKSIZ];
110: static long save[TRKSIZ];
111:
112: static long max_cyl;
113: static long max_trk;
114: static long max_sec;
115: static long sector_size;
116: static long num_slip;
117: static int max_drive;
118:
119: /*
120: ** Main, of course, is the entry point into vdfmt. It is responsible
121: ** for calling the routine for the get the disk parameters from the user,
122: ** creating and initializing the bad block patterns for scanning the disk, and
123: ** determining whether or not the user wants to format or relocate bad
124: ** blocks before calling the appropriate routines.
125: **
126: ** Formatting implies cerification of media and creation of the maintenance
127: ** cylinders and relocation of bad blocks.
128: */
129:
130: main()
131: {
132: register int index;
133: register int (*operation)();
134: register int controller, drive;
135: cdr *controller_address;
136: int (*get_operation_type())();
137:
138: printf("VDFORMAT Version 2.\n\n");
139: /* Identify which controllers are present and what type they are. */
140: for(controller = 0; controller < MAXCONTROLLER; controller++) {
141: controller_address = (cdr *)(vddcaddr[controller]|IOBASE);
142: if(!badaddr(controller_address, 2)) {
143: controller_address->cdr_reset = 0xffffffff;
144: DELAY(1000000);
145: if(controller_address->cdr_reset != 0xffffffff) {
146: ctlr_type[controller] = SMDCTLR;
147: controller_name[controller] = "SMD";
148: DELAY(1000000);
149: }
150: else {
151: ctlr_type[controller] = SMD_ECTLR;
152: controller_name[controller] = "SMD/E";
153: controller_address->cdr_reserved = 0x0;
154: DELAY(3000000);
155: }
156: printf("Controller #%d is an %s disk controller.\n",
157: controller, controller_name[controller]);
158: }
159: else
160: ctlr_type[controller] = UNKNOWN;
161: }
162: /* Get operation type */
163: operation = get_operation_type();
164: /* Read user input about disk parameters */
165: get_disk_parameters();
166: for(controller=0; controller<MAXCONTROLLER; controller++) {
167: max_drive = (ctlr_type[controller] == SMDCTLR) ? 4 : 16;
168: for(drive=0; drive<max_drive; drive++)
169: if(drives_to_do[controller][drive] != -1)
170: do_operation(controller, drive, operation);
171: }
172: }
173:
174: /*
175: ** Get_operation_type allows the user to choose which operation he / she
176: ** wants to perform on all the drives selected. It returns the address of that
177: ** function.
178: */
179:
180: int (*get_operation_type())()
181: {
182: extern int format_operation();
183: extern int scan_operation();
184: extern int relocate_operation();
185:
186: for(;;) {
187: if(get_yes_no("Do you want to format media"))
188: if(get_yes_no(sure_question)) {
189: get_number_of_patterns();
190: operation_string = format_string;
191: return format_operation;
192: }
193: if(get_yes_no("Do you want to scan an existing file system for bad sectors")) {
194: get_number_of_patterns();
195: operation_string = scan_string;
196: return scan_operation;
197: }
198: if(get_yes_no("Do you want to add bad sectors from manufacturer's list")) {
199: operation_string = relocate_string;
200: return relocate_operation;
201: }
202: printf("\nAn operation must be specified!\n\n");
203: }
204: }
205:
206: /*
207: ** Get_number_of_patterns reads and validates and sets the number of
208: ** patterns to use during a verification operation. If no string was typed
209: ** then the number of patterns defaults to four.
210: */
211:
212: get_number_of_patterns()
213: {
214: char count_string[10];
215:
216: for(;;) {
217: count_string[0] = (char)0;
218: printf("Number of patterns to use when verifying? [1-16]: (16) ");
219: gets(count_string);
220: if(count_string[0]) {
221: sscanf(count_string, "%d", &verify_count);
222: if ((verify_count >= 1) && (verify_count <= 16))
223: break;
224: printf("\nPattern count must be between 1 and 16.\n\n");
225: }
226: else {
227: printf("\nDefaulting to 16 patterns.\n\n");
228: verify_count = 16;
229: break;
230: }
231: }
232: }
233:
234: /*
235: ** This routine ask the user the controller number, unit number,
236: ** and the drive type of the drive to be formatted. When all information is
237: ** gathered and correct, then, the open() system routine is called to initialize
238: ** the controller and spin up drives if necessary. If the open fails the whole
239: ** process is started over again.
240: */
241:
242: get_disk_parameters()
243: {
244: register int at_least_one_drive_specified = 0;
245: register int controller, drive, type;
246:
247: for(controller = 0; controller < MAXCONTROLLER; controller++) {
248: max_drive = (ctlr_type[controller] == SMDCTLR) ? 4 : 16;
249: for(drive = 0; drive < max_drive; drive++)
250: drives_to_do[controller][drive] = -1;
251: }
252: printf("\nEnter controller number and drive number for each disk.\n");
253: printf("Key <RETURN> to controller number prompt when through.\n");
254: for(;;) {
255: if((controller = get_controller_number()) != -1) {
256: drive = get_drive_number(controller);
257: type = get_drive_type(controller);
258: printf("Is an '%s' drive on controller %d, unit %d ok",
259: vdst[type].type_name, controller, drive);
260: if(get_yes_no("")) {
261: drives_to_do[controller][drive] = type;
262: at_least_one_drive_specified = 1;
263: }
264: else
265: printf("\nEntry ignored.\n");
266: continue;
267: }
268: if(at_least_one_drive_specified)
269: break;
270: printf("You must specify at least one controller number!\n");
271: }
272: }
273:
274: /*
275: ** Get_controller_number reads, validates and returns a controller number.
276: */
277:
278: get_controller_number()
279: {
280: char controller[20];
281: int controller_number;
282: int i, controller_printed = FALSE;
283:
284: for(;;) {
285: printf("\nController number? [");
286: for(i=0; i<MAXCONTROLLER; i++)
287: if(ctlr_type[i] != UNKNOWN) {
288: if(controller_printed)
289: printf(", ");
290: controller_printed = TRUE;
291: printf("%d", i);
292: }
293: printf("]: ");
294: gets(controller);
295: if(!controller[0])
296: return -1;
297: if(!sscanf(controller, "%d", &controller_number)) {
298: printf("\nResponse must start with a digit!\n");
299: continue;
300: }
301: if ((controller_number >= 0) && (controller_number <= 3))
302: if(ctlr_type[controller_number] != UNKNOWN)
303: break;
304: printf("\nController number %d is illegal!\n",
305: controller_number);
306: }
307: return controller_number;
308: }
309:
310: /*
311: ** Get_drive_number reads, validates and returns a drive number.
312: */
313:
314: get_drive_number(controller)
315: int controller;
316: {
317: char drive_string[20];
318: int drive, max_drive;
319:
320: for(;;) {
321: max_drive = (ctlr_type[controller] == SMDCTLR) ? 3 : 15;
322: printf("Unit number? [0-%d]: ", max_drive);
323: gets(drive_string);
324: if(!drive_string[0]) {
325: printf("\nA drive number must be specified!\n\n");
326: continue;
327: }
328: if(!sscanf(drive_string, "%d", &drive)) {
329: printf("\nResponse must start with a digit!\n\n");
330: continue;
331: }
332: if((drive >= 0) && (drive <= max_drive))
333: break;
334: printf("\nDrive number %d is illegal!\n\n", drive);
335: }
336: return drive;
337: }
338:
339: /*
340: ** Do_operation executes the function wanted on all drive in the to_to
341: ** list.
342: */
343:
344: do_operation(controller, drive, operation)
345: int controller, drive;
346: int (*operation)();
347: {
348: int status;
349:
350: printf("\n%s %s on controller %d, drive %d, started. %s\n\n",
351: stars, operation_string, controller, drive, stars);
352: if(!(status = _setjmp(environ))) {
353: drive_name = "unknown";
354: init_environment_for_operation(controller, drive);
355: (*operation)();
356: printf("\n%s %s on controller %d, drive %d completed ok. %s\n",
357: stars, operation_string, controller, drive, stars);
358: return;
359: }
360: printf("\n%s %s on controller %d, drive %d ABORTED! %s\n",
361: stars, operation_string, controller, drive, stars);
362: return;
363: }
364:
365: /*
366: ** Init_environment_for_operation reset all the variables used by the
367: ** system to suit the new drive type.
368: */
369:
370: init_environment_for_operation(controller, drive)
371: int controller, drive;
372: {
373: register int index;
374:
375: ctlr_num = controller;
376: controller_address = (cdr *)(vddcaddr[controller]+IOBASE);
377: unit_number = drive;
378: drive_type = drives_to_do[controller][drive];
379: drive_name = vdst[drive_type].type_name;
380: max_cyl = vdst[drive_type].ncyl;
381: max_trk = vdst[drive_type].ntrak;
382: max_sec = vdst[drive_type].nsec;
383: sector_size = vdst[drive_type].secsize;
384: num_slip = vdst[drive_type].nslip;
385: bad_secs = 0;
386: /* Init bad block pattern array */
387: for(index=0; index<TRKSIZ; index++) {
388: pattern_0[index] = vdst[drive_type].fmt_pat[0];
389: pattern_1[index] = vdst[drive_type].fmt_pat[1];
390: pattern_2[index] = vdst[drive_type].fmt_pat[2];
391: pattern_3[index] = vdst[drive_type].fmt_pat[3];
392: pattern_4[index] = vdst[drive_type].fmt_pat[4];
393: pattern_5[index] = vdst[drive_type].fmt_pat[5];
394: pattern_6[index] = vdst[drive_type].fmt_pat[6];
395: pattern_7[index] = vdst[drive_type].fmt_pat[7];
396: pattern_8[index] = vdst[drive_type].fmt_pat[8];
397: pattern_9[index] = vdst[drive_type].fmt_pat[9];
398: pattern_10[index] = vdst[drive_type].fmt_pat[10];
399: pattern_11[index] = vdst[drive_type].fmt_pat[11];
400: pattern_12[index] = vdst[drive_type].fmt_pat[12];
401: pattern_13[index] = vdst[drive_type].fmt_pat[13];
402: pattern_14[index] = vdst[drive_type].fmt_pat[14];
403: pattern_15[index] = vdst[drive_type].fmt_pat[15];
404: }
405: spin_up_drive();
406: }
407:
408: /*
409: ** Spin_up_drive starts the drives on a controller and waits around for
410: ** the drive to spin up if it is not already spinning.
411: */
412:
413: spin_up_drive()
414: {
415: VDDC_RESET(controller_address, ctlr_type[ctlr_num]);
416: if(ctlr_type[ctlr_num] == SMD_ECTLR) {
417: ((cdr *)controller_address)->cdr_csr = 0;
418: ((cdr *)controller_address)->mdcb_tcf = AM_ENPDA;
419: ((cdr *)controller_address)->dcb_tcf = AM_ENPDA;
420: ((cdr *)controller_address)->trail_tcf = AM_ENPDA;
421: ((cdr *)controller_address)->data_tcf = AM_ENPDA;
422: ((cdr *)controller_address)->cdr_ccf = CCF_STS | XMD_32BIT |
423: BSZ_16WRD | CCF_ERR | CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE;
424: }
425: access_with_no_trailer(INIT, 10);
426: access_with_no_trailer(DIAG, 20);
427: configure_drive(0);
428: }
429:
430: /*
431: ** Access_with_no_trailer is used to perform controller functions which
432: ** require no data movement.
433: */
434:
435: access_with_no_trailer(function, wait_time)
436: int function, wait_time;
437: {
438: dcb.opcode = function; /* command */
439: dcb.intflg = NOINT;
440: dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
441: dcb.operrsta = 0;
442: dcb.devselect = (function == VDSTART) ? 0 : (char)unit_number;
443: dcb.trailcnt = (char)0;
444: mdcb.firstdcb = &dcb;
445: mdcb.vddcstat = 0;
446: VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
447: POLLTILLDONE(controller_address, &dcb, wait_time, ctlr_type[ctlr_num]);
448: if(vdtimeout <= 0) {
449: printf(" during startup operation.\n");
450: _longjmp(environ, 1);
451: }
452: return dcb.operrsta;
453: }
454:
455: /*
456: ** Configure_drive tells the controller what kind of drive is attached
457: ** on a particular line.
458: */
459:
460: configure_drive(pass)
461: int pass;
462: {
463: dcb.opcode = RSTCFG; /* command */
464: dcb.intflg = NOINT;
465: dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
466: dcb.operrsta = 0;
467: dcb.devselect = (char)unit_number;
468: dcb.trail.rstrail.ncyl = max_cyl;
469: dcb.trail.rstrail.nsurfaces = max_trk;
470: if(ctlr_type[ctlr_num] == SMDCTLR)
471: dcb.trailcnt = (char)2;
472: else {
473: dcb.trailcnt = (char)4;
474: dcb.trail.rstrail.nsectors = max_sec;
475: dcb.trail.rstrail.slip_sec = num_slip;
476: }
477: mdcb.firstdcb = &dcb;
478: mdcb.vddcstat = 0;
479: VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
480: POLLTILLDONE(controller_address, &dcb, 5, ctlr_type[ctlr_num]);
481: if(vdtimeout <= 0) {
482: printf(" during drive configuration.\n");
483: _longjmp(environ, 1);
484: }
485: if(dcb.operrsta & (NOTCYLERR | DRVNRDY)) {
486: if(pass) {
487: printf("\nDrive failed to start!\n\n");
488: _longjmp(environ, -1);
489: }
490: access_with_no_trailer(VDSTART, (unit_number*6)+62);
491: DELAY((unit_number * 5500000) + 62000000);
492: configure_drive(1);
493: }
494: }
495:
496: /*
497: ** Get_drive_type reads a drive type, validates it and returns the
498: ** validated drive type number to the calling routine.
499: */
500:
501: get_drive_type(controller)
502: register int controller;
503: {
504: char disk[10];
505:
506: for(;;) {
507: printf("Drive type? [fsd/smd/xfd");
508: if(ctlr_type[controller] == SMD_ECTLR)
509: printf("/fuj/xsd");
510: printf("]: ");
511: gets(disk);
512: if (!strcmp(disk,"fsd"))
513: return FSD;
514: if (!strcmp(disk,"smd"))
515: return SMD;
516: if (!strcmp(disk,"xfd"))
517: return XFD;
518: if(ctlr_type[controller] == SMD_ECTLR) {
519: if (!strcmp(disk,"fuj"))
520: return FUJ;
521: if (!strcmp(disk,"xsd"))
522: return XSD;
523: }
524: printf("Illegal drive type '%s'\n", disk);
525: }
526: }
527:
528: /*
529: ** Format_operation is the main routine for formatting a disk.
530: ** Take note the the first operation must be format_data_area due to a micro-
531: ** code bug in the controller. While formatting sectors one sector count is
532: ** lost every 64k sectors, so, on an fsd drive the last four sectors will
533: ** not be formatted. This is overcome by formatting the entire disk first, then
534: ** formatting the maintenance area, which happens to cover the last n sectors
535: ** that were lost.
536: */
537:
538: format_operation()
539: {
540: /* Format relocation area and data area */
541: format_data_area();
542: /* Verify the reloaction area and data area */
543: verify_data_area();
544: if(bad_secs)
545: relocate_bad_blocks();
546: /* Format maintenance cylinders for field service */
547: lay_maint_cylinders();
548: }
549:
550: /*
551: ** Relocate_operation() prompts the user for bad block numbers and adds
552: ** to the current bad block table. This process continues until a null response
553: ** is typed in when prompted for a bad block. The user is asked to verify that
554: ** the block number typed in is really the block that they wanted to relocate
555: ** before the tables are updated.
556: */
557:
558: relocate_operation()
559: {
560: long old_blk;
561: char nxt_blk[20];
562: dskadr daddr;
563: long last_block = max_cyl * max_trk * max_sec;
564:
565: bad_secs = 0;
566: printf("Just key <RETURN> when through entering sector numbers.\n");
567: while(bad_secs < MAXERR) {
568: printf("Sector number? ");
569: gets(nxt_blk);
570: if(!nxt_blk[0])
571: break;
572: sscanf(nxt_blk, "%ld", &old_blk);
573: if(old_blk >= last_block) {
574: printf("\nBlock number must be between 0 and %d!\n\n",
575: last_block - 1);
576: continue;
577: }
578: printf("Relocate sector #%ld", old_blk);
579: if(get_yes_no("")) {
580: daddr.sector = old_blk % max_sec;
581: daddr.track = (old_blk / max_sec) % max_trk;
582: daddr.cylinder = (old_blk / max_sec) / max_trk;
583: flag_sector(&daddr, 0);
584: }
585: else
586: printf("\nEntry ignored.\n");
587: printf("\n");
588: }
589: relocate_bad_blocks();
590: }
591:
592: /*
593: **
594: */
595:
596: scan_operation()
597: {
598: register int status, j=0;
599: dskadr daddr;
600: register int old_verify_count = verify_count;
601:
602: bad_secs = 0;
603: daddr.sector = 0;
604: for(daddr.cylinder=0;daddr.cylinder<(max_cyl-NUMSYS);daddr.cylinder++) {
605: if(j++ % 50) printf(".");
606: else printf("\n Scanning cylinder %d.", (int)daddr.cylinder);
607: for(daddr.track = 0; daddr.track < max_trk; daddr.track++) {
608: status = access(save, &daddr , FTR, 1, 1);
609: if(status & (HRDERR | SFTERR))
610: verify_count = 16;
611: verify_track(&daddr);
612: status = access(save, &daddr , FTW, 1, 1);
613: verify_count = old_verify_count;
614: }
615: }
616: printf("\n");
617: print_bad_sector_list();
618: print_number_of_bad_sectors("file system");
619: if(bad_secs)
620: relocate_bad_blocks();
621: }
622:
623: /*
624: ** This routine formats the user data area and bad block area of the disk.
625: */
626:
627: format_data_area()
628: {
629: register long sector_count;
630: dskadr zero;
631: register int index;
632:
633: printf("Formatting media...\n");
634: zero.cylinder = (short)0;
635: zero.track = (char)0;
636: zero.sector = (char)0;
637: sector_count = (long)(max_cyl * max_trk * max_sec);
638: format_sectors(&zero, &zero, NRM, sector_count);
639: printf("Formatting complete.\n");
640: }
641:
642: /*
643: ** This routine creates a bad block relocation area on disk and then
644: ** certifies the user data area of the disk.
645: */
646:
647: verify_data_area()
648: {
649: printf("Scanning for bad sectors...\n");
650: init_relocation_area();
651: verify_user_data_area();
652: printf("\nBad block scan complete.\n");
653: }
654:
655: /*
656: ** Vdinit_relocation_area certifies the bad block relocation area and flags
657: ** each bad block in the area as bad for later use by load_free_table(). No
658: ** relocation takes place.
659: */
660:
661: init_relocation_area()
662: {
663: printf(" Verifying bad sector relocation area...\n");
664: verify_cylinders((int)max_cyl - NUMSYS, NUMREL);
665: print_number_of_bad_sectors("relocation");
666: if(bad_secs)
667: mark_sectors_as_bad();
668: printf(" Relocation area verified.\n");
669: }
670:
671: /*
672: ** This routine certifies the area to be used for user data.
673: */
674:
675: verify_user_data_area()
676: {
677: int temp;
678:
679: printf(" Verifying file system area...\n");
680: verify_cylinders(0, (int)(max_cyl-NUMSYS));
681: print_number_of_bad_sectors("file system");
682: printf(" File system area verified.\n");
683: }
684:
685: /*
686: ** Vdlay_maint_cylinders creates the maintenance cylinders for field
687: ** service techs to run diagnostics on.
688: */
689:
690: lay_maint_cylinders()
691: {
692: dskadr daddr;
693: register long *pattern_ptr = (long *)&daddr;
694: register int index;
695:
696: printf("Creating maintenance cylinders...\n");
697: /* format read only cylinder */
698: daddr.cylinder = (short)(max_cyl - NUMMNT);
699: daddr.track = daddr.sector = (char)0;
700: format_sectors(&daddr, &daddr, NRM, (long)(NUMMNT * max_trk * max_sec));
701: printf(" Verifying maintenance area...\n");
702: verify_cylinders(daddr.cylinder, NUMMNT);
703: print_number_of_bad_sectors("maintenance");
704: printf(" Maintenance area verified.\n");
705: printf(" Writing Read / Write patterns.\n");
706: daddr.cylinder = (short)(max_cyl - NUMMNT);
707: for(daddr.track=0; daddr.track<max_trk; daddr.track++)
708: for(daddr.sector=0; daddr.sector<max_sec; daddr.sector++)
709: if(is_bad(&daddr)) {
710: extern rel_type new_location();
711: rel_type type;
712: dskadr naddr;
713:
714: type = new_location(&daddr, &naddr,
715: dsk_err[index].err_stat);
716: relocate(&daddr, &naddr, type);
717: }
718: printf(" Writing Read only patterns.\n");
719: for(daddr.cylinder++; daddr.cylinder<max_cyl; daddr.cylinder++) {
720: for(daddr.track=0; daddr.track<max_trk; daddr.track++)
721: for(daddr.sector = 0; daddr.sector < max_sec;
722: daddr.sector++) {
723: for(index=0; index<(sector_size/sizeof(long)); index++)
724: scratch[index] = *pattern_ptr;
725: if(is_bad(&daddr))
726: mark_bad_sector(&daddr, (short)VDWPT);
727: else
728: format_sectors(&daddr,&daddr,WPT,(long)1);
729: }
730: }
731: printf("Maintenance cylinders complete.\n");
732: }
733:
734: /*
735: ** Is_bad checks to see if a block is known to be bad already.
736: */
737:
738: is_bad(daddr)
739: dskadr *daddr;
740: {
741: register int index;
742:
743: for(index=0; index<bad_secs; index++)
744: if(daddr->cylinder == dsk_err[index].err_adr.cylinder)
745: if(daddr->track == dsk_err[index].err_adr.track)
746: if(daddr->sector==dsk_err[index].err_adr.sector)
747: return 1;
748: return 0;
749: }
750:
751: /*
752: ** verify_cylinders does full track certification for every track
753: ** on the cylinder. This is done for speed and minimal head movement. If
754: ** an error occurs on any single track the track is flagged for later
755: ** verification by verify sectors.
756: */
757:
758: verify_cylinders(base_cyl, cyl_count)
759: int base_cyl, cyl_count;
760: {
761: register int i, j = 0;
762: dskadr daddr;
763:
764: bad_secs = 0;
765: /* verify each track of each cylinder */
766: for (daddr.cylinder=base_cyl; daddr.cylinder<(base_cyl+cyl_count);
767: daddr.cylinder++) {
768: if(j++ % 50) printf(".");
769: else printf("\n Scanning cylinder %d.", (int)daddr.cylinder);
770: for (daddr.track = 0; daddr.track < max_trk; daddr.track++) {
771: verify_track(&daddr);
772: }
773: }
774: printf("\n");
775: print_bad_sector_list();
776: }
777:
778: /*
779: ** verify_track verifies a single track. If the full track write and
780: ** compare operation fails then each sector is read individually to determin
781: ** which sectors are really bad. If a sector is bad it is flagged as bad by
782: ** the verify sector routine.
783: */
784:
785: verify_track(daddr)
786: dskadr *daddr;
787: {
788: register int index, i;
789: register int count;
790: register long before;
791: register long *after;
792: register int status;
793: register long offset = sector_size / sizeof(long);
794: int pattern_count = verify_count;
795:
796: daddr->sector = (char)0;
797: access(pattern_address[0], daddr, FTW, 1, 1);
798: for(index = 0; index < pattern_count; index++) {
799: data_ok(dcb.operrsta);
800: if(dcb.operrsta & HEADER_ERROR) {
801: if(ctlr_type[ctlr_num]==SMDCTLR)
802: goto hard;
803: flag_sector(daddr, dcb.operrsta);
804: break;
805: }
806: if(status & DATA_ERROR)
807: pattern_count = 16;
808: status = access(scratch, daddr, FTR, 1, 1);
809: if(status & HEADER_ERROR) {
810: if(ctlr_type[ctlr_num]==SMDCTLR)
811: goto hard;
812: flag_sector(daddr, status);
813: break;
814: }
815: if(!data_ok(status)) {
816: pattern_count = 16;
817: for(i = 0; i < max_sec; i++) {
818: register long *next;
819:
820: daddr->sector = i;
821: next = &scratch[i * offset];
822: status = access(next, daddr, RD, 1, 1);
823: if(!data_ok(status))
824: flag_sector(daddr, status);
825: }
826: daddr->sector = (char)0;
827: }
828: if(index+1 < pattern_count)
829: access(pattern_address[index+1], daddr, FTW, 1, 0);
830: count = max_sec * offset;
831: before = *pattern_address[index];
832: after = scratch;
833: mtpr(0, PADC);
834: for(i=0; i<count; i++) {
835: if(before != *(after++)) {
836: daddr->sector = i / offset;
837: flag_sector(daddr, 0);
838: }
839: }
840: POLLTILLDONE(controller_address, &dcb, 60, ctlr_type[ctlr_num]);
841: if(vdtimeout <= 0) {
842: printf(" in verify_track.\n");
843: _longjmp(environ, 1);
844: }
845: }
846: return;
847:
848: hard: printf("\nSMD Controllers can't recover from header errors!");
849: printf(" Status = 0x%x\n", dcb.operrsta);
850: _longjmp(environ, dcb.operrsta);
851: }
852:
853: /*
854: ** Vdflag_sector makes an entry into the bad block table for the current
855: ** bad sector.
856: */
857:
858: flag_sector(daddr, status)
859: dskadr *daddr;
860: long status;
861: {
862: register int index;
863:
864: if(bad_secs < MAXERR) {
865: for(index=0; index<bad_secs; index++)
866: if((dsk_err[index].err_adr.cylinder==daddr->cylinder) &&
867: (dsk_err[index].err_adr.track == daddr->track) &&
868: (dsk_err[index].err_adr.sector==daddr->sector))
869: return;
870: dsk_err[bad_secs].err_adr.cylinder = daddr->cylinder;
871: dsk_err[bad_secs].err_adr.track = daddr->track;
872: dsk_err[bad_secs].err_adr.sector = daddr->sector;
873: dsk_err[bad_secs++].err_stat = status;
874: return;
875: }
876: printf("Maximum number of %d bad tracks exceeded!\n", MAXERR);
877: _longjmp(environ, MAXERR);
878: }
879:
880: /*
881: ** Print_bad_sector list tells the user which sectors are bad.
882: */
883:
884: print_bad_sector_list()
885: {
886: register int index;
887: register int sec;
888: dskadr daddr;
889:
890: if(bad_secs) {
891: printf(" The following sector");
892: if(bad_secs == 1)
893: printf(" is");
894: else
895: printf("s are");
896: printf(" bad:\n");
897: for(index=0; index<bad_secs; index++) {
898: sec = dsk_err[index].err_adr.cylinder * max_trk;
899: sec += dsk_err[index].err_adr.track;
900: sec *= max_sec;
901: sec += dsk_err[index].err_adr.sector;
902: printf(" %ld\n", sec);
903: }
904: }
905: }
906:
907: /*
908: ** Print_number_of_bad_sectors is used to announce to the user the
909: ** total number of bad sectors in a particular segment of the disk. Rules
910: ** of english for making a word plural are used to make a readable sentence.
911: */
912:
913: print_number_of_bad_sectors(str)
914: char *str;
915: {
916: if(bad_secs)
917: printf("\n %d", bad_secs);
918: else
919: printf("\n No");
920: printf(" bad sector");
921: if(bad_secs != 1)
922: printf("s");
923: printf(" found in %s area.\n", str);
924: }
925:
926: /*
927: ** Vdmark_sectors_as_bad marks every block in the current bad block
928: ** table as bad on the disk.
929: */
930:
931: mark_sectors_as_bad()
932: {
933: register int index;
934: dskadr daddr;
935:
936: for(index=0; index<bad_secs; index++) {
937: daddr.cylinder = dsk_err[index].err_adr.cylinder;
938: daddr.track = dsk_err[index].err_adr.track;
939: daddr.sector = dsk_err[index].err_adr.sector;
940: mark_bad_sector(&daddr, (short)0);
941: }
942: }
943:
944: /*
945: ** mark_bad_sector marks a single sector, on disk, as bad using the bad
946: ** block flags in the sector header.
947: */
948:
949: mark_bad_sector(daddr, flags)
950: dskadr *daddr;
951: short flags;
952: {
953: format_sectors(daddr, daddr, BAD | flags, (long)1);
954: }
955:
956:
957: /*
958: ** relocate_bad_blocks scans the current disk error table and relocates
959: ** every block that is flagged as bad in the table. At the beginning of the
960: ** routine the operator is given the chance to add blocks of his/her own to
961: ** the table.
962: */
963:
964: relocate_bad_blocks()
965: {
966: extern rel_type new_location();
967: register int index;
968: dskadr daddr, naddr;
969: rel_type type;
970:
971: printf("Relocating bad sectors...\n");
972: load_free_table();
973: for(index=0; index<bad_secs; index++) {
974: daddr.cylinder = dsk_err[index].err_adr.cylinder;
975: daddr.track = dsk_err[index].err_adr.track;
976: daddr.sector = dsk_err[index].err_adr.sector;
977: type = new_location(&daddr, &naddr, dsk_err[index].err_stat);
978: relocate(&daddr, &naddr, type);
979: }
980: printf("Relocation complete.\n");
981: }
982:
983: /*
984: ** Vdrelocate commands the controller to relocate a block from daddr
985: ** to naddr. Once this has been done the controller will automatically perform
986: ** relocation whenever data is transfered to or from daddr.
987: */
988:
989: relocate(daddr, naddr, type)
990: dskadr *daddr, *naddr;
991: rel_type type;
992: {
993: if(type == FULL_TRACK)
994: relocate_track(daddr, naddr);
995: else
996: relocate_sector(daddr, naddr);
997: }
998:
999:
1000: /*
1001: **
1002: */
1003:
1004: relocate_sector(daddr, naddr)
1005: dskadr *daddr, *naddr;
1006: {
1007: dskadr phys, reloc;
1008: register long blk;
1009: register long status;
1010:
1011: access(save, daddr, RD, 1, 1);
1012: phys.cylinder = daddr->cylinder;
1013: phys.track = daddr->track;
1014: phys.sector = daddr-> sector;
1015: reloc.cylinder = naddr->cylinder;
1016: reloc.track = naddr->track;
1017: reloc.sector = naddr->sector;
1018: format_sectors(&phys, &reloc, RELOC_SECTOR, (long)1);
1019:
1020: phys.cylinder = naddr->cylinder;
1021: phys.track = naddr->track;
1022: phys.sector = naddr->sector;
1023: reloc.cylinder = daddr->cylinder;
1024: reloc.track = daddr->track;
1025: reloc.sector = daddr->sector;
1026: format_sectors(&phys, &reloc, ALT_SECTOR, (long)1);
1027: blk = (daddr->cylinder * max_trk) + daddr->track;
1028: blk = (blk * max_sec) + daddr->sector;
1029: printf(" Sector #%ld ", blk);
1030: status = access(save, daddr, WD, 1, 1);
1031: if((status & ALTACC) && !(status & HRDERR)) {
1032: blk = (naddr->cylinder * max_trk) + naddr->track;
1033: blk = (blk * max_sec) + naddr->sector;
1034: printf("relocated to sector #%d.\n", blk);
1035: return;
1036: }
1037: printf("was not relocated successfully! Status = %lx\n", status);
1038: }
1039:
1040:
1041:
1042: /*
1043: **
1044: */
1045:
1046: relocate_track(daddr, naddr)
1047: dskadr *daddr, *naddr;
1048: {
1049: dskadr phys, reloc;
1050: register long blk;
1051: register long status;
1052:
1053: access(save, daddr, FTR, 1, 1);
1054: phys.cylinder = daddr->cylinder;
1055: phys.track = daddr->track;
1056: phys.sector = 0;
1057: reloc.cylinder = naddr->cylinder;
1058: reloc.track = naddr->track;
1059: reloc.sector = 0xff;
1060: format_sectors(&phys, &reloc, RELOC_SECTOR, max_sec);
1061:
1062: phys.cylinder = naddr->cylinder;
1063: phys.track = naddr->track;
1064: phys.sector = 0;
1065: reloc.cylinder = daddr->cylinder;
1066: reloc.track = daddr->track;
1067: reloc.sector = 0;
1068: format_sectors(&phys, &reloc, ALT_SECTOR, max_sec);
1069: blk = (daddr->cylinder * max_trk) + daddr->track;
1070: printf(" Track #%ld ", blk);
1071: status = access(save, daddr, FTW, 1, 1);
1072: if((status & ALTACC) && !(status & HRDERR)) {
1073: blk = (naddr->cylinder * max_trk) + naddr->track;
1074: printf("relocated to track #%d.\n", blk);
1075: return;
1076: }
1077: printf("was not relocated successfully! Status = %lx\n", status);
1078: }
1079:
1080: /*
1081: ** access is used by other routines to do reads and writes to the disk.
1082: ** The status of the read / write is returned to the caller for processing.
1083: */
1084:
1085: access(buf, daddr, func, count, wait)
1086: char *buf;
1087: dskadr *daddr;
1088: int func, count, wait;
1089: {
1090: dcb.opcode = func; /* format sector command */
1091: dcb.intflg = NOINT;
1092: dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
1093: dcb.operrsta = 0;
1094: dcb.devselect = (char)unit_number;
1095: dcb.trailcnt = (char)(sizeof(trrw) / 4);
1096: dcb.trail.rwtrail.memadr = buf;
1097: dcb.trail.rwtrail.wcount = count * (sector_size / sizeof(short));
1098: dcb.trail.rwtrail.disk.cylinder = daddr->cylinder;
1099: dcb.trail.rwtrail.disk.track = daddr->track;
1100: dcb.trail.rwtrail.disk.sector = daddr->sector;
1101: mdcb.firstdcb = &dcb;
1102: mdcb.vddcstat = 0;
1103: VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
1104: if(wait) {
1105: POLLTILLDONE(controller_address,&dcb,2*60,ctlr_type[ctlr_num]);
1106: if(vdtimeout <= 0) {
1107: printf(" in access.\n");
1108: _longjmp(environ, 1);
1109: }
1110: }
1111: return dcb.operrsta;
1112: }
1113:
1114: /*
1115: ** Vdformat_sectors is used to do the actual formatting of a block.
1116: */
1117:
1118: format_sectors(daddr, haddr, flags, count)
1119: dskadr *daddr, *haddr;
1120: short flags;
1121: long count;
1122: {
1123: dcb.opcode = FSECT; /* format sector command */
1124: dcb.intflg = NOINT;
1125: dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
1126: dcb.operrsta = 0;
1127: dcb.devselect = (char)unit_number;
1128: dcb.trailcnt = (char)(sizeof(trfmt) / 4);
1129: dcb.trail.fmtrail.addr = (char *)scratch;
1130: dcb.trail.fmtrail.nsectors = count;
1131: dcb.trail.fmtrail.disk.cylinder = daddr->cylinder | flags;
1132: dcb.trail.fmtrail.disk.track = daddr->track;
1133: dcb.trail.fmtrail.disk.sector = daddr->sector;
1134: dcb.trail.fmtrail.hdr.cylinder = haddr->cylinder | flags;
1135: dcb.trail.fmtrail.hdr.track = haddr->track;
1136: dcb.trail.fmtrail.hdr.sector = haddr->sector;
1137: mdcb.firstdcb = &dcb;
1138: mdcb.vddcstat = 0;
1139: VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
1140: POLLTILLDONE(controller_address, &dcb,
1141: ((count+849)/850)+120, ctlr_type[ctlr_num]);
1142: if(vdtimeout <= 0) {
1143: printf(" in format_sectors.\n");
1144: _longjmp(environ, 1);
1145: }
1146: if (!data_ok(dcb.operrsta)) {
1147: printf("Data error during format operation!\n");
1148: _longjmp(environ, dcb.operrsta);
1149: }
1150: }
1151:
1152: /*
1153: ** Print_dcb() dumps the MDCB and DCB for diagnostic purposes. This
1154: ** routine is called whenever a fatal error is encountered.
1155: */
1156:
1157: printdcb(ptr)
1158: register long *ptr;
1159: {
1160: register long i;
1161: register long trailer_count;
1162:
1163: printf("Dump of MDCB: ");
1164: for(i=0; i<4; i++)
1165: printf(" %lx", *(ptr+i));
1166: if(ptr = (long *)*ptr) {
1167: printf(" and DCB:");
1168: trailer_count = *(ptr+3) & 0xff;
1169: for(i=0; i<7+trailer_count; i++) {
1170: uncache(ptr+i);
1171: printf(" %lx", *(ptr+i));
1172: }
1173: }
1174: printf("\n");
1175: for(i=0; i<5000000; i++) ;
1176: }
1177:
1178:
1179: /*
1180: ** Vdload_free_table checks each block in the bad block relocation area
1181: ** to see if it is used. If it is, the free relocation block table is updated.
1182: */
1183:
1184: load_free_table()
1185: {
1186: dskadr daddr;
1187: register int i, j;
1188:
1189: /* Clear free table before starting */
1190: for(i = 0; i < (max_trk * NUMREL); i++)
1191: for(j = 0; j < max_sec; j++) {
1192: free[i][j].freestatus = NOTALLOCATED;
1193: free[i][j].error = (long)0;
1194: }
1195: /* For each relocation cylinder */
1196: for(daddr.cylinder = max_cyl-NUMSYS;
1197: daddr.cylinder < (max_cyl-NUMMNT); daddr.cylinder++)
1198: /* For each track on a cylinder */
1199: for(daddr.track = 0; daddr.track < max_trk; daddr.track++)
1200: /* For each sector on a track */
1201: for(daddr.sector=0;daddr.sector<max_sec;daddr.sector++)
1202: if(block_should_be_allocated(&daddr))
1203: allocate(&daddr, (long)0);
1204: }
1205:
1206: /*
1207: ** block_should_be_allocated does a read header and data command into
1208: ** a local buffer. The header address is then checked to see if the block
1209: ** has the relocation bit set or if the block was marked as bad. (either VDMF
1210: ** or VDUF or both bits set). In addition, if data data error is picked up
1211: ** during the read then the block should be allocated. If the block meets
1212: ** the above criteria then
1213: */
1214:
1215: block_should_be_allocated(daddr)
1216: dskadr *daddr;
1217: {
1218: /* return false if already replaced or either VDMF or VDUF is set */
1219: return (access(scratch, daddr, RD, 1, 1) & (HRDERR|SFTERR|ALTACC));
1220: }
1221:
1222: /*
1223: ** allocate marks a replacement sector as used.
1224: */
1225:
1226: allocate(daddr, status)
1227: dskadr *daddr;
1228: long status;
1229: {
1230: register long trk;
1231:
1232: trk = daddr->cylinder - (max_cyl - NUMSYS);
1233: trk *= max_trk;
1234: trk += daddr->track;
1235: free[trk][daddr->sector].freestatus = ALLOCATED;
1236: free[trk][daddr->sector].error = status;
1237: }
1238:
1239: /*
1240: ** Vdnew_location allocates a replacement block given a bad block address.
1241: ** The algorithm is fairly simple; it simply searches for the first
1242: ** free sector that has the same sector number of the bad sector. If no sector
1243: ** is found then the drive should be considered bad because of a microcode bug
1244: ** in the controller that forces us to use the same sector number as the bad
1245: ** sector for relocation purposes. Using different tracks and cylinders is ok
1246: ** of course.
1247: */
1248:
1249: rel_type new_location(daddr, naddr, status)
1250: dskadr *daddr, *naddr;
1251: long status;
1252: {
1253: register int index, sec;
1254:
1255: if(status & (HEADER_ERROR)) {
1256: for(index = 0; index < (max_trk * NUMREL); index++) {
1257: for(sec=0; sec < max_sec; sec++) {
1258: if(free[index][sec].freestatus == ALLOCATED)
1259: break;
1260: }
1261: if(sec == max_sec) {
1262: for(sec = 0; sec < max_sec; sec++) {
1263: free[index][sec].freestatus = ALLOCATED;
1264: free[index][sec].error = status;
1265: }
1266: naddr->cylinder=index/max_trk+(max_cyl-NUMSYS);
1267: naddr->track = index % max_trk;
1268: naddr->sector = 0;
1269: return FULL_TRACK;
1270: }
1271: }
1272: }
1273: for(index = 0; index < (max_trk * NUMREL); index++)
1274: if(free[index][daddr->sector].freestatus != ALLOCATED) {
1275: free[index][daddr->sector].freestatus = ALLOCATED;
1276: free[index][daddr->sector].error = status;
1277: naddr->cylinder = index / max_trk + (max_cyl - NUMSYS);
1278: naddr->track = index % max_trk;
1279: naddr->sector = daddr->sector;
1280: return SINGLE_SECTOR;
1281: }
1282: printf("Bad sector relocation area is full!");
1283: _longjmp(environ, daddr->sector);
1284: }
1285:
1286: /*
1287: ** data_ok checks an error status word for bit patterns
1288: ** associated with error conditions from the VDDC controller. If a hardware
1289: ** error is present then the problem is reported on the console and the program
1290: ** is halted. If a data error is present the a zero is returned.
1291: ** If everything is OK then a 1 is returned.
1292: */
1293:
1294: data_ok(status)
1295: long status;
1296: {
1297: if(status & HARD_ERROR){
1298: if(status & DRVNRDY)
1299: printf("\nDrive is not ready!");
1300: else if(status & INVDADR)
1301: printf("\nInvalid disk address issued!");
1302: else if(status & DNEMEM)
1303: printf("\nNon-existent memory error!");
1304: else if(status & PARERR)
1305: printf("\nMain memory parity error!");
1306: else if(status & OPABRT)
1307: printf("\nCPU aborted operation!");
1308: else if(status & WPTERR)
1309: printf("\nDrive is write protected!");
1310: else if(status & DSEEKERR)
1311: printf("\nDisk seek error!");
1312: else
1313: printf("\nNot on cylinder error!");
1314: printf(" Status = %lx\n", status);
1315: _longjmp(environ, -1);
1316: }
1317: return (int)(!(status & DATA_ERROR));
1318: }
1319:
1320: /*
1321: ** Vdget_yes_no is used to ask simple yes or no questions. The question
1322: ** prompt is supplied by the caller, The question mark, possible responses,
1323: ** and the default response is printed at the end of the prompt. The routine
1324: ** then reads the answer and returns a 1 if a 'y' is typed or no response was
1325: ** given, otherwise, a zero is returned.
1326: */
1327:
1328: get_yes_no(str)
1329: register char *str;
1330: {
1331: char answer[80];
1332:
1333: for(;;) {
1334: printf("%s? [y/n] (n): ", str);
1335: gets(answer);
1336: if((answer[0] == 'Y') || (answer[0] == 'y'))
1337: return(TRUE);
1338: if((answer[0] == 'N') || (answer[0] == 'n'))
1339: return(FALSE);
1340: printf("\nA 'Y' (yes) or 'N' (no) must be typed!\n\n");
1341: }
1342: }
1343:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.