snlb.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Sun Disk Label routines.
*/
/*
* Object Management
*/
static opaque_t snlb_create();
/*
* Local Function Prototypes
*/
int part);
int len);
struct dklb_objops snlb_ops = {
0, 0
};
static struct bbh_objops snlb_bbh_ops = {
0, 0
};
/*
* Local static data
*/
#ifdef SNLB_DEBUG
#define DENT 0x0001
#define DERR 0x0002
#define DIO 0x0004
#define DXALT 0x0008
#define DXDEVID 0x0010
#endif /* SNLB_DEBUG */
/*
* This is the driver loadable module wrapper.
*/
char _depends_on[] = "drv/objmgr";
&mod_miscops, /* Type of module */
"Solaris Disk Label Object"
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int err;
if (objmgr_ins_entry("snlb",
return (EINVAL);
(void) objmgr_del_entry("snlb");
return (err);
}
int
_fini(void)
{
#ifdef SNLB_DEBUG
if (snlb_debug & DENT)
PRF("snlb_fini: call\n");
#endif
return (EBUSY);
/* currently objmgr_ins_entry always succeeds */
}
return (err);
}
int
{
}
#ifdef SNLB_DEBUG
void
{
int i;
if (snlb_debug & DIO) {
PRF("DISK PART[%d]: start= 0x%x size= 0x%x\n",
}
}
}
}
#endif
static opaque_t
{
}
static int
{
return (DDI_SUCCESS);
}
static int
{
}
}
/* free the alt sector map data */
return (DDI_SUCCESS);
}
/*
* snlb_reopen:
* The actual work of "opening" the disk label driver is done here.
* Called from snlb_open and from snlb_ioctl when the disk label and
* vtoc need to be re-read.
* Always called with snlbp->s_mutex held.
*/
static int
{
int i;
int uidx;
int lba;
/* free the old alt sector map data */
/* clear the stale label info */
/* let the slice representing the whole disk to become valid */
/* assume no fdisk, thus UNIX fdisk partition start at 0 */
/* read the label */
if (!handle)
return (DDI_FAILURE);
/*
* Check for lba support before verifying sig; sig might not be
* there, say on a blank disk, but the max_chs mark may still
* be present
* First, check for lba-access-ok on root node (searching to prom).
*/
lba = 0;
"lba-access-ok", 0) != 0) {
lba = 1;
} else {
if (mbp) {
}
}
/*
* Next, look for 'no-bef-lba-access' prop on parent.
* Its presence means the realmode driver doesn't support
* LBA, so the target driver shouldn't advertise it as ok.
* This should be a temporary condition; one day all
* BEFs should support the LBA access functions.
*/
DDI_PROP_DONTPASS, "no-bef-lba-access", 0) != 0)) {
/* BEF doesn't support LBA; don't advertise it as ok */
lba = 0;
}
if (lba) {
"lba-access-ok", 0) == 0) {
/* not found; create it */
"?snlb: Can't create lba property "
"for instance %d\n",
}
}
}
/* check label */
/* DOS label missing - go for vtoc in block 1 */
snlb_preplb(snlbp, 0);
return (DDI_SUCCESS);
}
/* have a DOS label - use it */
/* look for a Solaris partition in the DOS mboot record */
uidx = -1;
continue;
if (uidx == -1)
uidx = i;
uidx = i;
}
}
if (uidx == -1) {
/* no UNIX partition, can't slice */
return (DDI_SUCCESS);
}
#ifdef SNLB_DEBUG
if (snlb_debug & DIO) {
PRF("snlb_reopen: FDISK partitions\n");
}
#endif
/* have a solaris partition - get vtoc from it */
return (DDI_SUCCESS);
}
/* max CHS values, as they are encoded into bytes, for 1022/254/63 */
#define LBA_MAX_HEAD (254)
static int
{
}
static int
{
int rc;
return (rc);
}
static void
{
struct scsi_inquiry *inqp;
long disksize;
/*
* If CD-ROM, special-case:
* - add full disk as slices 0 and 2 to the label
*/
/*
* Not heads * sectors * cyls, but the whole thing
* This applies later, to s2, as well.
*/
} else {
/* non-CD-ROM disks */
/* Add boot disk slice as slice 8 to the disk */
/* Add Alternates disk slice as slice 9 to the disk */
}
/* Add full disk slice as slice 2 to the disk */
"DEFAULT cyl %d alt %d hd %d sec %d",
#ifdef SNLB_DEBUG
if (snlb_debug & DIO) {
PRF("snlb_preplb:cyl= %d acyl= %d nhd=%d nsect= %d cap= 0x%x\n",
PRF("snlb_preplb: UNIX slices\n");
}
#endif
}
static int
{
unsigned short sum;
/* read the label */
KM_SLEEP);
if (!handle)
return (DDI_FAILURE);
/* check label */
return (DDI_FAILURE);
}
/* record label information */
/* get alternate sector partition map */
}
return (DDI_SUCCESS);
}
static int
{
short *sp;
short count;
unsigned short sum;
/* Check magic number of the label */
return (DDI_FAILURE);
/* Check the checksum of the label */
sum = 0;
while (count--) {
}
}
static void
{
/* save the disk label in memory */
#ifdef SNLB_DEBUG
if (snlb_debug & DIO) {
PRF("snlb_savelb:cyl= %d acyl= %d nhd=%d nsect= %d cap= 0x%x\n",
PRF("snlb_savelb: UNIX slices\n");
}
#endif
}
static void
{
}
static void
{
}
static void
{
}
/*ARGSUSED4*/
static int
{
int argsiz;
switch (cmd) {
case DKIOCGVTOC:
/* re-read the vtoc from disk */
return (ENXIO);
}
case DDI_MODEL_ILP32:
return (EFAULT);
break;
case DDI_MODEL_NONE:
return (EFAULT);
break;
}
return (0);
case DKIOCSVTOC:
/*
* Get the vtoc from the caller, write it onto the disk
*/
case DDI_MODEL_ILP32:
return (EFAULT);
break;
case DDI_MODEL_NONE:
return (EFAULT);
break;
}
/*
* If already have a devid, write it on the disk
* If we don't, create one and write it.
*/
}
} else {
}
break;
case DKIOCG_PHYGEOM:
{
return (EFAULT);
else
return (0);
}
case DKIOCG_VIRTGEOM:
{
/*
* If the controller returned us something that doesn't
* really fit into an Int 13/function 8 geometry
* result, just fail the ioctl. See PSARC 1998/313.
*/
return (EINVAL);
return (EFAULT);
else
return (0);
}
/* Return the geometry of the specified unit. */
case DKIOCGGEOM:
/* re-read the vtoc from disk */
return (ENXIO);
}
return (EFAULT);
else
return (0);
/* Set the geometry of the specified unit. */
case DKIOCSGEOM:
return (EFAULT);
return (0);
case DKIOCGAPART:
/* re-read the vtoc from disk */
return (ENXIO);
}
case DDI_MODEL_ILP32: {
int i;
}
return (EFAULT);
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
return (0);
case DKIOCSAPART:
case DDI_MODEL_ILP32: {
int i;
return (EFAULT);
}
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
}
return (0);
case DKIOCADDBAD:
/* snlb_get_altsctr(snlbp); */
/* re-read the vtoc from disk */
return (ENXIO);
}
return (0);
default:
break;
}
return (status);
}
static void
{
struct dkl_partition *pp;
int i;
int spc;
}
#ifdef SNLB_DEBUG
PRF("snlb_getmap: UNIX slices\n");
#endif
}
static void
{
struct dkl_partition *pp;
int i;
int spc;
}
#ifdef SNLB_DEBUG
#endif
}
static int
{
int status;
int backup_block;
int count;
KM_SLEEP);
if (!handle)
return (ENOMEM);
/* check label */
if (lbp) {
status = 0;
} else {
}
return (status);
/* DO backup copies of vtoc */
if (!handle)
return (ENOMEM);
handle);
if (!lbp)
return (EIO);
backup_block += 2;
}
return (0);
}
static void
{
struct dkl_partition *pp;
} else {
}
}
static void
{
(sizeof (long) + sizeof (struct alts_ent *))));
}
}
static int
{
struct dkl_partition *pp;
struct alts_parttbl *ap;
int i;
long alts_ent_used;
break;
}
if (i >= SNDKMAP)
return (DDI_FAILURE);
if (!hdl_altspart)
return (DDI_FAILURE);
return (DDI_FAILURE);
}
if (!hdl_enttbl)
return (DDI_FAILURE);
if (!enttblp) {
return (DDI_FAILURE);
}
/*
* If we bail out before this point, the old alternate map, if it
* ever existed, is still intact.
* Now its time to destroy the old map and create a new one.
*/
}
return (DDI_SUCCESS);
}
static void
{
struct dkl_partition *pp;
int i, j;
for (i = 0; i < SNDKMAP; i++) {
sdp->s_alts_altcount[i] = 0;
continue;
if (i == SNUSLICE_WHOLE) {
continue;
}
for (j = 0; j < sdp->s_alts_entused; j++) {
/*
* if bad sector cluster is less than partition range
* then skip
*/
continue;
/*
* if bad sector cluster passed the end of the
* partition then stop
*/
break;
if (!sdp->s_alts_firstalt[i]) {
sdp->s_alts_firstalt[i] =
}
sdp->s_alts_altcount[i]++;
}
}
}
/*ARGSUSED*/
static bbh_cookie_t
{
struct bbh_handle *hp;
return (ckp);
}
/*ARGSUSED*/
static void
{
struct bbh_handle *hp;
}
/*
* snlb_bbh_gethandle remaps the bad sectors to alternates.
* There are 7 different cases when the comparison is made
* between the bad sector cluster and the disk section.
*
* bad sector cluster gggggggggggbbbbbbbggggggggggg
* case 1: ddddd
* case 2: -d-----
* case 3: ddddd
* case 4: dddddddddddd
* case 5: ddddddd-----
* case 6: ---ddddddd
* case 7: ddddddd
*
* where: g = good sector, b = bad sector
* d = sector in disk section
* - = disk section may be extended to cover those disk area
*/
static opaque_t
{
struct bbh_handle *hp;
struct bbh_cookie *ckp;
long alts_used;
long d_count;
int i;
int idx;
int cnt;
return (NULL);
return (NULL);
}
/*
* binary search for the largest bad sector index in the alternate
* entry table which overlaps or larger than the starting d_sec
*/
/* if starting sector is > the largest bad sector, return */
if (i == -1) {
return (NULL);
}
/* i is the starting index. Set altp to the starting entry addr */
altp += i;
/* calculate the number of bad sectors */
break;
}
if (!cnt) {
return (NULL);
}
/* calculate the maximum number of reserved cookies */
cnt <<= 1;
cnt++;
/* allocate the handle */
altp += i;
/* CASE 1: */
break;
/* CASE 3: */
continue;
/* CASE 2 and 7: */
#ifdef SNLB_DEBUG
if (snlb_debug & DXALT) {
printf("snlb_bbh_gethandle: CASE 2 & 7 \n");
}
#endif
break;
}
/* at least one bad sector in our section. break it. */
/* CASE 5: */
#ifdef SNLB_DEBUG
if (snlb_debug & DXALT) {
printf("snlb_bbh_gethandle: CASE 5 \n");
}
#endif
break;
}
/* CASE 6: */
#ifdef SNLB_DEBUG
if (snlb_debug & DXALT) {
printf("snlb_bbh_gethandle: CASE 6 \n");
}
#endif
idx++;
continue; /* check rest of section */
}
/* CASE 4: */
#ifdef SNLB_DEBUG
if (snlb_debug & DXALT) {
printf("snlb_bbh_gethandle: CASE 4\n");
}
#endif
idx += 2;
}
#ifdef SNLB_DEBUG
if (snlb_debug & DXALT) {
PRF("snlb_bbh_gethandle: [%d]", i);
PRF(" sector= %d count= %d \n",
}
}
#endif
}
static int
{
int i;
int ind;
int interval;
int mystatus = -1;
if (!cnt)
return (mystatus);
ind = i;
return (ind-1);
} else {
interval >>= 1;
/* record the largest bad sector index */
if (!interval)
break;
} else {
/*
* if key is larger than the last element
* then break
*/
break;
}
}
}
return (mystatus);
}
/*
* Create (if necessary) and register the devid.
* There are 4 different ways we can get a device id:
* 1. Already have one - nothing to do
* 2. Build one from the drive's model and serial numbers
* 3. Read one from the disk (first sector of last track)
* 4. Fabricate one and write it on the disk.
* If any of these succeeds, register the deviceid
* Must be holding snlbp->s_mutex when this routine is called.
*/
void
{
int rc;
/* Try options until one succeeds, or all have failed */
/* 1. All done if already registered */
return;
/* 2. Build a devid from the model and serial number, if present */
if (rc != DDI_SUCCESS) {
/* Can't read or write the devid if no unix partition */
if (!have_unix_partition)
return;
/* 3. Read devid from the disk, if present */
/* 4. otherwise make one up and write it on the disk */
if (rc != DDI_SUCCESS)
}
/* If we managed to get a devid any of the above ways, register it */
if (rc == DDI_SUCCESS)
}
/*
* Test for a valid model or serial number. Assume that a valid representation
* contains at least one character that is neither a space, 0 digit, or NULL.
* Trim trailing blanks and NULLS from returned length.
*/
static void
{
char ch;
int i;
int tb;
tb++;
else
tb = 0;
}
/* Atleast one non 0 or blank character. */
} else {
*retlen = 0;
}
}
/*
* Build a devid from the model and serial number, if present
* Return DDI_SUCCESS or DDI_FAILURE.
*/
static int
{
int rc = DDI_SUCCESS;
char *hwid;
int amodel_len;
int model_len;
int aserno_len;
int serno_len;
int total_len;
/*
* Initialize the model and serial number information.
* First make the ioctl calls to get the required lengths,
* then allocate and make ioctl calls to get the information.
*/
amodel_len + 1);
aserno_len + 1);
/* Verify the model and serial number */
if (model_len == 0) {
rc = DDI_FAILURE;
goto out;
}
if (serno_len == 0) {
rc = DDI_FAILURE;
goto out;
}
/*
* The device ID will be concatenation of the model number,
* the '=' separator, the serial number. Allocate
* the string and concatenate the components.
*/
/* Initialize the device ID, trailing NULL not included */
/* Free the allocated string */
if (rc == DDI_SUCCESS)
if (serno)
return (rc);
}
/*
* Read a devid from on the first block of the last track of
* the last cylinder. Make sure what we read is a valid devid.
* Return DDI_SUCCESS or DDI_FAILURE.
*/
static int
{
int chksum;
int i, sz;
if (blk < 0)
return (DDI_FAILURE);
/* read the devid */
if (!handle)
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/* Validate the revision */
return (DDI_FAILURE);
}
/* Calculate the checksum */
chksum = 0;
for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
/* Compare the checksums */
return (DDI_FAILURE);
}
/* Validate the device id */
return (DDI_FAILURE);
}
/* keep a copy of the device id */
return (DDI_SUCCESS);
}
/*
* Write a devid on the first block of the last track of the last cylinder.
* Return DDI_SUCCESS or DDI_FAILURE.
* Must be holding snlbp->s_mutex when this routine is called.
*/
static int
{
int i;
if (blk < 0)
return (DDI_FAILURE);
if (!handle)
return (DDI_FAILURE);
/* Locate the buffer */
/* Fill in the revision */
/* Copy in the device id */
/* Calculate the chksum */
chksum = 0;
for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
/* Fill in the checksum */
/* write the devid */
return (DDI_SUCCESS);
}
/*
* Create a devid and write it on the first block of the last track of
* the last cylinder.
* Return DDI_SUCCESS or DDI_FAILURE.
* Must be holding snlbp->s_mutex when this routine is called.
*/
static int
{
int i;
int rc;
if (rc != DDI_SUCCESS)
return (rc);
if (blk < 0)
return (DDI_FAILURE);
if (!handle)
return (DDI_FAILURE);
/* Locate the buffer */
/* Fill in the revision */
/* Copy in the device id */
/* Calculate the chksum */
chksum = 0;
for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
/* Fill in the checksum */
/* write the devid */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Compute the block number where the devid is (to be) stored
* on the disk. Return block number, or -1 if can't compute.
* The "rw" string is for debugging printfs only.
* Must be holding snlbp->s_mutex when this routine is called.
*/
static daddr_t
{
return (-1);
/* Next to last cylinder is used */
#ifdef SNLB_DEBUG
if (snlb_debug & DXDEVID) {
PRF("devid: start %d, capacity %d, devid block %d\n",
}
#endif
return (blk);
}
/*
* Make an ioctl call to get a string from the actual disk driver.
* This routine serves two purposes:
* 1. Called with a NULL buf argument to get the length required to
* hold the string
* 2. Called with a buffer to get the string
*/
static int
{
char fakebuf[2];
int dummyrval;
} else {
}
}