Files
Arquivotheca.Solaris-2.5/cmd/format/defect.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

727 lines
17 KiB
C
Executable File

#ifndef lint
#pragma ident "@(#)defect.c 1.12 94/09/25 SMI"
#endif lint
/*
* Copyright (c) 1991 by Sun Microsystems, Inc.
*/
/*
* This file contains routines that manipulate the defect list.
*/
#include "global.h"
#include <sys/types.h>
#include <sys/param.h>
#ifdef sparc
#include <sys/hdio.h>
#endif
#include <sys/buf.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <string.h>
#include <unistd.h>
#include <memory.h>
#ifdef sparc
#include <sys/dkbad.h>
#include <sys/scsi/targets/sddef.h>
#endif
#include "misc.h"
#include "param.h"
#ifdef sparc
/*
* This structure is the bad block table for the current disk if
* the disk uses bad-144 defect mapping.
*/
struct dkbad badmap;
#endif
/*
* This routine reads the defect list off the disk. It also reads in the
* bad block table if the disk is a BAD144 type. The defect list is
* located on the first 2 tracks of the 2nd alternate cylinder of all
* disks. The bad block map is located on the first 5 even sectors of
* the last track of the last cylinder.
*/
void
read_list(list)
struct defect_list *list;
{
int size, head, sec, status;
struct bt_bad *bt;
assert(!EMBEDDED_SCSI);
/*
* Panther's working list is maintained by the controller
*/
if (cur_ctype->ctype_flags & CF_WLIST) {
if (*cur_ops->op_ex_cur != NULL &&
(status = (*cur_ops->op_ex_cur)(list)) == 0) {
if (list->header.magicno != DEFECT_MAGIC) {
fmt_print("Defect list BAD\n");
} else {
fmt_print("Controller working list found\n");
}
return;
}
if (*cur_ops->op_ex_man != NULL &&
(status = (*cur_ops->op_ex_man)(list)) == 0) {
if (list->header.magicno != DEFECT_MAGIC) {
fmt_print("Defect list BAD\n");
} else {
fmt_print("MANUFACTURER's list found\n");
}
return;
}
fmt_print("No defect list found\n");
return;
}
/*
* Loop for each copy of the defect list until we get a good one.
*/
for (head = 0; head < LISTCOUNT; head++) {
/*
* Try to read the list header.
*/
if ((*cur_ops->op_rdwr)(DIR_READ, cur_file,
chs2bn(ncyl + 1, head, 0), 1,
(char *)&list->header), F_NORMAL)
continue;
/*
* If the magic number is wrong, this copy is corrupt.
*/
if (list->header.magicno != DEFECT_MAGIC)
continue;
/*
* Allocate space for the rest of the list.
*/
size = LISTSIZE(list->header.count);
list->list = (struct defect_entry *)zalloc(size * SECSIZE);
/*
* Try to read in the rest of the list. If there is an
* error, or the checksum is wrong, this copy is corrupt.
*/
if ((*cur_ops->op_rdwr)(DIR_READ, cur_file,
chs2bn(ncyl + 1, head, 1), size,
(char *)list->list, F_NORMAL) ||
checkdefsum(list, CK_CHECKSUM)) {
/*
* Destroy the list and go on.
*/
kill_deflist(list);
continue;
}
/*
* Got a good copy, stop searching.
*/
break;
}
/*
* Check the list for non-bfi defects (SMD and Adaptec disks only).
* This is required because the logical sectors in 451 defect lists
* are off by the head skew.
*/
(void) makebfi(list, (struct defect_entry *)NULL);
#ifdef sparc
if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
return;
/*
* The disk uses BAD144, read in the bad-block table.
*/
for (sec = 0; sec < BAD_LISTCNT * 2; sec += 2) {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file,
chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
&badmap, F_NORMAL);
if (status)
continue;
/*
* Do a sanity check on the list read in. If it passes,
* stop searching.
*/
if (badmap.bt_mbz != 0)
continue;
for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++) {
if (bt->bt_cyl < 0)
break;
if (bt->bt_trksec < 0)
continue;
head = bt->bt_trksec >> 8;
if ((bt->bt_cyl >= pcyl) || (head >= nhead) ||
((bt->bt_trksec & 0xff) >= sectors(head))) {
status = -1;
break;
}
}
if (status)
continue;
return;
}
/*
* If we couldn't find the bad block table, initialize it to
* zero entries.
*/
for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++)
bt->bt_cyl = bt->bt_trksec = -1;
badmap.bt_mbz = badmap.bt_csn = badmap.bt_flag = 0;
#endif
}
/*
* Put all logical sector defects into Bytes From Index (bfi) format.
* This is necessary
* because 450/451 formatted drives have all their logical sectors off
* by the head skew but the 7053 does not.
* We are called by add_ldef() and d_add() when a defect is being added
* to the list, or by read_list() when a defect list is read in.
*/
int
makebfi(list, def)
struct defect_list *list;
struct defect_entry *def;
{
struct defect_entry *ptr, save;
struct defect_entry *end = list->list + list->header.count - 1;
struct dk_cinfo dkinfo;
int index, skew = 0;
int i, savpos;
/*
* Only do this stuff for SMD and Adaptec controllers.
* Note sector skew for * 450/451 controllers.
*/
if (ioctl(cur_file, DKIOCINFO, &dkinfo) >= 0) {
switch (dkinfo.dki_ctype) {
case DKC_XY450:
skew = 1;
break;
case DKC_XD7053:
break;
default:
return (0);
}
}
if (def != NULL) {
/*
* Calculate the bfi for this defect. The caller will
* sort it and add it to the list.
*/
calc_bfi(list, def, end, skew);
return (1);
}
/*
* if there is no defect list then return
*/
if (list->list == (struct defect_entry *)0)
return (0);
/*
* Sort the defect list in place.
*/
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
if (ptr->bfi != UNKNOWN)
continue;
/*
* Got a non-bfi defect. Look through the list up to
* this point for a previous defects on this track. If
* the FIRST defect on the track is closer to sector 0
* than this one, we have to add the number of
* bytes/sector when we calculate the bfi, as we are
* "ahead of" the slipped sector. If the defect is
* numerically less than the first defect on the track,
* no addition is necessary.
*
* NOTE: all this assumes we only slip once per track and
* that logical defects always follow bfi defects in the
* defect list. If the list is all logical defects, we
* assume chronological ordering. Thus, sector 10 will be
* slipped if it is first in the list, even if followed
* by sector 5.
*/
calc_bfi(list, ptr, ptr, skew);
}
/*
* Sort the list. We take the expedient route and remove the
* defect from the list and use add_def to put it back in. Not
* pretty, but it works.
*/
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
(void) memcpy((char *)&save, (char *)ptr,
sizeof (struct defect_entry));
savpos = ptr - list->list;
for (i = savpos; i < list->header.count; i++)
*(list->list + i) = *(list->list + i + 1);
--list->header.count;
index = sort_defect(&save, list);
add_def(&save, list, index);
}
/*
* Make sure to redo the checksum.
*/
(void) checkdefsum(list, CK_MAKESUM);
return (1);
}
/*
* Calculate the bytes from index for this defect. If the first defect
* on this track (determined by its position in the defect list) was
* closer to logical sector 0, then we add the number of bytes/sector,
* since this one is "ahead" of the slipped sector. We mark the length
* of the defect in bits as being the length of a sector to differentiate
* it from manufacturers and bfi defects added by the user.
*/
void
calc_bfi(list, def, end, skew)
struct defect_list *list;
struct defect_entry *def;
struct defect_entry *end;
int skew;
{
struct defect_entry *tmp;
int bps = cur_dtype->dtype_bps;
int a, b;
int hsect;
#ifdef sparc
struct hdk_type type;
if (ioctl(cur_file, HDKIOCGTYPE, &type) != -1)
hsect = type.hdkt_hsect;
else /* assume there is a spare */
#endif
hsect = nsect + 1;
def->bfi = bps * ((def->sect + def->head * skew) % hsect);
def->nbits = SECSIZE;
if (list->list == (struct defect_entry *)0)
return;
/*
* No adjustments necessary for a non-slipped disk.
*/
if (nsect == hsect)
return;
for (tmp = list->list; tmp < end; tmp++) {
if (tmp->cyl != def->cyl || tmp->head != def->head)
continue;
/*
* Found another defect on this track. See if it's closer
* numerically to logical sector 0. If it is, bump our
* defect's bfi by the number of bytes per sector.
*/
a = tmp->bfi;
b = def->bfi;
/*
* 'a' must be at the beginning of a sector (boundary)
* for this to work.
*/
a = a - (a % bps);
if (a < tmp->head * bps)
a = a + ((nsect + 1 - tmp->head) * bps);
if (b < def->head * bps)
b = b + ((nsect + 1 - def->head) * bps);
if (a <= b) {
def->bfi += bps;
return;
} else
break;
}
}
/*
* Add the logical sector information for each bfi entry. This is
* done to be compatible with the diag program
* This has to be done anytime that the defect list can be written to
* the disk or a file
* A disk on a 450 without a spare and with a runt does not work properly.
* This bug is left in because it would mean that the disk might have to
* be formatted to determine what the track headers look like. I didn't
* do that because the code to figure out if the disk needs to be formatted
* to determine the header geometry is error prone (and destructive).
*/
int
makelsect(list)
struct defect_list *list;
{
struct defect_entry *ptr, *tmp;
struct dk_cinfo dkinfo;
int skew = 0;
int runt = 1;
int spare = 1;
int hsect = nsect;
#ifdef sparc
struct hdk_type type;
/*
* Only do for SMD disks which for now are 7053 & 451/450
*/
if (ioctl(cur_file, DKIOCINFO, &dkinfo) >= 0) {
switch (dkinfo.dki_ctype) {
case DKC_XY450:
skew = 1;
if (!ioctl(cur_file, HDKIOCGTYPE, &type))
if ((hsect = type.hdkt_hsect) == (nsect + 1)) {
runt = 0;
} else if (hsect == nsect) {
spare = 0;
}
break;
case DKC_XD7053:
break;
default:
return (0);
}
}
#endif
/*
* if there is no defect list then return
*/
if (list->list == (struct defect_entry *)0)
return (0);
#ifdef sparc
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
if (ptr->bfi == UNKNOWN)
continue;
ptr->sect = ptr->bfi/cur_dtype->dtype_bps -
ptr->head * skew;
if (ptr->sect < 0)
ptr->sect += runt ? (hsect - 1) : hsect;
/*
* correct the logical sector for track that has more than
* one defect that are on seperate physical sectors
*/
if (ptr != list->list) {
tmp = &ptr[-1];
if ((ptr->cyl == tmp->cyl &&
ptr->head == tmp->head &&
ptr->sect != tmp->sect &&
tmp->bfi != ptr->bfi) &&
spare)
ptr->sect -= 1;
}
}
#endif
/*
* Make sure to redo the checksum.
*/
(void) checkdefsum(list, CK_MAKESUM);
return (0);
}
/*
* This routine either checks or calculates the checksum for a defect
* list, depending on the mode parameter. In check mode, it returns
* whether or not the checksum is correct.
*/
int
checkdefsum(list, mode)
struct defect_list *list;
int mode;
{
register int *lp, i, sum = 0;
/*
* Perform the rolling xor to get what the checksum should be.
*/
lp = (int *)list->list;
for (i = 0; i < (list->header.count *
sizeof (struct defect_entry) / sizeof (int)); i++)
sum ^= *(lp + i);
/*
* If in check mode, return whether header checksum was correct.
*/
if (mode == CK_CHECKSUM)
return (sum != list->header.cksum);
/*
* If in create mode, set the header checksum.
*/
else {
list->header.cksum = sum;
return (0);
}
}
/*
* This routine prints a single defect to stdout in a readable format.
*/
void
pr_defect(def, num)
struct defect_entry *def;
int num;
{
/*
* Make defect numbering look 1 relative.
*/
++num;
/*
* Print out common values.
*/
fmt_print("%4d%8d%7d", num, def->cyl, def->head);
/*
* The rest of the values may be unknown. If they are, just
* print blanks instead. Also, only print length only if bfi is
* known, and assume that a known bfi implies an unknown sect.
*/
if (def->bfi != UNKNOWN) {
fmt_print("%8d", def->bfi);
if (def->nbits != UNKNOWN)
fmt_print("%8d", def->nbits);
} else {
fmt_print(" ");
fmt_print("%8d", def->sect);
fmt_print("%8d", chs2bn(def->cyl, def->head, def->sect));
}
fmt_print("\n");
}
/*
* This routine calculates where in a defect list a given defect should
* be sorted. It returns the index that the defect should become. The
* algorithm used sorts all bfi based defects by cylinder/head/bfi, and
* adds all logical sector defects to the end of the list. This is
* necessary because the ordering of logical sector defects is significant
* when sector slipping is employed.
*/
int
sort_defect(def, list)
struct defect_entry *def;
struct defect_list *list;
{
struct defect_entry *ptr;
/*
* If it's a logical sector defect, return the entry at the end
* of the list.
*/
if (def->bfi == UNKNOWN)
return (list->header.count);
/*
* It's a bfi defect. Loop through the defect list.
*/
for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
/*
* If we get to a logical sector defect, put this defect
* right before it.
*/
if (ptr->bfi == UNKNOWN)
goto found;
/*
* If we get to a defect that is past this one in
* cylinder/head/bfi, put this defect right before it.
*/
if (def->cyl < ptr->cyl)
goto found;
if (def->cyl != ptr->cyl)
continue;
if (def->head < ptr->head)
goto found;
if (def->head != ptr->head)
continue;
if (def->bfi < ptr->bfi)
goto found;
}
found:
/*
* Return the index to put the defect at.
*/
return (ptr - list->list);
}
/*
* This routine writes the defect list on the back on the disk. It also
* writes the bad block table to disk if bad-144 mapping applies to the
* current disk.
*/
void
write_deflist(list)
struct defect_list *list;
{
int size, status, head, sec;
#ifdef sparc
caddr_t bad_ptr = (caddr_t)&badmap;
#endif
assert(!EMBEDDED_SCSI);
/*
* Panther's working list is maintained by the controller
*/
if (cur_ctype->ctype_flags & CF_WLIST) {
(*cur_ops->op_wr_cur)(list);
return;
}
/*
* If the list is null, there is nothing to write.
*/
if (list->list != NULL) {
/*
* Make sure that the defect list is compatible with diag
*/
(void) makelsect(list);
/*
* calculate how many sectors the defect list will occupy.
*/
size = LISTSIZE(list->header.count);
/*
* Loop for each copy of the list to be written. Write
* out the header of the list followed by the data.
*/
for (head = 0; head < LISTCOUNT; head++) {
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
chs2bn(ncyl + 1, head, 0), 1,
(char *)&list->header, F_NORMAL);
if (status) {
err_print(
"Warning: error saving defect list.\n");
continue;
}
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
chs2bn(ncyl + 1, head, 1), size,
(char *)list->list, F_NORMAL);
if (status)
err_print(
"Warning: error saving defect list.\n");
}
}
if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
return;
#ifdef sparc
/*
* Current disk uses bad-144 mapping. Loop for each copy of the
* bad block table to be written and write it out.
*/
for (sec = 0; sec < BAD_LISTCNT * 2; sec += 2) {
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
&badmap, F_NORMAL);
if (status) {
err_print(
"Warning: error saving bad block map table.\n");
continue;
}
}
/*
* Execute an ioctl to tell unix about the new bad block table.
*/
if (ioctl(cur_file, HDKIOCSBAD, &bad_ptr))
err_print(
"Warning: error telling SunOS bad block map table.\n");
#endif
}
/*
* This routine adds a logical sector to the given defect list.
*/
void
add_ldef(blkno, list)
daddr_t blkno;
struct defect_list *list;
{
struct defect_entry def;
int index;
/*
* Calculate the fields for the defect struct.
*/
def.cyl = bn2c(blkno);
def.head = bn2h(blkno);
def.sect = bn2s(blkno);
/*
* Initialize the unknown fields.
*/
def.bfi = def.nbits = UNKNOWN;
/*
* Make this a bfi defect.
*/
(void) makebfi(list, &def);
/*
* Calculate the index into the list that the defect belongs at.
*/
index = sort_defect(&def, list);
/*
* Add the defect to the list.
*/
add_def(&def, list, index);
}
/*
* This routine adds the given defect struct to the defect list at
* a precalculated index.
*/
void
add_def(def, list, index)
struct defect_entry *def;
struct defect_list *list;
int index;
{
int count, i;
/*
* If adding this defect makes the list overflow into another
* sector, allocate the necessary space.
*/
count = list->header.count;
if (LISTSIZE(count + 1) > LISTSIZE(count))
list->list = (struct defect_entry *)rezalloc((void *)list->list,
LISTSIZE(count + 1) * SECSIZE);
/*
* Slip all the defects after this one down one slot in the list.
*/
for (i = count; i > index; i--)
*(list->list + i) = *(list->list + i - 1);
/*
* Fill in the created hole with this defect.
*/
*(list->list + i) = *def;
/*
* Increment the count and calculate a new checksum.
*/
list->header.count++;
(void) checkdefsum(list, CK_MAKESUM);
}
/*
* This routine sets the given defect list back to null.
*/
void
kill_deflist(list)
struct defect_list *list;
{
/*
* If it's already null, we're done.
*/
if (list->list == NULL)
return;
/*
* Free the malloc'd space it's using.
*/
destroy_data((char *)list->list);
/*
* Mark it as null, and clear any flags.
*/
list->list = NULL;
list->flags = 0;
}