/* hard.c winchester formatter and diagnostic for Miniframe. Tom Trebisky ttrebisky@as.arizona.edu tjt 6/4/90 - wrote floppy.c tjt 12/22/90 - converted floppy.c to hard.c tjt 1/8/91 - Version 1.0 operational and useful tjt 2/2/91 - Version 1.1 add spiral skewing tjt 2/19/91 - Version 1.2 add disk parameter table tjt 2/24/91 - Version 1.3 split out menus, bad block handling tjt 2/26/91 - Version 1.4 write boot image to disk */ #ifdef NEVER /* these options are NOT on. */ #define DEBUG #else /* these options ARE on. */ #endif typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned long u_long; #include "dkbad.h" #include "volhdr.h" /* Disk DMA controller registers */ #define DISK_DMA_COUNT (short *) 0xc80000 /* r/w word count */ #define DISK_DMA_LADDR (short *) 0xc80002 /* wo low 16 bits */ #define DISK_DMA_UADDR_W (short *) 0xc80006 /* wo upper 5 bits */ #define DISK_DMA_UADDR_R (short *) 0xc80008 /* wo upper 5 bits */ /* Now here is a strange place to hide a bit. * In the slow communications status register, we have a bit * that goes low whenever a floppy drive is physically connected * to the the 34 pin connector, whether it is powered up, ready, or whatever. */ #define SC_STAT (short *) 0xc30008 #define SC_FDNP 0x01 /* Disk Bus Interface Unit registers */ #define FD_RESET_ON (short *) 0xc60020 #define FD_RESET_OFF (short *) 0xc60022 #define HD_RESET_ON (short *) 0xc60024 #define HD_RESET_OFF (short *) 0xc60026 #define FD_MOTOR_ON (short *) 0xc60028 #define FD_MOTOR_OFF (short *) 0xc6002A #define HD_DMA_ENABLE (short *) 0xc6002C #define FD_DMA_ENABLE (short *) 0xc6002E #define DISK_DMA_DISABLE (short *) 0xc60030 #define FD_SINGLE (short *) 0xc60032 #define FD_DOUBLE (short *) 0xc60034 #define DISK_BIU_RESET (short *) 0xc60036 /* the WD1010 HDC chip */ #define HD_EFLAGS (short *) 0xc60002 #define HD_WPC (short *) 0xc60002 #define HD_SCOUNT (short *) 0xc60004 #define HD_SNUM (short *) 0xc60006 #define HD_CYLOW (short *) 0xc60008 #define HD_CYHIGH (short *) 0xc6000A #define HD_SDH (short *) 0xc6000C #define HD_STATUS (short *) 0xc6000E #define HD_COMMAND (short *) 0xc6000E /* here are the 6 WD1010 comands */ #define HDC_RESTORE 0x10 #define HDC_SEEK 0x70 #define HDC_READ 0x20 #define HDC_WRITE 0x30 #define HDC_SCANID 0x40 #define HDC_FORMAT 0x50 #define HDC_IEOC 0x08 /* interrupt at end of cmd */ #define HDC_MULT 0x04 /* multi-sector read/write */ /* here are bits in the WD1010 status register */ #define HDS_BUSY 0x80 #define HDS_DRDY 0x40 #define HDS_WF 0x20 #define HDS_SC 0x10 /* seek complete */ #define HDS_DRQ 0x08 #define HDS_CIP 0x02 /* command in progress */ #define HDS_ERR 0x01 #define HDS_MASK (HDS_BUSY|HDS_WF|HDS_CIP|HDS_ERR) /* 0xA3 */ /* here are bits in the WD1010 error register */ #define HDE_BBLK 0x80 #define HDE_CRC 0x40 #define HDE_IDNF 0x10 #define HDE_ABORT 0x04 #define HDE_TK0 0x02 #define HDE_DAM 0x01 /* the WD2797 FDC chip */ #define FD_COMMAND (short *) 0xc60010 #define FD_STATUS (short *) 0xc60010 #define FD_TRACK (short *) 0xc60012 #define FD_SECTOR (short *) 0xc60014 #define FD_DATA (short *) 0xc60016 /* bits in the WD2797 status register */ #define FD_NREADY 0x80 #define FD_WPROT 0x40 #define FD_HLOADED 0x20 #define FD_SEEKERR 0x10 #define FD_CRCERR 0x08 #define FD_TRACK0 0x04 #define FD_INDEX 0x02 #define FD_BUSY 0x01 /* these bits change for type II and III commands */ #define FD_RTYPE 0x20 /* on read, 1 = deleted data */ #define FD_RNF 0x10 /* record not found */ #define FD_LDATA 0x04 /* lost data */ #define FD_DRQ 0x02 /* the 8259 interrupt controller chip */ #define PIC_A0 (short *) 0xc90000 #define PIC_A1 (short *) 0xc90002 /* here is the way we initialize the PIC */ #define ICW1 0x13 #define ICW2 0x0 #define ICW4 0x3 #define OCW1 0xff /* Here is the deal on the Miniframe interrupt setup - Level 7 (NMI) autovector: parity, page-fault, mmuerr, mnp Level 6 Timer autovector Level 5 8274 autovector Level 4 8259 normal vector Level 3 expansion 8259 normal vector (not for us) Level 2 expansion autovector (not for us) Level 1 printer autovector On the 8259, the following is connected - 0 = FCOMM, FCTXU status asserted 1 = FCOMM, FCRXSA status asserted 2 = FCOMM, FCCARRIER status asserted 3 = FCOMM, FCTC status asserted 4 = Fast timer 5 = Disk overflow, underrun 6 = Hard Disk EOT 7 = Floppy Disk EOT */ /* these codes are returned by hddone() */ #define HDD_EFLAGS 0x01 /* reserved for controller detected errors */ #define HDD_OVERRUN 0x02 /* (or underrun) */ #define HDD_TIMEOUT 0x04 /* we gave up waiting */ /* here is a structure to store information about bad sectors */ struct badblk { u_short b_cyl; u_char b_head; u_char b_sect; u_short b_flags; struct badblk *b_forw; struct badblk *b_back; }; #define NULBAD (struct badblk *) 0 /* why the sector was considered bad */ #define BF_WRITE 0x01 /* error writing to it */ #define BF_READ 0x02 /* error reading it */ #define BF_DATA 0x04 /* data read does not match what was written */ #define BF_HAND 0x08 /* selected by hand entry */ /* some housekeeping flags */ #define BF_ALLOC 0x10 /* replacement sector has been allocated */ #define BF_MARKED 0x20 /* original has been marked bad */ /* this means the data on disk is fully consistent */ #define BF_NEW 0x40 /* found this on latest test */ #define BF_MAPPED 0x80 /* has been marked bad and mapped to replacement */ struct dskadd { u_short n_cyl; u_short n_head; u_short n_sec; }; #define NDRIVES 2 /* miniframe only has 2 hard drives */ struct disktab { char *d_type; /* string naming disk type */ u_short d_secsize; u_short d_ntracks; u_short d_nsectors; u_short d_ncylinders; u_short d_precomp; u_short d_steprate; u_short d_skew; u_short d_nbad; struct badblk *d_bforw; struct badblk *d_bback; struct badblk *d_badfree; /* always points to free list pool */ struct badblk *d_nxtbad; /* next available free item */ u_short d_maxbad; struct dkbad *d_binfo; /* bad block table, as read from disk */ struct dskadd d_curblk; }; struct disktab diskinfo[NDRIVES]; struct dkbad diskbad[NDRIVES]; short drive; struct disktab *cd; #define READ 1 #define WRITE 2 /* temporary drive geometry stuff - eventually should be * replaced by a table/menu. */ char stname[] = "ST251-1"; #define NCYL 820 /* cylinders on a ST251-1 */ #define NHEADS 6 /* heads on a ST251-1 */ #define PRECOMP 900 /* ST251 does not require rwc */ #define STEPRATE 0 /* 0x0 = .35ms (full tilt) */ /* these things never really change, but we still put them in a table */ #define NSECTOR 17 /* sectors per track */ #define SSIZE 512 /* single sector */ /* ?? I wonder if we could format 9 * 1k sectors ?? */ #define MAXHEADS 8 /* max heads ever allowed */ #define MAXSEC 17 /* max sectors per track ever allowed */ #define TSIZE (SSIZE*NSECTOR) /* entire track */ char *iobuf; char *prompt(); long atol(); unsigned long atoh(); /* called before main - allows hardware initialization */ configure() { } extern char end; static char *nalloc = (char *) 0; /* really simple minded memory allocator. * - no check for running past end of memory. * - but it does force even address alignment. */ char * malloc(size) u_long size; { register char *rval; if ( nalloc == (char *) 0 ) nalloc = (char *) (((u_long) &end + 1) & ~1); rval = nalloc; nalloc = (char *) (((u_long) nalloc + size + 1) & ~1); return ( rval ); } static char uname[] = "userdisk"; /* interactive entry of disk parameters */ setparam() { cd->d_type = uname; cd->d_secsize = SSIZE; cd->d_ntracks = getparam("heads "); cd->d_nsectors = NSECTOR; cd->d_ncylinders = getparam("cylinders "); cd->d_precomp = getparam("precomp cylinder "); cd->d_steprate = getparam("step rate "); cd->d_skew = 1; cd->d_nbad = 0; cd->d_bforw = NULBAD; cd->d_bback = NULBAD; cd->d_nxtbad = cd->d_badfree; cd->d_maxbad = cd->d_ntracks * (cd->d_nsectors - 1 ); if ( cd->d_maxbad > MAXBAD ) cd->d_maxbad = MAXBAD; /* NOW should read in bad sector table from the disk */ } getparam(msg) char *msg; { char *cp; int val; cp = prompt( msg ); val = atol(cp); /* printf("%d\n",val); */ return ( val ); } gethparam(msg) char *msg; { char *cp; int val; cp = prompt( msg ); val = atoh(cp); return ( val ); } dkinit(dnum,dname) char *dname; { register struct disktab *dp; dp = &diskinfo[dnum]; dp->d_type = dname; dp->d_secsize = SSIZE; dp->d_ntracks = NHEADS; dp->d_nsectors = NSECTOR; dp->d_ncylinders = NCYL; dp->d_precomp = PRECOMP; dp->d_steprate = STEPRATE; dp->d_skew = 1; dp->d_nbad = 0; dp->d_bforw = NULBAD; dp->d_bback = NULBAD; dp->d_nxtbad = dp->d_badfree = (struct badblk *) malloc ( MAXBAD * sizeof ( struct badblk ) ); dp->d_maxbad = dp->d_ntracks * (dp->d_nsectors - 1 ); if ( dp->d_maxbad > MAXBAD ) dp->d_maxbad = MAXBAD; /* NOW should read in bad sector table from the disk */ dp->d_binfo = &diskbad[dnum]; } main() { int times; int cyl; int fill; char *cp; register i; printf("Type return when ready"); cp = prompt((char *) 0 ); /* wait for user */ printf("Winchester test (ver 1.4) starting\n"); /* must be biggest we will EVER need */ iobuf = malloc ( TSIZE ); for ( cp = iobuf; cp < &iobuf[SSIZE]; ) *cp++ = 0xae; /* current block for sector read/write */ cd->d_curblk.n_cyl = 0; cd->d_curblk.n_head = 0; cd->d_curblk.n_sec = 0; *HD_SDH = 0x18; /* impossible (select no drive) */ printf("io buffer is at: "); hex8(iobuf); putchar('\n'); picinit(); /* initialize drive info */ dkinit(0,stname); dkinit(1,stname); cd = &diskinfo[drive=0]; hdinit(drive); for ( ;; ) { printf("win[%d]: ",drive); cp = prompt((char *) 0 ); if ( *cp == '\0' ) { printf("ebits: %x\n",(*HD_EFLAGS)&0xff); printf("status: %x\n",(*HD_STATUS)&0xff); continue; } if ( *cp == 'q' ) /* q - quit */ break; else if ( *cp == 'i' ) /* i - initialize */ hdinit(drive); else if ( *cp == 'c' ) { /* c - change drive */ cp = prompt("drive (0,1)? "); drive = atol(cp) & 1; hdinit(drive); cd = &diskinfo[drive]; } else if ( *cp == 'p' ) /* p - set disk parameters */ setparam(); else if ( *cp == 'h' ) /* h - help */ hdhelp(); else if ( *cp == 't' ) /* t - test submenu */ tests(); else if ( *cp == 'b' ) /* b - bad block submenu */ bad(); else if ( *cp == 'm' ) /* m - mkboot */ mkboot(); else if ( *cp == 'f' ) /* f - format tracks */ hdformat(); else if ( *cp == 'n' ) { /* n - read next sector */ int code; incblk(&cd->d_curblk,cd->d_nsectors); if ( code = winio (iobuf,READ,cd->d_curblk.n_sec, cd->d_curblk.n_cyl,cd->d_curblk.n_head) ) { printf("io error: %x status = %x flags = %x\n", code>>8, *HD_STATUS&0xff,*HD_EFLAGS&0xff); } else { printf("sector %d, track %d, cylinder %d\n", cd->d_curblk.n_sec,cd->d_curblk.n_head,cd->d_curblk.n_cyl); vxdmp(iobuf,SSIZE); } } else if ( *cp == 'r' ) /* r - read sector */ sectorio(READ); else if ( *cp == 'w' ) /* w - write sector */ sectorio(WRITE); else if ( *cp == 'd' ) /* d - dump sector buffer */ vxdmp(iobuf,SSIZE); else if ( *cp == 'e' ) { /* b - fill sector buffer */ cp = prompt("fill value (0x00)? "); if ( *cp == '\0' ) fill = 0; else fill = atoh(cp)&0xff; for ( i=0; id_ncylinders-1); cp = prompt( (char *) 0 ); cyl = atol(cp); if ( cyl < 0 ) cyl = 0; if ( cyl > NCYL-1 ) cyl = cd->d_ncylinders-1; hdseek(cyl); } else { printf(" ?\n"); } } /* re-enter the gdb nub */ (*((int (*) ()) 0x6c000) ) (); /* return should exit to boot */ } hdhelp() { printf("i - initialize (restore) drive\n"); printf("c - change (select) drive\n"); printf("p - change (set) drive parameters\n"); printf("f - format range of cylinders\n"); printf("b - bad block submenu\n"); printf("t - test submenu\n"); printf("s - seek to cylinder\n"); printf("r - read a sector\n"); printf("n - read next sector\n"); printf("w - write a sector\n"); printf("d - display (dump) sector buffer\n"); printf("e - fill sector buffer\n"); printf("m - install boot image (mkboot)\n"); printf("q - exit this program\n"); } /* bad block handling submenu */ bad() { int cyl, head, sector; char *cp; for ( ;; ) { printf("Bad block handling menu -\n"); printf(" l - display bad block list\n"); printf(" r - reread bad block list from disk\n"); printf(" w - write bad block list to disk\n"); printf(" c - clear (discard) bad block list\n"); printf(" e - add entry to bad block list\n"); printf(" q - quit to main menu\n"); do { cp = prompt("selection ? "); } while ( *cp == '\0' ); if ( *cp == 'q' ) /* q - quit */ break; else if ( *cp == 'l' ) { /* l - list */ badshow(); if ( cd->d_nbad > 16 ) cp = prompt("Return to continue:"); } else if ( *cp == 'r' ) /* r - read */ rdbad(); else if ( *cp == 'w' ) /* w - write */ wrbad(); else if ( *cp == 'c' ) { /* c - clear */ cd->d_nbad = 0; cd->d_bforw = NULBAD; cd->d_bback = NULBAD; cd->d_nxtbad = cd->d_badfree; badshow(); } else if ( *cp == 'e' ) { /* e - hand entry */ cyl = getparam("cylinder? "); head = getparam("head? "); sector = getparam("sector? "); (void) benter(cyl, head, sector, BF_HAND); } } } /* test submenu */ tests() { int cyl, head, sector; char *cp; for ( ;; ) { printf("Disk test menu -\n"); printf(" 1 - sanity check (r/w)\n"); printf(" 2 - non-destructive read test\n"); printf(" 3 - read/write pattern test\n"); printf(" 4 - interleave timing test\n"); printf(" 9 - seek test (not recommended)\n"); printf(" q - quit to main menu\n"); do { cp = prompt("selection ? "); } while ( *cp == '\0' ); if ( *cp == 'q' ) break; else if ( *cp == '1' ) sanity(); else if ( *cp == '2' ) readtest(); else if ( *cp == '3' ) rwtest(); else if ( *cp == '4' ) itest(); else if ( *cp == '9' ) seektest(); } } /* write a bootable image on this disk. * The i/o for the boot image is done in 1K blocks, * in particular, note that the size of the volume header * must be a 1K unit (this is verified below). */ #define KSIZE 1024 struct dskadd nxtblk; struct ctvol sblock; long lsum(); mkboot() { u_long imsize; u_long start, end; u_long addr; int imblocks; int i; /* start writing boot image here */ nxtblk.n_cyl = 0; nxtblk.n_head = 0; nxtblk.n_sec = 0; if ( sizeof(struct ctvol) != KSIZE ) printf("Superblock structure malformed\n"); start = gethparam ( "starting address of image: " ); end = gethparam ( "ending address of image: " ); imsize = end - start + 1; imblocks = (imsize+KSIZE-1) / KSIZE; printf("image size: %d bytes\n",imsize); printf("image uses %d 1K blocks\n",imblocks); zfill ( (char *) &sblock, KSIZE ); sblock.magic = (long) CTMAGIC; sblock.nheads = cd->d_ntracks; sblock.sectrk = cd->d_nsectors; sblock.seccyl = cd->d_ntracks * cd->d_nsectors; sblock.flags = 1; /* double density floppy */ sblock.ldrptr = (long) 1; sblock.ldrcnt = imblocks; sblock.cksum = (long) (-1) - lsum ( (long *) &sblock, (KSIZE/sizeof(long))-1 ); dwrite( (char *) &sblock ); /* It would seem tidy to zero the unused part of the last block, * but in fact it is unnecessary (It will be the BSS area anyway * and will get zeroed by the startup code if need be), and would * be bad to do unless the last block was copied into a local buffer. */ addr = start; for ( i=0; id_nsectors & ~1; if ( nxtblk.n_cyl != 0 ) { printf("Image too big\n"); return; } if ( winio(buf,WRITE,nxtblk.n_sec,0,nxtblk.n_head) ) printf("Boot write error %d %d",nxtblk.n_head,nxtblk.n_sec); incblk(&nxtblk,nspt); if ( nxtblk.n_cyl != 0 ) { printf("Image too big\n"); return; } if ( winio(buf+SSIZE,WRITE,nxtblk.n_sec,0,nxtblk.n_head) ) printf("Boot write error %d %d",nxtblk.n_head,nxtblk.n_sec); incblk(&nxtblk,nspt); } /* sequence thru the disk blocks * Notice: each track gets n_sec-1 blocks written, the boot roms * require this kind of business. */ incblk(nbp,ns) struct dskadd *nbp; { if ( ++nbp->n_sec < ns ) return; nbp->n_sec = 0; if ( ++nbp->n_head < cd->d_ntracks ) return; nbp->n_head = 0; ++nbp->n_cyl; } /* read bad block info from disk */ rdbad() { register struct dkbad *bp = cd->d_binfo; register i; register num; /* there is a copy at the end of each track */ for ( i=0; id_ntracks; i++ ) { if ( winio((char *)bp,READ,cd->d_nsectors-1,cd->d_ncylinders-1,i) ) printf("trouble reading bad block table from track %d\n",i); else break; } if ( i >= cd->d_ntracks ) return(0); if ( i != 0 ) printf("Bad block list read from track %d\n",i); if ( bp->bt_csn != BTMAGIC ) return(0); num = 0; for ( i=0; ibt_bad[i].bt_cyl == -1 ) break; ++num; (void) benter(bp->bt_bad[i].bt_cyl, bp->bt_bad[i].bt_trksec>>8, bp->bt_bad[i].bt_trksec&0xff, BF_MAPPED); } if ( num == 0 ) printf("bad block list empty on disk\n"); else printf("%d bad blocks in list read from disk\n",num); return ( 1 ); } wrbad() { int new, old; register struct dkbad *bp = cd->d_binfo; register struct badblk *bb; register i; bb = cd->d_bforw; bp->bt_csn = BTMAGIC; bp->bt_flag = 0; new = old = 0; for ( i=0; bb != NULBAD; i++ ) { if ( bb->b_flags & BF_MAPPED ) ++old; else ++new; bp->bt_bad[i].bt_cyl = bb->b_cyl; bp->bt_bad[i].bt_trksec = bb->b_head<<8 | bb->b_sect; bb = bb->b_forw; } /* fill rest of table */ for ( ; ibt_bad[i].bt_cyl = -1; bp->bt_bad[i].bt_trksec = -1; } /* write a copy at the end of each track */ for ( i=0; id_ntracks; i++ ) { if ( winio((char *)bp,WRITE,cd->d_nsectors-1,cd->d_ncylinders-1,i) ) printf("trouble writing bad block table to track %d\n",i); } } hdinit(drive) { int stat; register timeout; /* *HD_RESET_ON = 0; */ *DISK_BIU_RESET = 0; /***/ *HD_RESET_OFF = 0; *HD_WPC = cd->d_precomp>>2; /* RWC cylinder */ if ( drive ) *HD_SDH = 0x28; /* 512 byte sectors, drive 1 */ else *HD_SDH = 0x20; /* 512 byte sectors, drive 0 */ *HD_COMMAND = HDC_RESTORE|cd->d_steprate; /* restore */ if ( stat = hddone() ) hderr("Restore did not complete normally",stat); /* should verify track zero status after the restore !!!!! */ if ( (stat = *HD_STATUS) & HDS_MASK ) hderr("Restore failed",stat); } sanity() { int cyl, head, sector; int scyl, ecyl; int lsecsize; long *lbuf; char *cp; register i; register long tag; lbuf = (long *) iobuf; printf("starting cyl (0-%d)? ",cd->d_ncylinders-1); cp = prompt( (char *) 0 ); scyl = atol(cp); if ( scyl < 0 ) scyl = 0; if ( scyl > cd->d_ncylinders-1 ) scyl = cd->d_ncylinders-1; printf("ending cyl (%d-%d)? ",scyl,cd->d_ncylinders-1); cp = prompt( (char *) 0 ); ecyl = atol(cp); if ( ecyl < 0 ) ecyl = 0; if ( ecyl > cd->d_ncylinders-1 ) ecyl = cd->d_ncylinders-1; lsecsize = cd->d_secsize / sizeof(long); for ( cyl=scyl; cyl<=ecyl; cyl++ ) for ( head=0; headd_ntracks; head++ ) for ( sector=0; sectord_nsectors; sector++ ) { tag = cyl<<16 | head<<8 | sector; for ( i=0; id_ntracks; head++ ) for ( sector=0; sectord_nsectors; sector++ ) { for ( i=0; i cd->d_ntracks ) nhds = 1; */ nhds = cd->d_ntracks; cp = prompt( "cyl ? " ); cyl = atol(cp); if ( cyl < 0 ) cyl = 0; if ( cyl >= cd->d_ncylinders ) cyl = cd->d_ncylinders-1; /* set up timer1 to give us 3200 ticks per second. * this is 76800/3200 = 24 (need lsb only) */ *TMR_CW = (CW_C1|CW_LM|CW_M2|CW_BIN); /* 0x74 */ asm("nop"); *TIMER1 = 24; asm("nop"); *TIMER1 = 0; asm("nop"); for ( i=0; id_nbad == 0 ) { printf("No bad sectors.\n"); return; } printf("\nbad sector table:\n"); bp = cd->d_bforw; for ( i=0; bp != NULBAD; i++ ) { if ( i < 10 ) printf(" "); if ( i < 100 ) printf(" "); printf("%d: cyl %d, head %d, sector %d ", i+1, bp->b_cyl,bp->b_head,bp->b_sect); if ( bp->b_flags & BF_HAND ) putchar ( 'H' ); if ( bp->b_flags & BF_WRITE ) putchar ( 'W' ); if ( bp->b_flags & BF_READ ) putchar ( 'R' ); if ( bp->b_flags & BF_DATA ) putchar ( 'D' ); if ( bp->b_flags & BF_NEW ) printf(" (new)"); if ( bp->b_flags & BF_MAPPED ) printf(" (mapped)"); putchar ( '\n' ); bp = bp->b_forw; } if ( cd->d_nbad > cd->d_maxbad ) printf("bad sector table overflowed, info lost!\n"); } /* clear the NEW bit in all bad sector table entries */ clearnew() { register struct badblk *bp = cd->d_bforw; while ( bp != NULBAD ) { bp->b_flags &= ~BF_NEW; bp = bp->b_forw; } } /* we don't EVER expect to have to use this structure, but just in * case !! */ struct deadtrack { u_short d_cyl; u_char d_head; }; #define MAXDEAD 20 struct deadtrack deadtab[MAXDEAD]; int ndead; hdformat() { short fmtbuf[MAXHEADS][MAXSEC]; int cyl, head, times; int scyl, ecyl; int soft, hard; int nsoft, nhard; int pass; int spt; int pos; int error; /* any error in cylinder */ int terr; /* any error in track */ char badsec[MAXSEC]; register char *cp; register i; printf("starting cyl (0-%d)? ",cd->d_ncylinders-1); cp = prompt( (char *) 0 ); scyl = atol(cp); if ( scyl < 0 ) scyl = 0; if ( scyl > cd->d_ncylinders-1 ) scyl = cd->d_ncylinders-1; printf("ending cyl (%d-%d)? ",scyl,cd->d_ncylinders-1); cp = prompt( (char *) 0 ); ecyl = atol(cp); if ( ecyl < 0 ) ecyl = 0; if ( ecyl > cd->d_ncylinders-1 ) ecyl = cd->d_ncylinders-1; cp = prompt( "iterations ? " ); times = atol(cp); if ( times <= 0 ) times = 1; #ifdef OLD /* now this is in the disk parameter structure */ cp = prompt( "skew ? " ); if ( *cp == '\0' ) skew = 1; else { skew = atol(cp); if ( skew <= 0 ) skew = 0; } #endif #ifdef VARFMT /* I was curious to find out if I could push the format to 18 or more * sectors per track -- no luck (as expected), the whole track ends up * unreadable (IDNF) -- so no sense keeping this prompt. * Now this is just a bit of history -- if it was desired to allow * truly variable sector formats, one would need to define MAXSEC or such * and watch how arrays are dimensioned here and there. * No point in this, it is clear, at least for this hardware, we are always * talking 17 sectors of 512 bytes per track, no two ways about it. */ cp = prompt( "sectors per track? " ); if ( *cp == '\0' ) spt = cd->d_nsectors; else spt = atol(cp); #endif spt = cd->d_nsectors; /* 1:1 interleave format */ for ( head=0; headd_ntracks; head++ ) { pos = (head * cd->d_skew) % spt; for ( i=0; id_ntracks,' '); } error = 0; outbs(cd->d_ntracks + 5 ); /* do not have %4d, so simulate leading blanks */ if ( cyl < 1000 ) putchar ( ' ' ); if ( cyl < 100 ) putchar ( ' ' ); if ( cyl < 10 ) putchar ( ' ' ); printf("%d ",cyl); for ( head=0; headd_ntracks; head++ ) { soft = hard = 0; for ( i=0; id_nsectors; ++sector ) badsec[sector] = 0; /* first try to read entire track, if trouble, detail the sectors */ if ( trackio (iobuf,READ,cyl,head) ) { count = 0; for ( sector=0; sectord_nsectors; sector++ ) if ( secchk(cyl,head,sector,FMTPAT) ) { badsec[sector] = 1; ++count; } *bad = 1; if ( count == cd->d_nsectors ) return ( 1 ); /* looks like a bad format */ else return ( 0 ); /* OK, we are done */ } /* OK, it seemed to read OK, check the data */ error = 0; etp = (u_short *) &iobuf[TSIZE]; for ( wp=(u_short *)iobuf; wpd_nsectors; sector++ ) if ( secchk(cyl,head,sector,FMTPAT) ) badsec[sector] = 1; } *bad = error; return ( 0 ); } /* see if we can read a sector, and validate its contents. * any trouble -- try to enter it in the bad sector table. */ secchk(cyl,head,sector,patt) u_short patt; { register u_short *wp; register u_short *esp; /* can we read it at all ?? */ if ( winio (iobuf,READ,sector,cyl,head) ) { (void) benter(cyl, head, sector, BF_READ); return ( 1 ); } /* OK, we read it, but is the data right ?? */ esp = (u_short *) &iobuf[SSIZE]; for ( wp=(u_short *)iobuf; wp>1); *DISK_DMA_COUNT = nio; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp &= 0xffff; #ifdef DEBUG printf("lower DMA address: "); hex4(dmatmp); putchar('\n'); #endif *DISK_DMA_LADDR = dmatmp; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp >>= 16; #ifdef DEBUG printf("upper DMA address: "); hex4(dmatmp); putchar('\n'); #endif *DISK_DMA_UADDR_W = dmatmp; /* formatting is a write */ /* Second tell the controller to do the i/o */ /* this controller does implied seeks */ *HD_CYHIGH = cyl>>8; *HD_CYLOW = cyl; *HD_DMA_ENABLE = 0; *HD_SCOUNT = spt; /* sectors in this track */ if ( onetoone ) *HD_SNUM = 53; /* gap size in bytes */ else *HD_SNUM = 28; /* gap size in bytes */ #ifdef DEBUG printf("SDH "); hex2(0x20 | drive<<3 | head&0x07); putchar('\n'); #endif *HD_SDH = 0x20 | drive<<3 | head&0x07; /* GO for it !! */ *HD_COMMAND = HDC_FORMAT; /* This routine does "noisy" error recovery -- it just prints * messages and never returns a status -- at present this is * not causing any problems, but would be something to tidy up * (in the same fashion as winio() and trackio() ) in the future. */ if ( stat = hddone() ) hderr("Format did not complete normally",stat); stat = *HD_STATUS; #ifdef DEBUG printf("Done with status: "); hex2(stat); putchar('\n'); #endif if ( stat & HDS_MASK ) hderr("Format IO error",stat); } sectorio(iotype) { int cyl, head, sector; int code; register char *cp; printf("cylinder (0-%d)? ",cd->d_ncylinders-1); cp = prompt( (char *) 0 ); cyl = atol(cp); if ( cyl < 0 ) cyl = 0; if ( cyl > cd->d_ncylinders-1 ) cyl = cd->d_ncylinders-1; printf("head (0,%d)? ",cd->d_ntracks-1); cp = prompt( (char *) 0 ); head = atol(cp); if ( head < 0 ) head = 0; if ( head > cd->d_ntracks-1 ) head = cd->d_ntracks-1; printf("sector (0-%d)? ",cd->d_nsectors-1); cp = prompt( (char *) 0 ); sector = atol(cp); if ( sector < 0 ) sector = 0; if ( sector > cd->d_nsectors-1 ) sector = cd->d_nsectors-1; cd->d_curblk.n_cyl = cyl; cd->d_curblk.n_head = head; cd->d_curblk.n_sec = sector; if ( code = winio (iobuf,iotype,sector,cyl,head) ) { printf("io error: %x status = %x flags = %x\n", code>>8, *HD_STATUS&0xff,*HD_EFLAGS&0xff); return; } if ( iotype == READ ) vxdmp(iobuf,SSIZE); } /* running this on an entire ST251-1 took 2min 2sec (122sec). * This disk has 820*6*17*512 = 41,820 kb, so the effective transfer rate is: * 41,820 / 122 = 343 kb/s * Not super impressive, but OK (the serial i/o slows it down a bit). * (and this includes the seek time). * *** NOW faster with skew=1 format, namely 1min 49sec (109sec) */ readtest() { int scyl, ecyl, times; int cyl, head, sector; int error; /* flag for bad cylinders */ int terr; /* flag for bad tracks */ char badsec[MAXSEC]; register char *cp; register i; printf("starting cyl (0-%d)? ",cd->d_ncylinders-1); cp = prompt( (char *) 0 ); scyl = atol(cp); if ( scyl < 0 ) scyl = 0; if ( scyl > cd->d_ncylinders-1 ) scyl = cd->d_ncylinders-1; printf("ending cyl (%d-%d)? ",scyl,cd->d_ncylinders-1); cp = prompt( (char *) 0 ); ecyl = atol(cp); if ( ecyl < scyl ) ecyl = scyl; if ( ecyl > cd->d_ncylinders-1 ) ecyl = cd->d_ncylinders-1; cp = prompt( "iterations ? " ); times = atol(cp); if ( times <= 0 ) times = 1; error = 1; /* trigger first info line */ clearnew(); for ( cyl=scyl; cyl<=ecyl; cyl++ ) { /* restore information line after errors */ if ( error ) { printf("reading cylinder: 0 "); outby(cd->d_ntracks,' '); } error = 0; outbs ( cd->d_ntracks + 5 ); /* do not have %4d, so simulate leading blanks */ if ( cyl < 1000 ) putchar ( ' ' ); if ( cyl < 100 ) putchar ( ' ' ); if ( cyl < 10 ) putchar ( ' ' ); printf("%d ",cyl); for ( head=0; headd_ntracks; head++ ) { for ( i=0; id_nsectors; ++i ) badsec[i] = 0; terr = 0; for ( i=0; id_nsectors; sector++ ) { if ( badsec[sector] ) continue; if ( winio (iobuf,READ,sector,cyl,head) ) { badsec[sector] = 1; (void) benter(cyl, head, sector, BF_READ); } } } } showsym(terr,badsec); error += terr; } if ( error ) printf("\n"); } if ( ! error ) printf("\n"); badshow(); /* display bad sector table */ /* end of non-destructive read-only test */ } showsym(err,badsec) char badsec[]; { register num; register i; if ( err ) { num = 0; for ( i=0; i< cd->d_nsectors; ++i ) if ( badsec[i] ) ++num; printf("%c",(num<10) ? '0'+num : 'A'-10+num ); } else printf("."); } /* here are the Purdue/EE severe burnin patterns */ u_short spat[] = { 0xf00f, 0xec6d, 0031463,0070707,0133333,0155555,0161616,0143434, 0107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525, 0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252, #ifndef SHORTPASS 0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252, 052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525, #endif 052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525 }; #define NPT (sizeof (spat) / sizeof (u_short)) rwtest() { int scyl, ecyl, times; int cyl, head; int error; int severe; char badsec[MAXSEC]; int terr; char *resp; char *cp; u_short patt; register i; #ifdef OLD printf("WARNING !!!\n"); printf("This test destroys all data on the drive\n"); resp = prompt("Enter Y to continue: "); if ( *resp != 'Y' ) return; #endif printf("starting cyl (0-%d)? ",cd->d_ncylinders-1); cp = prompt( (char *) 0 ); scyl = atol(cp); if ( scyl < 0 ) scyl = 0; if ( scyl > cd->d_ncylinders-1 ) scyl = cd->d_ncylinders-1; printf("ending cyl (%d-%d)? ",scyl,cd->d_ncylinders-1); cp = prompt( (char *) 0 ); ecyl = atol(cp); if ( ecyl < scyl ) ecyl = scyl; if ( ecyl > cd->d_ncylinders-1 ) ecyl = cd->d_ncylinders-1; severe = 0; cp = prompt( "pattern (0xaaaa)? " ); if ( *cp == '\0' ) patt = 0xaaaa; else if ( *cp == 's' ) { severe = 1; printf("severe burn-in (%d patterns)\n",NPT); } else patt = atoh(cp); cp = prompt( "iterations ? " ); times = atol(cp); if ( times <= 0 ) times = 1; error = 1; /* trigger first info line */ clearnew(); for ( cyl=scyl; cyl<=ecyl; cyl++ ) { /* restore information line after errors */ if ( error ) { printf("testing cylinder: 0 "); outby(cd->d_ntracks,' '); } error = 0; outbs ( cd->d_ntracks + 5 ); /* do not have %4d, so simulate leading blanks */ if ( cyl < 1000 ) putchar ( ' ' ); if ( cyl < 100 ) putchar ( ' ' ); if ( cyl < 10 ) putchar ( ' ' ); printf("%d ",cyl); for ( head=0; headd_ntracks; head++ ) { for ( i=0; id_nsectors; i++ ) badsec[i] = 0; terr = 0; if ( severe ) { for ( i=0; id_nsectors; sector++ ) { if ( badsec[sector] ) continue; if ( winio (iobuf,WRITE,sector,cyl,head) ) { badsec[sector] = 1; (void) benter(cyl, head, sector, BF_WRITE); } } /* read -- sector by sector */ for ( sector=0; sectord_nsectors; sector++ ) { if ( badsec[sector] ) continue; if ( secchk(cyl,head,sector,patt) ) badsec[sector] = 1; #ifdef NEVER /* replaced by secchk() */ if ( winio (iobuf,READ,sector,cyl,head) ) { badsec[sector] = 1; (void) benter(cyl, head, sector, BF_READ); continue; } /* the read went OK, now check the data */ for ( wp=(u_short *)iobuf; wpd_bforw; bp != NULBAD; bp = bp->b_forw ) { if ( bp->b_cyl==cyl && bp->b_head==head && bp->b_sect==sector ) { bp->b_flags |= why; return ( 0 ); } } /* Aha! A new one! */ ++cd->d_nbad; if ( cd->d_nxtbad >= cd->d_badfree + cd->d_maxbad ) { /* The table filling up is bad, it means we loose information. * If we really have a disk this messed up, we will have to rethink * this whole scheme (currently the bad block mapping logic uses * a table holding at most 126 bad blocks on the disk itself.) */ if ( cd->d_nbad == cd->d_maxbad+1 ) printf("BAD SECTOR TABLE FULL\n"); return ( 1 ); } /* allocate new entry, and fill it. */ new = cd->d_nxtbad++; new->b_cyl = cyl; new->b_head = head; new->b_sect = sector; new->b_flags = (why | BF_NEW); if ( (bp = cd->d_bforw) == NULBAD ) { /* first entry ever */ new->b_back = new->b_forw = NULBAD; cd->d_bback = cd->d_bforw = new; return ( 1 ); } if ( btest ( new, bp ) < 0 ) { /* add to start of list */ new->b_forw = bp; new->b_back = NULBAD; bp->b_back = cd->d_bforw = new; return ( 1 ); } while ( btest ( new, bp ) > 0 ) { if ( bp->b_forw == NULBAD ) { /* add to end of list */ new->b_forw = NULBAD; new->b_back = bp; cd->d_bback = bp->b_forw = new; return ( 1 ); } bp = bp->b_forw; } /* add before this entry */ new->b_forw = bp; new->b_back = bp->b_back; bp->b_back->b_forw = new; bp->b_back = new; return ( 1 ); } btest ( new, ref ) struct badblk *new, *ref; { if ( new->b_cyl < ref->b_cyl ) return ( -1 ); if ( new->b_cyl > ref->b_cyl ) return ( 1 ); if ( new->b_head < ref->b_head ) return ( -1 ); if ( new->b_head > ref->b_head ) return ( 1 ); return ( new->b_sect - ref->b_sect ); } winio (buf,io,sector,cyl,head) char *buf; { int stat; int code; register nio; register unsigned dmatmp; /* first pulse the reset line (groping in the dark). * ?? maybe only do this as recovery from overrun/underrun. */ *HD_RESET_ON = 0; *HD_RESET_OFF = 0; /* First, set up the dma */ *DISK_DMA_DISABLE = 0; *DISK_BIU_RESET = 0; nio = SSIZE; nio = -(nio>>1); *DISK_DMA_COUNT = nio; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp &= 0xffff; #ifdef DEBUG printf("lower DMA address: "); hex4(dmatmp); putchar('\n'); #endif *DISK_DMA_LADDR = dmatmp; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp >>= 16; #ifdef DEBUG printf("upper DMA address: "); hex4(dmatmp); putchar('\n'); #endif if ( io == READ ) *DISK_DMA_UADDR_R = dmatmp; else *DISK_DMA_UADDR_W = dmatmp; /* Second tell the controller to do the i/o */ /* this controller does implied seeks */ *HD_CYHIGH = cyl>>8; *HD_CYLOW = cyl; *HD_DMA_ENABLE = 0; #ifdef DEBUG printf("Sector %d\n",sector); #endif *HD_SNUM = sector; #ifdef DEBUG printf("SDH "); hex2(0x20 | drive<<3 | head&0x07); putchar('\n'); #endif *HD_SDH = 0x20 | drive<<3 | head&0x07; /* single sector reads and writes are done here * for these HD_SCOUNT is ignored. */ /* using dmatmp as a scratch variable */ if ( io == READ ) dmatmp = (HDC_READ|HDC_IEOC); else dmatmp = (HDC_WRITE); #ifdef DEBUG printf("Command to hd controller: "); hex2(dmatmp); putchar('\n'); #endif /* GO for it !! */ *HD_COMMAND = dmatmp; code = hddone()<<8; stat = *HD_STATUS; #ifdef DEBUG printf("Done with status: "); hex2(stat); putchar('\n'); if ( stat & HDS_MASK ) { printf("io error, cyl %d, head %d, sector %d",cyl,head,sector); hderr("; ",stat); code |= (0x0100 | *HD_EFLAGS&0xff); } #else if ( stat & HDS_MASK ) code |= (0x0100 | *HD_EFLAGS&0xff); #endif return ( code ); } trackio (buf,io,cyl,head) char *buf; { int stat; int code; register nio; register unsigned dmatmp; /* first pulse the reset line (groping in the dark). * ?? maybe only do this as recovery from overrun/underrun. */ *HD_RESET_ON = 0; *HD_RESET_OFF = 0; /* First, set up the dma */ *DISK_DMA_DISABLE = 0; *DISK_BIU_RESET = 0; nio = TSIZE; /* entire track */ nio = -(nio>>1); *DISK_DMA_COUNT = nio; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp &= 0xffff; #ifdef DEBUG printf("lower DMA address: "); hex4(dmatmp); putchar('\n'); #endif *DISK_DMA_LADDR = dmatmp; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp >>= 16; #ifdef DEBUG printf("upper DMA address: "); hex4(dmatmp); putchar('\n'); #endif if ( io == READ ) *DISK_DMA_UADDR_R = dmatmp; else *DISK_DMA_UADDR_W = dmatmp; /* Second tell the controller to do the i/o */ /* this controller does implied seeks */ *HD_CYHIGH = cyl>>8; *HD_CYLOW = cyl; *HD_DMA_ENABLE = 0; /* read or write a whole track */ *HD_SNUM = 0; *HD_SCOUNT = cd->d_nsectors; #ifdef DEBUG printf("SDH "); hex2(0x20 | drive<<3 | head&0x07); putchar('\n'); #endif *HD_SDH = 0x20 | drive<<3 | head&0x07; /* using dmatmp as a scratch variable */ if ( io == READ ) dmatmp = (HDC_READ|HDC_IEOC|HDC_MULT); else dmatmp = (HDC_WRITE|HDC_MULT); #ifdef DEBUG printf("Command to hd controller: "); hex2(dmatmp); putchar('\n'); #endif /* GO for it !! */ *HD_COMMAND = dmatmp; code = hddone()<<8; stat = *HD_STATUS; #ifdef DEBUG printf("Done with status: "); hex2(stat); putchar('\n'); if ( stat & HDS_MASK ) { printf("track io error, cyl %d, head %d",cyl,head); printf(",sector %d",*HD_SNUM&0xff); hderr("; ",stat); code |= (0x0100 | *HD_EFLAGS&0xff); } #else if ( stat & HDS_MASK ) code |= (0x0100 | *HD_EFLAGS&0xff); #endif return ( code ); } seektest() { int scyl, ecyl; char *cp; printf("starting cyl (0-%d)? ",cd->d_ncylinders-1); cp = prompt( (char *) 0 ); scyl = atol(cp); if ( scyl < 0 ) scyl = 0; if ( scyl > cd->d_ncylinders-1 ) scyl = cd->d_ncylinders-1; printf("ending cyl (%d-%d)? ",scyl,cd->d_ncylinders-1); cp = prompt( (char *) 0 ); ecyl = atol(cp); if ( ecyl < 0 ) ecyl = 0; if ( ecyl > cd->d_ncylinders-1 ) ecyl = cd->d_ncylinders-1; while ( scyl <= ecyl ) { printf("Seeking to cylinder %d\n",scyl); hdseek(scyl++); printf("Seeking to cylinder %d\n",ecyl); hdseek(ecyl--); } } hdseek(cyl) { int stat; #ifdef DEBUG printf("seeking to cylinder %d\n",cyl); #endif *HD_CYHIGH = cyl>>8; *HD_CYLOW = cyl; *HD_COMMAND = HDC_SEEK|cd->d_steprate; /* seek */ (void ) hddone(); #ifdef NEVER /* I tried this code, inspired by (my memory of) the boot roms, the result * running my "seek test" was a horrible noise and the drive locking up * until I cycled power - I think I will avoid this (and the pseudo-interrupt * seems to work just fine. */ /* the boot ROMs check CIP to find out when a seek is done */ /* wait for seek to start */ while ( ! (*HD_STATUS & HDS_BUSY) ) ; while ( *HD_STATUS & HDS_CIP ) ; #endif if ( (stat = *HD_STATUS) & HDS_MASK ) hderr("Seek error",stat); } /* initialize the PIC, this was a real smart thing to do */ picinit() { *PIC_A0 = ICW1; *PIC_A1 = ICW2; *PIC_A1 = ICW4; *PIC_A1 = 0xff; /* all sources masked */ } /* This must be long enough for really long seeks - the longest * (and slowest) is a restore when the heads were way out at cylinder * 800 or such - this can take several seconds. * (The restore is done taking one step at a time and waiting for seek * complete - the specified step rate is ignored.) */ #define PICTIMEOUT 400000 /* was 100000 */ /* note: the "interrupt" is presented only on one read by the 8259 * when polling like this, you read 0x continuously, then once get * 0x8x, then back to 0x as long as you care to keep reading. * Also note that on this machine, when reading an 8 bit register * on a 16 bit bus, the upper 8 bits gets set to ones, so you get * 0xff8x (or 0xff0x, it is not sign extension). * */ hddone() { long timeout; register picstat; register code = 0; #ifdef DEBUG int picold = -1; printf("begin hddone (wait for IO)\n"); #endif *PIC_A1 = 0x9f; /* allow levels 5 and 6 */ timeout = PICTIMEOUT; while ( timeout-- ) { *PIC_A0 = 0x0e; /* polling the 8259 */ picstat = (*PIC_A0) & 0xff; #ifdef DEBUG if ( picold == -1 ) { printf("PIC status = "); hex2(picstat); putchar('\n'); picold = picstat; } else if ( picstat != picold ) { printf("PIC status = "); hex2(picstat); putchar('\n'); picold = picstat; } #endif if ( picstat == 0x86 ) /* Winchester EOT */ break; if ( picstat == 0x85 ) { /* overrun, underrun */ #ifdef DEBUG printf("overrun, underrun\n"); #endif code = HDD_OVERRUN; break; } } #ifdef DEBUG printf("Done with IO wait loop"); if ( timeout == -1 ) printf(" TIMEOUT"); #endif if ( timeout == -1 ) code = HDD_TIMEOUT; *HD_SDH = 0x18; /* impossible (select no drive) */ *DISK_DMA_DISABLE = 0; *DISK_BIU_RESET = 0; *PIC_A1 = 0xff; /* remask all sources */ #ifdef DEBUG putchar('\n'); #endif return ( code ); } hderr(msg,status) char *msg; { printf("%s, status = ",msg); hex2(status); printf(", err = "); hex2(*HD_EFLAGS); putchar('\n'); } char * prompt(msg) char *msg; { static char buf[132]; if ( msg ) printf("%s", msg); gets(buf); return (buf); } /* vxdmp - memory dump, vxworks style */ vxdmp(b,n) char *b; { register i; register j; register c; for ( i=0; i '~' ) c = '.'; putchar(c); } printf("*\n"); } }