/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains routines that manipulate the defect list.
*/
#include "global.h"
#include <sys/types.h>
#include <sys/param.h>
#if defined(sparc)
#include <sys/hdio.h>
#endif /* defined(sparc) */
#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>
#if defined(sparc)
#include <sys/dkbad.h>
#endif /* defined(sparc) */
#include "misc.h"
#include "param.h"
#if defined(sparc)
/*
* This structure is the bad block table for the current disk if
* the disk uses bad-144 defect mapping.
*/
struct dkbad badmap;
#endif /* defined(sparc) */
/*
* 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(struct defect_list *list)
{
int size, head;
#if defined(sparc)
int sec, status;
struct bt_bad *bt;
#endif /* defined(sparc) */
assert(!EMBEDDED_SCSI);
/*
* This flags has been introduced only for Sparc ATA IDE.
* This indicates that no list manipulation is done in this controller
* and hence return without any other checking.
*/
if (cur_ctype->ctype_flags & CF_NOWLIST) {
return;
}
/*
* Panther's working list is maintained by the controller
*/
if (cur_ctype->ctype_flags & CF_WLIST) {
if (*cur_ops->op_ex_cur != NULL &&
((*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 &&
((*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,
(diskaddr_t)chs2bn(ncyl + 1, head, 0), 1,
(char *)&list->header, NULL), 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 = deflist_size(cur_blksz, list->header.count);
list->list = (struct defect_entry *)zalloc(size * cur_blksz);
/*
* 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,
(diskaddr_t)chs2bn(ncyl + 1, head, 1), size,
(char *)list->list, F_NORMAL, NULL) ||
checkdefsum(list, CK_CHECKSUM)) {
/*
* Destroy the list and go on.
*/
kill_deflist(list);
continue;
}
/*
* Got a good copy, stop searching.
*/
break;
}
#if defined(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 < nsect)); sec += 2) {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file,
(diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
&badmap, F_NORMAL, NULL);
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 /* defined(sparc) */
}
/*
* 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(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(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("%8llu", 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(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(struct defect_list *list)
{
int size, head, status;
#if defined(sparc)
int sec;
caddr_t bad_ptr = (caddr_t)&badmap;
#endif /* defined(sparc) */
assert(!EMBEDDED_SCSI);
/*
* Sparc ATA IDE.
* This indicates that no list manipulation is done in this controller
* and hence return without any other checking.
*/
if (cur_ctype->ctype_flags & CF_NOWLIST) {
return;
}
/*
* 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) {
/*
* calculate how many sectors the defect list will occupy.
*/
size = deflist_size(cur_blksz, 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,
(diskaddr_t)chs2bn(ncyl + 1, head, 0), 1,
(char *)&list->header, F_NORMAL, NULL);
if (status) {
err_print(
"Warning: error saving defect list.\n");
continue;
}
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
(diskaddr_t)chs2bn(ncyl + 1, head, 1), size,
(char *)list->list, F_NORMAL, NULL);
if (status)
err_print(
"Warning: error saving defect list.\n");
}
}
if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
return;
#if defined(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 < nsect)); sec += 2) {
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
(diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
&badmap, F_NORMAL, NULL);
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 /* defined(sparc) */
}
/*
* This routine adds a logical sector to the given defect list.
*/
void
add_ldef(diskaddr_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;
/*
* 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(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 (deflist_size(cur_blksz, count + 1) > deflist_size(cur_blksz, count))
list->list = (struct defect_entry *)rezalloc((void *)list->list,
deflist_size(cur_blksz, count + 1) * cur_blksz);
/*
* 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(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;
}
/*
* This routine returns the defect list size
* according to the sector size.
*/
int
deflist_size(int secsz, int sz)
{
int rval;
if (secsz == 0) {
secsz = SECSIZE;
}
rval = sz ? ((sz * sizeof (struct defect_entry) +
secsz - 1) / secsz) : 1;
return (rval);
}