* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)format.c 6.7 (Berkeley) %G%";
* Standalone program to do media checking
* and record bad block information on any
* disk with the appropriate driver and RM03-style headers.
* add new bad sectors to bad-sector table when formatting by track
* (rearranging replacements ala bad144 -a)
* multi-pass format for disks with skip-sector capability
#define MAXBADDESC 126 /* size of bad block table */
#define CHUNK 48 /* max # of sectors/io operation */
#define SECTSIZ 512 /* standard sector size */
#define HDRSIZ 4 /* number of bytes in sector header */
#define SSDEV(fd) (ioctl((fd), SAIOSSDEV, (char *)0) == 0)
struct dkbad dkbad
; /* bad sector table */
struct dkbad oldbad
; /* old bad sector table */
struct dkbad sstab
; /* skip sector table */
int errors
[NERRORS
]; /* histogram of errors */
* Purdue/EE severe burnin patterns.
unsigned short ppat
[] = {
0xf00f, 0xec6d, 0031463,0070707,0133333,0155555,0161616,0143434,
0107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525,
0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525,
052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525
#define NPT (sizeof (ppat) / sizeof (short))
int maxpass
, npat
; /* subscript to ppat[] */
int severe
; /* nz if running "severe" burnin */
int ssdev
; /* device supports skip sectors */
int startcyl
, endcyl
, starttrack
, endtrack
;
int nbads
; /* subscript for bads */
daddr_t bads
[2*MAXBADDESC
]; /* Bad blocks accumulated */
int lastsector
, tracksize
, rtracksize
;
int unit
, fd
, resid
, i
, trk
, cyl
, debug
;
printf("Disk format/check utility\n\n");
cp
= prompt("Enable debugging (0=none, 1=bse, 2=ecc, 3=bse+ecc)? ");
for (i
= 0; i
< NERRORS
; i
++)
ioctl(fd
, SAIODEVDATA
, &st
);
printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n",
st
.ncyl
, st
.ntrak
, st
.nsect
);
ioctl(fd
, SAIOSSI
, (char *)0); /* set skip sector inhibit */
printf("(not counting skip-sector replacement)\n");
printf("Start formatting...make sure the drive is online\n");
ioctl(fd
, SAIONOBAD
, (char *)0);
ioctl(fd
, SAIORETRIES
, (char *)0);
ioctl(fd
, SAIOECCLIM
, (char *)maxeccbits
);
ioctl(fd
, SAIODEBUG
, (char *)debug
);
tracksize
= sizeof (struct sector
) * st
.nsect
;
rtracksize
= SECTSIZ
* st
.nsect
;
bp
= (struct sector
*)malloc(tracksize
);
rbp
= malloc(rtracksize
);
for (; pass
< maxpass
; pass
++) {
printf("Begin pass %d\n", pass
);
* Begin check, for each track,
* 1) Write header and test pattern.
* 2) Read data. Hardware checks header and data ECC.
* Read data (esp on Eagles) is much faster than write check.
sector
= ((startcyl
* st
.ntrak
) + starttrack
) * st
.nsect
;
lastsector
= ((endcyl
* st
.ntrak
) + endtrack
) * st
.nsect
for ( ; sector
< lastsector
; sector
+= st
.nsect
) {
trk
= (sector
% st
.nspc
) / st
.nsect
;
for (i
= 0; i
< st
.nsect
; i
++) {
(u_short
) cyl
| HDR1_FMT22
| HDR1_OKSCT
;
bp
[i
].header2
= ((u_short
)trk
<< 8) + i
;
if (sector
&& (sector
% (st
.nspc
* 100)) == 0)
printf("cylinder %d\n", cyl
);
* Try and write the headers and data patterns into
* each sector in the track. Continue until such
* we're done, or until there's less than a sector's
* worth of data to transfer.
* The lseek call is necessary because of
* the odd sector size (516 bytes)
for (resid
= tracksize
, cbp
= bp
, sn
= sector
;;) {
lseek(fd
, sn
* SECTSIZ
, 0);
ioctl(fd
, SAIOHDR
, (char *)0);
cc
= write(fd
, cbp
, resid
);
* Don't record errors during write,
* all errors will be found during
sn
= iob
[fd
- 3].i_errblk
;
resid
-= (sn
- sector
) * sizeof (struct sector
);
if (resid
< sizeof (struct sector
))
* Retry remainder of track on error until
* we're done, or until there's less than a
for (resid
= rtracksize
, rcbp
= rbp
, sn
= sector
;;) {
lseek(fd
, sn
* SECTSIZ
, 0);
cc
= read(fd
, rcbp
, resid
);
rsn
= sn
- (sn
/ st
.nsect
);
printf("sector %d, read error\n\n", rsn
);
if (recorderror(fd
, sn
, &st
) < 0 && pass
> 0)
/* advance past bad sector */
resid
= rtracksize
- ((sn
- sector
) * SECTSIZ
);
rcbp
= rbp
+ ((sn
- sector
) * SECTSIZ
);
if (severe
&& maxpass
< NPT
) {
cp
= prompt("More passes? (0 or number) ");
* Sort bads and insert in bad block table.
qsort(bads
, nbads
, sizeof (daddr_t
), qcompar
);
for (i
= 0; i
< nbads
; i
++)
recorderror(fd
, bads
[i
], &st
);
if (errors
[FE_TOTAL
] || errors
[FE_SSE
]) {
/* change the headers of all the bad sectors */
writebb(fd
, errors
[FE_SSE
], &sstab
, &st
, SSERR
);
writebb(fd
, errors
[FE_TOTAL
], &dkbad
, &st
, BSERR
);
if (errors
[FE_TOTAL
] || errors
[FE_SSE
]) {
for (i
= 0; i
< NERRORS
; i
++)
printf("%s: %d\n", errornames
[i
], errors
[i
]);
printf("Total of %d hard errors revectored\n",
errors
[FE_TOTAL
] + errors
[FE_SSE
]);
if (endcyl
== st
.ncyl
- 1 &&
(startcyl
< st
.ncyl
- 1 || starttrack
== 0)) {
while (errors
[FE_TOTAL
] < MAXBADDESC
) {
int i
= errors
[FE_TOTAL
]++;
dkbad
.bt_bad
[i
].bt_cyl
= -1;
dkbad
.bt_bad
[i
].bt_trksec
= -1;
printf("\nWriting bad sector table at sector #%d\n",
st
.ncyl
* st
.nspc
- st
.nsect
);
for (i
= 0; i
< 10 && i
< st
.nsect
; i
+= 2) {
lseek(fd
, SECTSIZ
* (st
.ncyl
* st
.nspc
- st
.nsect
+ i
), 0);
write(fd
, &dkbad
, sizeof (dkbad
));
} else if (errors
[FE_TOTAL
]) {
printf("New bad sectors (not added to table):\n");
for (i
= 0; i
< errors
[FE_TOTAL
]; i
++) {
printf("bn %d (cn=%d, tn=%d, sn=%d)\n", badsn(bt
, &st
),
bt
->bt_cyl
, bt
->bt_trksec
>>8, bt
->bt_trksec
&0xff);
ioctl(fd
,SAIONOSSI
,(char *)0);
register daddr_t
*l1
, *l2
;
register struct bt_bad
*bt
;
return ((bt
->bt_cyl
* st
->ntrak
+ (bt
->bt_trksec
>>8)) *
(st
->nsect
- 1) + (bt
->bt_trksec
&0xff)) - 1;
return ((bt
->bt_cyl
*st
->ntrak
+ (bt
->bt_trksec
>>8)) * st
->nsect
* Mark the bad/skipped sectors.
* Bad sectors on skip-sector devices are assumed to be skipped also,
* and must be done after the (earlier) first skipped sector.
writebb(fd
, nsects
, dbad
, st
, sw
)
struct sector bb_buf
; /* buffer for one sector plus 4 byte header */
for (i
= 0; i
< nsects
; i
++) {
bb_buf
.header1
= HDR1_FMT22
|btp
->bt_cyl
;
bb_buf
.header1
|= HDR1_SSF
;
btp
->bt_cyl
| HDR1_FMT22
| HDR1_SSF
| HDR1_OKSCT
;
bb_buf
.header2
= btp
->bt_trksec
;
bn
= st
->nspc
* btp
->bt_cyl
+
st
->nsect
* (btp
->bt_trksec
>> 8) +
lseek(fd
, bn
* SECTSIZ
, 0);
ioctl(fd
, SAIOHDR
, (char *)0);
write(fd
, &bb_buf
, sizeof (bb_buf
));
* If skip sector, mark all remaining
for (j
= (btp
->bt_trksec
& 0xff) + 1, bn
++;
j
< st
->nsect
; j
++, bn
++) {
bb_buf
.header2
= j
| (btp
->bt_trksec
& 0xff00);
lseek(fd
, bn
* SECTSIZ
, 0);
ioctl(fd
, SAIOHDR
, (char *)0);
write(fd
, &bb_buf
, sizeof (bb_buf
));
* Record an error, and if there's room, put
* it in the appropriate bad sector table.
* If severe burnin store block in a list after making sure
* we have not already found it on a prev pass.
for (i
= 0; i
< nbads
; i
++)
return(0); /* bn already flagged */
if (nbads
>= (ssdev
? 2 * MAXBADDESC
: MAXBADDESC
)) {
printf("Bad sector table full, format terminating\n");
if (errno
< EBSE
|| errno
> EHER
)
if (errno
>= EBSE
&& errno
<= EHER
) {
if (ssdev
) { /* if drive has skip sector capability */
if (errors
[FE_SSE
] >= MAXBADDESC
) {
/* this is bogus, we don't maintain skip sector table */
printf("Too many skip sector errors\n");
/* only one skip sector/track */
tn
!= (sstab
.bt_bad
[ss
- 1].bt_trksec
>> 8) ||
cn
!= sstab
.bt_bad
[ss
- 1].bt_cyl
) {
* Don't bother with skipping the extra sector
* at the end of the track.
sstab
.bt_bad
[ss
].bt_cyl
= cn
;
sstab
.bt_bad
[ss
].bt_trksec
= (tn
<<8) + sn
;
if (errors
[FE_TOTAL
] >= MAXBADDESC
) {
printf("Too many bad sectors\n");
/* record the bad sector address and continue */
dkbad
.bt_bad
[errors
[FE_TOTAL
]].bt_cyl
= cn
;
dkbad
.bt_bad
[errors
[FE_TOTAL
]++].bt_trksec
= (tn
<< 8) + sn
;
* Allocate memory on a page-aligned address.
* Round allocated chunk to a page multiple to
last
= (caddr_t
)(((int)&end
+ 511) & ~0x1ff);
size
= (size
+ 511) & ~0x1ff;
* Prompt and verify a device name from the user.
register struct devsw
*dp
;
cp
= prompt("Device to format? ");
if ((fd
= open(cp
, 2)) < 0) {
printf("Known devices are: ");
for (dp
= devsw
; dp
->dv_name
; dp
++)
printf("%s ",dp
->dv_name
);
printf("Formatting drive %c%c%d on adaptor %d: ",
cp
[0], cp
[1], iob
[fd
- 3].i_unit
% 8, iob
[fd
- 3].i_unit
/ 8);
cp
= prompt("verify (yes/no)? ");
while (*cp
!= 'y' && *cp
!= 'n')
cp
= prompt("Huh, yes or no? ");
* Find range of tracks to format.
startcyl
= getnum("Starting cylinder", 0, st
->ncyl
- 1, 0);
starttrack
= getnum("Starting track", 0, st
->ntrak
- 1, 0);
endcyl
= getnum("Ending cylinder", 0, st
->ncyl
- 1, st
->ncyl
- 1);
endtrack
= getnum("Ending track", 0, st
->ntrak
- 1, st
->ntrak
- 1);
getnum(s
, low
, high
, dflt
)
printf("%s (%d): ", s
, dflt
);
if (val
>= low
&& val
<= high
)
printf("Value must be in range [%d,%d]\n", low
, high
);
{ 0xf00ff00f, "RH750 worst case" },
{ 0xec6dec6d, "media worst case" },
{ 0xa5a5a5a5, "alternate 1's and 0's" },
{ 0xFFFFFFFF, "Severe burnin (up to 48 passes)" },
register struct pattern
*p
;
printf("Available test patterns are:\n");
for (p
= pat
; p
->pa_value
; p
++)
printf("\t%d - (%x) %s\n", (p
- pat
) + 1,
p
->pa_value
& 0xffff, p
->pa_name
);
cp
= prompt("Pattern (one of the above, other to restart)? ");
if (pattern
< 0 || pattern
>= npatterns
)
if (pat
[pattern
].pa_value
== -1) {
cp
= prompt("How many passes (up to 48)? ");
"Maximum number of bit errors to allow for soft ECC",
* Initialize the buffer with the requested pattern.
register struct xsect
*bp
;
register struct pattern
*pptr
;
register long *pp
, *last
;
register struct xsect
*lastbuf
;
size
/= sizeof (struct sector
);
patt
= ppat
[npat
] | ((long)ppat
[npat
] << 16);
printf("Write pattern 0x%x\n", patt
&0xffff);
for (pp
= bp
->buf
; pp
< last
; pp
++)