|
|
Power 6/32 Unix version 1.2b
/*
** format disk on VDDC / SMD_E controller - (fsd/smd/xfd/xsd) type
**
** Author: John R. Franks
**
** This program is used to maintain drives attached to a VDDC controller.
** The basic functions are 1) format media, 2) verify media, 3) relocate
** bad blocks that have appeared on the media, and 4) Format and initialize the
** maintenance cylinders on the drive.
**
** For in depth information on the structure of this program please refer
** to the comments above the individual subroutines in addition to the design
** notes in vdutil.doc.
*/
#include <setjmp.h>
#include "../machine/mtpr.h"
#include "../h/param.h"
#include "../h/inode.h"
#include "../h/fs.h"
#include "../vba/vddc.h"
#include "../stand/saio.h"
#define MAXCONTROLLER 4
#define MAXDRIVE 16
#define NUMMNT 2
#define NUMREL 3
#define NUMSYS (NUMREL+NUMMNT)
#define TRUE 1
#define FALSE 0
#define MAXTRKS 24
#define MAXSECS_PER_TRK 48
#define MAXERR 1000
#define SECSIZ 512
#define TRKSIZ ((SECSIZ/sizeof(long)) * MAXSECS_PER_TRK)
#define HARD_ERROR (DRVNRDY | INVDADR | DNEMEM | PARERR | OPABRT | \
WPTERR | DSEEKERR | NOTCYLERR)
#define DATA_ERROR (CTLRERR | UCDATERR | DCOMPERR | DSERLY | DSLATE | \
TOPLUS | TOMNUS | CPDCRT | \
HRDERR | SFTERR)
#define HEADER_ERROR (HCRCERR | HCMPERR)
#define NRM (short)0
#define BAD (short)VDUF
#define WPT (short)(NRM | VDWPT)
#define RELOC_SECTOR (short)(VDALT)
#define ALT_SECTOR (short)(VDALT)
static jmp_buf environ;
/* Free bad block allocation bit map */
typedef struct {
long error;
enum {ALLOCATED, NOTALLOCATED} freestatus;
} fmt_free;
typedef enum {SINGLE_SECTOR, FULL_TRACK} rel_type;
typedef struct {
dskadr err_adr;
long err_stat;
} fmt_err;
static fmt_free free[NUMREL*MAXTRKS][MAXSECS_PER_TRK];
static fmt_err dsk_err[MAXERR];/* Disk error table */
static fmt_mdcb mdcb; /* Master device control block */
static fmt_dcb dcb; /* Device control blocks */
static int bad_secs = 0; /* Sequence # of last error detected */
static cdr *controller_address; /* controller physical address */
static int ctlr_num = 0; /* ctlr number */
static int unit_number = 0; /* unit number */
static int drive_type; /* disk type index */
static char *drive_name;
static int verify_count = 16;
static char *stars = "***************";
static char *sure_question = "This is DESTRUCTIVE! Are you sure";
static char *operation_string = 0;
static char *format_string = "Formatting";
static char *scan_string = "Scanning";
static char *relocate_string = "Relocation";
static char drives_to_do[MAXCONTROLLER][MAXDRIVE];
static char ctlr_type[MAXCONTROLLER] =
{UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN};
static char *controller_name[MAXCONTROLLER] =
{"Unknown","Unknown","Unknown","Unknown"};
static long pattern_0[TRKSIZ], pattern_1[TRKSIZ];
static long pattern_2[TRKSIZ], pattern_3[TRKSIZ];
static long pattern_4[TRKSIZ], pattern_5[TRKSIZ];
static long pattern_6[TRKSIZ], pattern_7[TRKSIZ];
static long pattern_8[TRKSIZ], pattern_9[TRKSIZ];
static long pattern_10[TRKSIZ], pattern_11[TRKSIZ];
static long pattern_12[TRKSIZ], pattern_13[TRKSIZ];
static long pattern_14[TRKSIZ], pattern_15[TRKSIZ];
static long *pattern_address[] = {
pattern_0, pattern_1, pattern_2, pattern_3,
pattern_4, pattern_5, pattern_6, pattern_7,
pattern_8, pattern_9, pattern_10, pattern_11,
pattern_12, pattern_13, pattern_14, pattern_15
};
/* Double buffer for scanning existing file systems and general scratch */
static long scratch[TRKSIZ];
static long save[TRKSIZ];
static long max_cyl;
static long max_trk;
static long max_sec;
static long sector_size;
static long num_slip;
static int max_drive;
/*
** Main, of course, is the entry point into vdfmt. It is responsible
** for calling the routine for the get the disk parameters from the user,
** creating and initializing the bad block patterns for scanning the disk, and
** determining whether or not the user wants to format or relocate bad
** blocks before calling the appropriate routines.
**
** Formatting implies cerification of media and creation of the maintenance
** cylinders and relocation of bad blocks.
*/
main()
{
register int index;
register int (*operation)();
register int controller, drive;
cdr *controller_address;
int (*get_operation_type())();
printf("VDFORMAT Version 2.\n\n");
/* Identify which controllers are present and what type they are. */
for(controller = 0; controller < MAXCONTROLLER; controller++) {
controller_address = (cdr *)(vddcaddr[controller]|IOBASE);
if(!badaddr(controller_address, 2)) {
controller_address->cdr_reset = 0xffffffff;
DELAY(1000000);
if(controller_address->cdr_reset != 0xffffffff) {
ctlr_type[controller] = SMDCTLR;
controller_name[controller] = "SMD";
DELAY(1000000);
}
else {
ctlr_type[controller] = SMD_ECTLR;
controller_name[controller] = "SMD/E";
controller_address->cdr_reserved = 0x0;
DELAY(3000000);
}
printf("Controller #%d is an %s disk controller.\n",
controller, controller_name[controller]);
}
else
ctlr_type[controller] = UNKNOWN;
}
/* Get operation type */
operation = get_operation_type();
/* Read user input about disk parameters */
get_disk_parameters();
for(controller=0; controller<MAXCONTROLLER; controller++) {
max_drive = (ctlr_type[controller] == SMDCTLR) ? 4 : 16;
for(drive=0; drive<max_drive; drive++)
if(drives_to_do[controller][drive] != -1)
do_operation(controller, drive, operation);
}
}
/*
** Get_operation_type allows the user to choose which operation he / she
** wants to perform on all the drives selected. It returns the address of that
** function.
*/
int (*get_operation_type())()
{
extern int format_operation();
extern int scan_operation();
extern int relocate_operation();
for(;;) {
if(get_yes_no("Do you want to format media"))
if(get_yes_no(sure_question)) {
get_number_of_patterns();
operation_string = format_string;
return format_operation;
}
if(get_yes_no("Do you want to scan an existing file system for bad sectors")) {
get_number_of_patterns();
operation_string = scan_string;
return scan_operation;
}
if(get_yes_no("Do you want to add bad sectors from manufacturer's list")) {
operation_string = relocate_string;
return relocate_operation;
}
printf("\nAn operation must be specified!\n\n");
}
}
/*
** Get_number_of_patterns reads and validates and sets the number of
** patterns to use during a verification operation. If no string was typed
** then the number of patterns defaults to four.
*/
get_number_of_patterns()
{
char count_string[10];
for(;;) {
count_string[0] = (char)0;
printf("Number of patterns to use when verifying? [1-16]: (16) ");
gets(count_string);
if(count_string[0]) {
sscanf(count_string, "%d", &verify_count);
if ((verify_count >= 1) && (verify_count <= 16))
break;
printf("\nPattern count must be between 1 and 16.\n\n");
}
else {
printf("\nDefaulting to 16 patterns.\n\n");
verify_count = 16;
break;
}
}
}
/*
** This routine ask the user the controller number, unit number,
** and the drive type of the drive to be formatted. When all information is
** gathered and correct, then, the open() system routine is called to initialize
** the controller and spin up drives if necessary. If the open fails the whole
** process is started over again.
*/
get_disk_parameters()
{
register int at_least_one_drive_specified = 0;
register int controller, drive, type;
for(controller = 0; controller < MAXCONTROLLER; controller++) {
max_drive = (ctlr_type[controller] == SMDCTLR) ? 4 : 16;
for(drive = 0; drive < max_drive; drive++)
drives_to_do[controller][drive] = -1;
}
printf("\nEnter controller number and drive number for each disk.\n");
printf("Key <RETURN> to controller number prompt when through.\n");
for(;;) {
if((controller = get_controller_number()) != -1) {
drive = get_drive_number(controller);
type = get_drive_type(controller);
printf("Is an '%s' drive on controller %d, unit %d ok",
vdst[type].type_name, controller, drive);
if(get_yes_no("")) {
drives_to_do[controller][drive] = type;
at_least_one_drive_specified = 1;
}
else
printf("\nEntry ignored.\n");
continue;
}
if(at_least_one_drive_specified)
break;
printf("You must specify at least one controller number!\n");
}
}
/*
** Get_controller_number reads, validates and returns a controller number.
*/
get_controller_number()
{
char controller[20];
int controller_number;
int i, controller_printed = FALSE;
for(;;) {
printf("\nController number? [");
for(i=0; i<MAXCONTROLLER; i++)
if(ctlr_type[i] != UNKNOWN) {
if(controller_printed)
printf(", ");
controller_printed = TRUE;
printf("%d", i);
}
printf("]: ");
gets(controller);
if(!controller[0])
return -1;
if(!sscanf(controller, "%d", &controller_number)) {
printf("\nResponse must start with a digit!\n");
continue;
}
if ((controller_number >= 0) && (controller_number <= 3))
if(ctlr_type[controller_number] != UNKNOWN)
break;
printf("\nController number %d is illegal!\n",
controller_number);
}
return controller_number;
}
/*
** Get_drive_number reads, validates and returns a drive number.
*/
get_drive_number(controller)
int controller;
{
char drive_string[20];
int drive, max_drive;
for(;;) {
max_drive = (ctlr_type[controller] == SMDCTLR) ? 3 : 15;
printf("Unit number? [0-%d]: ", max_drive);
gets(drive_string);
if(!drive_string[0]) {
printf("\nA drive number must be specified!\n\n");
continue;
}
if(!sscanf(drive_string, "%d", &drive)) {
printf("\nResponse must start with a digit!\n\n");
continue;
}
if((drive >= 0) && (drive <= max_drive))
break;
printf("\nDrive number %d is illegal!\n\n", drive);
}
return drive;
}
/*
** Do_operation executes the function wanted on all drive in the to_to
** list.
*/
do_operation(controller, drive, operation)
int controller, drive;
int (*operation)();
{
int status;
printf("\n%s %s on controller %d, drive %d, started. %s\n\n",
stars, operation_string, controller, drive, stars);
if(!(status = _setjmp(environ))) {
drive_name = "unknown";
init_environment_for_operation(controller, drive);
(*operation)();
printf("\n%s %s on controller %d, drive %d completed ok. %s\n",
stars, operation_string, controller, drive, stars);
return;
}
printf("\n%s %s on controller %d, drive %d ABORTED! %s\n",
stars, operation_string, controller, drive, stars);
return;
}
/*
** Init_environment_for_operation reset all the variables used by the
** system to suit the new drive type.
*/
init_environment_for_operation(controller, drive)
int controller, drive;
{
register int index;
ctlr_num = controller;
controller_address = (cdr *)(vddcaddr[controller]+IOBASE);
unit_number = drive;
drive_type = drives_to_do[controller][drive];
drive_name = vdst[drive_type].type_name;
max_cyl = vdst[drive_type].ncyl;
max_trk = vdst[drive_type].ntrak;
max_sec = vdst[drive_type].nsec;
sector_size = vdst[drive_type].secsize;
num_slip = vdst[drive_type].nslip;
bad_secs = 0;
/* Init bad block pattern array */
for(index=0; index<TRKSIZ; index++) {
pattern_0[index] = vdst[drive_type].fmt_pat[0];
pattern_1[index] = vdst[drive_type].fmt_pat[1];
pattern_2[index] = vdst[drive_type].fmt_pat[2];
pattern_3[index] = vdst[drive_type].fmt_pat[3];
pattern_4[index] = vdst[drive_type].fmt_pat[4];
pattern_5[index] = vdst[drive_type].fmt_pat[5];
pattern_6[index] = vdst[drive_type].fmt_pat[6];
pattern_7[index] = vdst[drive_type].fmt_pat[7];
pattern_8[index] = vdst[drive_type].fmt_pat[8];
pattern_9[index] = vdst[drive_type].fmt_pat[9];
pattern_10[index] = vdst[drive_type].fmt_pat[10];
pattern_11[index] = vdst[drive_type].fmt_pat[11];
pattern_12[index] = vdst[drive_type].fmt_pat[12];
pattern_13[index] = vdst[drive_type].fmt_pat[13];
pattern_14[index] = vdst[drive_type].fmt_pat[14];
pattern_15[index] = vdst[drive_type].fmt_pat[15];
}
spin_up_drive();
}
/*
** Spin_up_drive starts the drives on a controller and waits around for
** the drive to spin up if it is not already spinning.
*/
spin_up_drive()
{
VDDC_RESET(controller_address, ctlr_type[ctlr_num]);
if(ctlr_type[ctlr_num] == SMD_ECTLR) {
((cdr *)controller_address)->cdr_csr = 0;
((cdr *)controller_address)->mdcb_tcf = AM_ENPDA;
((cdr *)controller_address)->dcb_tcf = AM_ENPDA;
((cdr *)controller_address)->trail_tcf = AM_ENPDA;
((cdr *)controller_address)->data_tcf = AM_ENPDA;
((cdr *)controller_address)->cdr_ccf = CCF_STS | XMD_32BIT |
BSZ_16WRD | CCF_ERR | CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE;
}
access_with_no_trailer(INIT, 10);
access_with_no_trailer(DIAG, 20);
configure_drive(0);
}
/*
** Access_with_no_trailer is used to perform controller functions which
** require no data movement.
*/
access_with_no_trailer(function, wait_time)
int function, wait_time;
{
dcb.opcode = function; /* command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (function == VDSTART) ? 0 : (char)unit_number;
dcb.trailcnt = (char)0;
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
POLLTILLDONE(controller_address, &dcb, wait_time, ctlr_type[ctlr_num]);
if(vdtimeout <= 0) {
printf(" during startup operation.\n");
_longjmp(environ, 1);
}
return dcb.operrsta;
}
/*
** Configure_drive tells the controller what kind of drive is attached
** on a particular line.
*/
configure_drive(pass)
int pass;
{
dcb.opcode = RSTCFG; /* command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)unit_number;
dcb.trail.rstrail.ncyl = max_cyl;
dcb.trail.rstrail.nsurfaces = max_trk;
if(ctlr_type[ctlr_num] == SMDCTLR)
dcb.trailcnt = (char)2;
else {
dcb.trailcnt = (char)4;
dcb.trail.rstrail.nsectors = max_sec;
dcb.trail.rstrail.slip_sec = num_slip;
}
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
POLLTILLDONE(controller_address, &dcb, 5, ctlr_type[ctlr_num]);
if(vdtimeout <= 0) {
printf(" during drive configuration.\n");
_longjmp(environ, 1);
}
if(dcb.operrsta & (NOTCYLERR | DRVNRDY)) {
if(pass) {
printf("\nDrive failed to start!\n\n");
_longjmp(environ, -1);
}
access_with_no_trailer(VDSTART, (unit_number*6)+62);
DELAY((unit_number * 5500000) + 62000000);
configure_drive(1);
}
}
/*
** Get_drive_type reads a drive type, validates it and returns the
** validated drive type number to the calling routine.
*/
get_drive_type(controller)
register int controller;
{
char disk[10];
for(;;) {
printf("Drive type? [fsd/smd/xfd");
if(ctlr_type[controller] == SMD_ECTLR)
printf("/fuj/xsd");
printf("]: ");
gets(disk);
if (!strcmp(disk,"fsd"))
return FSD;
if (!strcmp(disk,"smd"))
return SMD;
if (!strcmp(disk,"xfd"))
return XFD;
if(ctlr_type[controller] == SMD_ECTLR) {
if (!strcmp(disk,"fuj"))
return FUJ;
if (!strcmp(disk,"xsd"))
return XSD;
}
printf("Illegal drive type '%s'\n", disk);
}
}
/*
** Format_operation is the main routine for formatting a disk.
** Take note the the first operation must be format_data_area due to a micro-
** code bug in the controller. While formatting sectors one sector count is
** lost every 64k sectors, so, on an fsd drive the last four sectors will
** not be formatted. This is overcome by formatting the entire disk first, then
** formatting the maintenance area, which happens to cover the last n sectors
** that were lost.
*/
format_operation()
{
/* Format relocation area and data area */
format_data_area();
/* Verify the reloaction area and data area */
verify_data_area();
if(bad_secs)
relocate_bad_blocks();
/* Format maintenance cylinders for field service */
lay_maint_cylinders();
}
/*
** Relocate_operation() prompts the user for bad block numbers and adds
** to the current bad block table. This process continues until a null response
** is typed in when prompted for a bad block. The user is asked to verify that
** the block number typed in is really the block that they wanted to relocate
** before the tables are updated.
*/
relocate_operation()
{
long old_blk;
char nxt_blk[20];
dskadr daddr;
long last_block = max_cyl * max_trk * max_sec;
bad_secs = 0;
printf("Just key <RETURN> when through entering sector numbers.\n");
while(bad_secs < MAXERR) {
printf("Sector number? ");
gets(nxt_blk);
if(!nxt_blk[0])
break;
sscanf(nxt_blk, "%ld", &old_blk);
if(old_blk >= last_block) {
printf("\nBlock number must be between 0 and %d!\n\n",
last_block - 1);
continue;
}
printf("Relocate sector #%ld", old_blk);
if(get_yes_no("")) {
daddr.sector = old_blk % max_sec;
daddr.track = (old_blk / max_sec) % max_trk;
daddr.cylinder = (old_blk / max_sec) / max_trk;
flag_sector(&daddr, 0);
}
else
printf("\nEntry ignored.\n");
printf("\n");
}
relocate_bad_blocks();
}
/*
**
*/
scan_operation()
{
register int status, j=0;
dskadr daddr;
register int old_verify_count = verify_count;
bad_secs = 0;
daddr.sector = 0;
for(daddr.cylinder=0;daddr.cylinder<(max_cyl-NUMSYS);daddr.cylinder++) {
if(j++ % 50) printf(".");
else printf("\n Scanning cylinder %d.", (int)daddr.cylinder);
for(daddr.track = 0; daddr.track < max_trk; daddr.track++) {
status = access(save, &daddr , FTR, 1, 1);
if(status & (HRDERR | SFTERR))
verify_count = 16;
verify_track(&daddr);
status = access(save, &daddr , FTW, 1, 1);
verify_count = old_verify_count;
}
}
printf("\n");
print_bad_sector_list();
print_number_of_bad_sectors("file system");
if(bad_secs)
relocate_bad_blocks();
}
/*
** This routine formats the user data area and bad block area of the disk.
*/
format_data_area()
{
register long sector_count;
dskadr zero;
register int index;
printf("Formatting media...\n");
zero.cylinder = (short)0;
zero.track = (char)0;
zero.sector = (char)0;
sector_count = (long)(max_cyl * max_trk * max_sec);
format_sectors(&zero, &zero, NRM, sector_count);
printf("Formatting complete.\n");
}
/*
** This routine creates a bad block relocation area on disk and then
** certifies the user data area of the disk.
*/
verify_data_area()
{
printf("Scanning for bad sectors...\n");
init_relocation_area();
verify_user_data_area();
printf("\nBad block scan complete.\n");
}
/*
** Vdinit_relocation_area certifies the bad block relocation area and flags
** each bad block in the area as bad for later use by load_free_table(). No
** relocation takes place.
*/
init_relocation_area()
{
printf(" Verifying bad sector relocation area...\n");
verify_cylinders((int)max_cyl - NUMSYS, NUMREL);
print_number_of_bad_sectors("relocation");
if(bad_secs)
mark_sectors_as_bad();
printf(" Relocation area verified.\n");
}
/*
** This routine certifies the area to be used for user data.
*/
verify_user_data_area()
{
int temp;
printf(" Verifying file system area...\n");
verify_cylinders(0, (int)(max_cyl-NUMSYS));
print_number_of_bad_sectors("file system");
printf(" File system area verified.\n");
}
/*
** Vdlay_maint_cylinders creates the maintenance cylinders for field
** service techs to run diagnostics on.
*/
lay_maint_cylinders()
{
dskadr daddr;
register long *pattern_ptr = (long *)&daddr;
register int index;
printf("Creating maintenance cylinders...\n");
/* format read only cylinder */
daddr.cylinder = (short)(max_cyl - NUMMNT);
daddr.track = daddr.sector = (char)0;
format_sectors(&daddr, &daddr, NRM, (long)(NUMMNT * max_trk * max_sec));
printf(" Verifying maintenance area...\n");
verify_cylinders(daddr.cylinder, NUMMNT);
print_number_of_bad_sectors("maintenance");
printf(" Maintenance area verified.\n");
printf(" Writing Read / Write patterns.\n");
daddr.cylinder = (short)(max_cyl - NUMMNT);
for(daddr.track=0; daddr.track<max_trk; daddr.track++)
for(daddr.sector=0; daddr.sector<max_sec; daddr.sector++)
if(is_bad(&daddr)) {
extern rel_type new_location();
rel_type type;
dskadr naddr;
type = new_location(&daddr, &naddr,
dsk_err[index].err_stat);
relocate(&daddr, &naddr, type);
}
printf(" Writing Read only patterns.\n");
for(daddr.cylinder++; daddr.cylinder<max_cyl; daddr.cylinder++) {
for(daddr.track=0; daddr.track<max_trk; daddr.track++)
for(daddr.sector = 0; daddr.sector < max_sec;
daddr.sector++) {
for(index=0; index<(sector_size/sizeof(long)); index++)
scratch[index] = *pattern_ptr;
if(is_bad(&daddr))
mark_bad_sector(&daddr, (short)VDWPT);
else
format_sectors(&daddr,&daddr,WPT,(long)1);
}
}
printf("Maintenance cylinders complete.\n");
}
/*
** Is_bad checks to see if a block is known to be bad already.
*/
is_bad(daddr)
dskadr *daddr;
{
register int index;
for(index=0; index<bad_secs; index++)
if(daddr->cylinder == dsk_err[index].err_adr.cylinder)
if(daddr->track == dsk_err[index].err_adr.track)
if(daddr->sector==dsk_err[index].err_adr.sector)
return 1;
return 0;
}
/*
** verify_cylinders does full track certification for every track
** on the cylinder. This is done for speed and minimal head movement. If
** an error occurs on any single track the track is flagged for later
** verification by verify sectors.
*/
verify_cylinders(base_cyl, cyl_count)
int base_cyl, cyl_count;
{
register int i, j = 0;
dskadr daddr;
bad_secs = 0;
/* verify each track of each cylinder */
for (daddr.cylinder=base_cyl; daddr.cylinder<(base_cyl+cyl_count);
daddr.cylinder++) {
if(j++ % 50) printf(".");
else printf("\n Scanning cylinder %d.", (int)daddr.cylinder);
for (daddr.track = 0; daddr.track < max_trk; daddr.track++) {
verify_track(&daddr);
}
}
printf("\n");
print_bad_sector_list();
}
/*
** verify_track verifies a single track. If the full track write and
** compare operation fails then each sector is read individually to determin
** which sectors are really bad. If a sector is bad it is flagged as bad by
** the verify sector routine.
*/
verify_track(daddr)
dskadr *daddr;
{
register int index, i;
register int count;
register long before;
register long *after;
register int status;
register long offset = sector_size / sizeof(long);
int pattern_count = verify_count;
daddr->sector = (char)0;
access(pattern_address[0], daddr, FTW, 1, 1);
for(index = 0; index < pattern_count; index++) {
data_ok(dcb.operrsta);
if(dcb.operrsta & HEADER_ERROR) {
if(ctlr_type[ctlr_num]==SMDCTLR)
goto hard;
flag_sector(daddr, dcb.operrsta);
break;
}
if(status & DATA_ERROR)
pattern_count = 16;
status = access(scratch, daddr, FTR, 1, 1);
if(status & HEADER_ERROR) {
if(ctlr_type[ctlr_num]==SMDCTLR)
goto hard;
flag_sector(daddr, status);
break;
}
if(!data_ok(status)) {
pattern_count = 16;
for(i = 0; i < max_sec; i++) {
register long *next;
daddr->sector = i;
next = &scratch[i * offset];
status = access(next, daddr, RD, 1, 1);
if(!data_ok(status))
flag_sector(daddr, status);
}
daddr->sector = (char)0;
}
if(index+1 < pattern_count)
access(pattern_address[index+1], daddr, FTW, 1, 0);
count = max_sec * offset;
before = *pattern_address[index];
after = scratch;
mtpr(0, PADC);
for(i=0; i<count; i++) {
if(before != *(after++)) {
daddr->sector = i / offset;
flag_sector(daddr, 0);
}
}
POLLTILLDONE(controller_address, &dcb, 60, ctlr_type[ctlr_num]);
if(vdtimeout <= 0) {
printf(" in verify_track.\n");
_longjmp(environ, 1);
}
}
return;
hard: printf("\nSMD Controllers can't recover from header errors!");
printf(" Status = 0x%x\n", dcb.operrsta);
_longjmp(environ, dcb.operrsta);
}
/*
** Vdflag_sector makes an entry into the bad block table for the current
** bad sector.
*/
flag_sector(daddr, status)
dskadr *daddr;
long status;
{
register int index;
if(bad_secs < MAXERR) {
for(index=0; index<bad_secs; index++)
if((dsk_err[index].err_adr.cylinder==daddr->cylinder) &&
(dsk_err[index].err_adr.track == daddr->track) &&
(dsk_err[index].err_adr.sector==daddr->sector))
return;
dsk_err[bad_secs].err_adr.cylinder = daddr->cylinder;
dsk_err[bad_secs].err_adr.track = daddr->track;
dsk_err[bad_secs].err_adr.sector = daddr->sector;
dsk_err[bad_secs++].err_stat = status;
return;
}
printf("Maximum number of %d bad tracks exceeded!\n", MAXERR);
_longjmp(environ, MAXERR);
}
/*
** Print_bad_sector list tells the user which sectors are bad.
*/
print_bad_sector_list()
{
register int index;
register int sec;
dskadr daddr;
if(bad_secs) {
printf(" The following sector");
if(bad_secs == 1)
printf(" is");
else
printf("s are");
printf(" bad:\n");
for(index=0; index<bad_secs; index++) {
sec = dsk_err[index].err_adr.cylinder * max_trk;
sec += dsk_err[index].err_adr.track;
sec *= max_sec;
sec += dsk_err[index].err_adr.sector;
printf(" %ld\n", sec);
}
}
}
/*
** Print_number_of_bad_sectors is used to announce to the user the
** total number of bad sectors in a particular segment of the disk. Rules
** of english for making a word plural are used to make a readable sentence.
*/
print_number_of_bad_sectors(str)
char *str;
{
if(bad_secs)
printf("\n %d", bad_secs);
else
printf("\n No");
printf(" bad sector");
if(bad_secs != 1)
printf("s");
printf(" found in %s area.\n", str);
}
/*
** Vdmark_sectors_as_bad marks every block in the current bad block
** table as bad on the disk.
*/
mark_sectors_as_bad()
{
register int index;
dskadr daddr;
for(index=0; index<bad_secs; index++) {
daddr.cylinder = dsk_err[index].err_adr.cylinder;
daddr.track = dsk_err[index].err_adr.track;
daddr.sector = dsk_err[index].err_adr.sector;
mark_bad_sector(&daddr, (short)0);
}
}
/*
** mark_bad_sector marks a single sector, on disk, as bad using the bad
** block flags in the sector header.
*/
mark_bad_sector(daddr, flags)
dskadr *daddr;
short flags;
{
format_sectors(daddr, daddr, BAD | flags, (long)1);
}
/*
** relocate_bad_blocks scans the current disk error table and relocates
** every block that is flagged as bad in the table. At the beginning of the
** routine the operator is given the chance to add blocks of his/her own to
** the table.
*/
relocate_bad_blocks()
{
extern rel_type new_location();
register int index;
dskadr daddr, naddr;
rel_type type;
printf("Relocating bad sectors...\n");
load_free_table();
for(index=0; index<bad_secs; index++) {
daddr.cylinder = dsk_err[index].err_adr.cylinder;
daddr.track = dsk_err[index].err_adr.track;
daddr.sector = dsk_err[index].err_adr.sector;
type = new_location(&daddr, &naddr, dsk_err[index].err_stat);
relocate(&daddr, &naddr, type);
}
printf("Relocation complete.\n");
}
/*
** Vdrelocate commands the controller to relocate a block from daddr
** to naddr. Once this has been done the controller will automatically perform
** relocation whenever data is transfered to or from daddr.
*/
relocate(daddr, naddr, type)
dskadr *daddr, *naddr;
rel_type type;
{
if(type == FULL_TRACK)
relocate_track(daddr, naddr);
else
relocate_sector(daddr, naddr);
}
/*
**
*/
relocate_sector(daddr, naddr)
dskadr *daddr, *naddr;
{
dskadr phys, reloc;
register long blk;
register long status;
access(save, daddr, RD, 1, 1);
phys.cylinder = daddr->cylinder;
phys.track = daddr->track;
phys.sector = daddr-> sector;
reloc.cylinder = naddr->cylinder;
reloc.track = naddr->track;
reloc.sector = naddr->sector;
format_sectors(&phys, &reloc, RELOC_SECTOR, (long)1);
phys.cylinder = naddr->cylinder;
phys.track = naddr->track;
phys.sector = naddr->sector;
reloc.cylinder = daddr->cylinder;
reloc.track = daddr->track;
reloc.sector = daddr->sector;
format_sectors(&phys, &reloc, ALT_SECTOR, (long)1);
blk = (daddr->cylinder * max_trk) + daddr->track;
blk = (blk * max_sec) + daddr->sector;
printf(" Sector #%ld ", blk);
status = access(save, daddr, WD, 1, 1);
if((status & ALTACC) && !(status & HRDERR)) {
blk = (naddr->cylinder * max_trk) + naddr->track;
blk = (blk * max_sec) + naddr->sector;
printf("relocated to sector #%d.\n", blk);
return;
}
printf("was not relocated successfully! Status = %lx\n", status);
}
/*
**
*/
relocate_track(daddr, naddr)
dskadr *daddr, *naddr;
{
dskadr phys, reloc;
register long blk;
register long status;
access(save, daddr, FTR, 1, 1);
phys.cylinder = daddr->cylinder;
phys.track = daddr->track;
phys.sector = 0;
reloc.cylinder = naddr->cylinder;
reloc.track = naddr->track;
reloc.sector = 0xff;
format_sectors(&phys, &reloc, RELOC_SECTOR, max_sec);
phys.cylinder = naddr->cylinder;
phys.track = naddr->track;
phys.sector = 0;
reloc.cylinder = daddr->cylinder;
reloc.track = daddr->track;
reloc.sector = 0;
format_sectors(&phys, &reloc, ALT_SECTOR, max_sec);
blk = (daddr->cylinder * max_trk) + daddr->track;
printf(" Track #%ld ", blk);
status = access(save, daddr, FTW, 1, 1);
if((status & ALTACC) && !(status & HRDERR)) {
blk = (naddr->cylinder * max_trk) + naddr->track;
printf("relocated to track #%d.\n", blk);
return;
}
printf("was not relocated successfully! Status = %lx\n", status);
}
/*
** access is used by other routines to do reads and writes to the disk.
** The status of the read / write is returned to the caller for processing.
*/
access(buf, daddr, func, count, wait)
char *buf;
dskadr *daddr;
int func, count, wait;
{
dcb.opcode = func; /* format sector command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)unit_number;
dcb.trailcnt = (char)(sizeof(trrw) / 4);
dcb.trail.rwtrail.memadr = buf;
dcb.trail.rwtrail.wcount = count * (sector_size / sizeof(short));
dcb.trail.rwtrail.disk.cylinder = daddr->cylinder;
dcb.trail.rwtrail.disk.track = daddr->track;
dcb.trail.rwtrail.disk.sector = daddr->sector;
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
if(wait) {
POLLTILLDONE(controller_address,&dcb,2*60,ctlr_type[ctlr_num]);
if(vdtimeout <= 0) {
printf(" in access.\n");
_longjmp(environ, 1);
}
}
return dcb.operrsta;
}
/*
** Vdformat_sectors is used to do the actual formatting of a block.
*/
format_sectors(daddr, haddr, flags, count)
dskadr *daddr, *haddr;
short flags;
long count;
{
dcb.opcode = FSECT; /* format sector command */
dcb.intflg = NOINT;
dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)unit_number;
dcb.trailcnt = (char)(sizeof(trfmt) / 4);
dcb.trail.fmtrail.addr = (char *)scratch;
dcb.trail.fmtrail.nsectors = count;
dcb.trail.fmtrail.disk.cylinder = daddr->cylinder | flags;
dcb.trail.fmtrail.disk.track = daddr->track;
dcb.trail.fmtrail.disk.sector = daddr->sector;
dcb.trail.fmtrail.hdr.cylinder = haddr->cylinder | flags;
dcb.trail.fmtrail.hdr.track = haddr->track;
dcb.trail.fmtrail.hdr.sector = haddr->sector;
mdcb.firstdcb = &dcb;
mdcb.vddcstat = 0;
VDDC_ATTENTION(controller_address, &mdcb, ctlr_type[ctlr_num]);
POLLTILLDONE(controller_address, &dcb,
((count+849)/850)+120, ctlr_type[ctlr_num]);
if(vdtimeout <= 0) {
printf(" in format_sectors.\n");
_longjmp(environ, 1);
}
if (!data_ok(dcb.operrsta)) {
printf("Data error during format operation!\n");
_longjmp(environ, dcb.operrsta);
}
}
/*
** Print_dcb() dumps the MDCB and DCB for diagnostic purposes. This
** routine is called whenever a fatal error is encountered.
*/
printdcb(ptr)
register long *ptr;
{
register long i;
register long trailer_count;
printf("Dump of MDCB: ");
for(i=0; i<4; i++)
printf(" %lx", *(ptr+i));
if(ptr = (long *)*ptr) {
printf(" and DCB:");
trailer_count = *(ptr+3) & 0xff;
for(i=0; i<7+trailer_count; i++) {
uncache(ptr+i);
printf(" %lx", *(ptr+i));
}
}
printf("\n");
for(i=0; i<5000000; i++) ;
}
/*
** Vdload_free_table checks each block in the bad block relocation area
** to see if it is used. If it is, the free relocation block table is updated.
*/
load_free_table()
{
dskadr daddr;
register int i, j;
/* Clear free table before starting */
for(i = 0; i < (max_trk * NUMREL); i++)
for(j = 0; j < max_sec; j++) {
free[i][j].freestatus = NOTALLOCATED;
free[i][j].error = (long)0;
}
/* For each relocation cylinder */
for(daddr.cylinder = max_cyl-NUMSYS;
daddr.cylinder < (max_cyl-NUMMNT); daddr.cylinder++)
/* For each track on a cylinder */
for(daddr.track = 0; daddr.track < max_trk; daddr.track++)
/* For each sector on a track */
for(daddr.sector=0;daddr.sector<max_sec;daddr.sector++)
if(block_should_be_allocated(&daddr))
allocate(&daddr, (long)0);
}
/*
** block_should_be_allocated does a read header and data command into
** a local buffer. The header address is then checked to see if the block
** has the relocation bit set or if the block was marked as bad. (either VDMF
** or VDUF or both bits set). In addition, if data data error is picked up
** during the read then the block should be allocated. If the block meets
** the above criteria then
*/
block_should_be_allocated(daddr)
dskadr *daddr;
{
/* return false if already replaced or either VDMF or VDUF is set */
return (access(scratch, daddr, RD, 1, 1) & (HRDERR|SFTERR|ALTACC));
}
/*
** allocate marks a replacement sector as used.
*/
allocate(daddr, status)
dskadr *daddr;
long status;
{
register long trk;
trk = daddr->cylinder - (max_cyl - NUMSYS);
trk *= max_trk;
trk += daddr->track;
free[trk][daddr->sector].freestatus = ALLOCATED;
free[trk][daddr->sector].error = status;
}
/*
** Vdnew_location allocates a replacement block given a bad block address.
** The algorithm is fairly simple; it simply searches for the first
** free sector that has the same sector number of the bad sector. If no sector
** is found then the drive should be considered bad because of a microcode bug
** in the controller that forces us to use the same sector number as the bad
** sector for relocation purposes. Using different tracks and cylinders is ok
** of course.
*/
rel_type new_location(daddr, naddr, status)
dskadr *daddr, *naddr;
long status;
{
register int index, sec;
if(status & (HEADER_ERROR)) {
for(index = 0; index < (max_trk * NUMREL); index++) {
for(sec=0; sec < max_sec; sec++) {
if(free[index][sec].freestatus == ALLOCATED)
break;
}
if(sec == max_sec) {
for(sec = 0; sec < max_sec; sec++) {
free[index][sec].freestatus = ALLOCATED;
free[index][sec].error = status;
}
naddr->cylinder=index/max_trk+(max_cyl-NUMSYS);
naddr->track = index % max_trk;
naddr->sector = 0;
return FULL_TRACK;
}
}
}
for(index = 0; index < (max_trk * NUMREL); index++)
if(free[index][daddr->sector].freestatus != ALLOCATED) {
free[index][daddr->sector].freestatus = ALLOCATED;
free[index][daddr->sector].error = status;
naddr->cylinder = index / max_trk + (max_cyl - NUMSYS);
naddr->track = index % max_trk;
naddr->sector = daddr->sector;
return SINGLE_SECTOR;
}
printf("Bad sector relocation area is full!");
_longjmp(environ, daddr->sector);
}
/*
** data_ok checks an error status word for bit patterns
** associated with error conditions from the VDDC controller. If a hardware
** error is present then the problem is reported on the console and the program
** is halted. If a data error is present the a zero is returned.
** If everything is OK then a 1 is returned.
*/
data_ok(status)
long status;
{
if(status & HARD_ERROR){
if(status & DRVNRDY)
printf("\nDrive is not ready!");
else if(status & INVDADR)
printf("\nInvalid disk address issued!");
else if(status & DNEMEM)
printf("\nNon-existent memory error!");
else if(status & PARERR)
printf("\nMain memory parity error!");
else if(status & OPABRT)
printf("\nCPU aborted operation!");
else if(status & WPTERR)
printf("\nDrive is write protected!");
else if(status & DSEEKERR)
printf("\nDisk seek error!");
else
printf("\nNot on cylinder error!");
printf(" Status = %lx\n", status);
_longjmp(environ, -1);
}
return (int)(!(status & DATA_ERROR));
}
/*
** Vdget_yes_no is used to ask simple yes or no questions. The question
** prompt is supplied by the caller, The question mark, possible responses,
** and the default response is printed at the end of the prompt. The routine
** then reads the answer and returns a 1 if a 'y' is typed or no response was
** given, otherwise, a zero is returned.
*/
get_yes_no(str)
register char *str;
{
char answer[80];
for(;;) {
printf("%s? [y/n] (n): ", str);
gets(answer);
if((answer[0] == 'Y') || (answer[0] == 'y'))
return(TRUE);
if((answer[0] == 'N') || (answer[0] == 'n'))
return(FALSE);
printf("\nA 'Y' (yes) or 'N' (no) must be typed!\n\n");
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.