scsi_confsubr.c revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6
/*
* 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
* 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.
*/
/*
* Utility SCSI configuration routines
*/
/*
* Many routines in this file have built in parallel bus assumption
* which might need to change as other interconnect evolve.
*/
/*
* macro for filling in lun value for scsi-1 support
*/
}
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"SCSI Bus Utility Routines"
};
static struct modlinkage modlinkage = {
};
static void create_inquiry_props(struct scsi_device *);
static int get_inquiry_prop_len(char *, size_t);
static int scsi_check_ss2_LUN_limit(struct scsi_device *);
static void scsi_establish_LUN_limit(struct scsi_device *);
static void scsi_update_parent_ss2_prop(dev_info_t *, int, int);
int (*callback)(), int *, int *);
/*
* this int-array HBA-node property keeps track of strictly SCSI-2
* target IDs
*/
#define SS2_LUN0_TGT_LIST_PROP "ss2-targets"
/*
* for keeping track of nodes for which we do *NOT* want to probe above LUN 7
* (i.e. strict SCSI-2 targets)
*
* note that we could also keep track of dtype (SCSI device type) and
* ANSI (SCSI standard conformance level), but all currently-known cases of
* this problem are on SCSI-2 PROCESSOR device types
*/
typedef struct ss2_lun0_info {
const char *sli_vid; /* SCSI inquiry VID */
const char *sli_pid; /* SCSI inquiry PID */
const char *sli_rev; /* SCSI inquiry REV */
/*
* these two workarounds are for the SCSI-2 GEM2* chips used in the
* D1000 and D240
*/
#define SES_D1000_VID "SYMBIOS"
#define SES_D1000_REV "2"
#define SES_D240_VID "SUN"
#define SES_D240_REV "2"
/*
* a static list of targets where we do *not* want to probe above LUN 7
*/
static const ss2_lun0_info_t scsi_probe_strict_s2_list[] = {
};
static const int scsi_probe_strict_s2_size =
sizeof (scsi_probe_strict_s2_list) / sizeof (struct ss2_lun0_info);
#ifdef DEBUG
int scsi_probe_debug = 0;
#define SCSI_PROBE_DEBUG0(l, s) \
if (scsi_probe_debug >= (l)) printf(s)
#define SCSI_PROBE_DEBUG1(l, s, a1) \
#else /* DEBUG */
#define SCSI_PROBE_DEBUG0(l, s)
#define SCSI_PROBE_DEBUG1(l, s, a1)
#endif /* DEBUG */
/*
* architecture dependent allocation restrictions. For x86, we'll set
* dma_attr_addr_hi to scsi_max_phys_addr and dma_attr_sgllen to
* scsi_sgl_size during _init().
*/
#if defined(__sparc)
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0xFFFFFFFFull, /* high DMA address range */
0xFFFFFFFFull, /* DMA counter register */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
1, /* s/g list length */
512, /* granularity of device */
0 /* DMA transfer flags */
};
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0x0, /* high DMA address range [set in _init()] */
0xFFFFull, /* DMA counter register */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
0, /* s/g list length */
512, /* granularity of device [set in _init()] */
0 /* DMA transfer flags */
};
int scsi_sgl_size = 0xFF;
#endif
int
_init()
{
#if defined(__x86)
/* set the max physical address for iob allocs on x86 */
/*
* set the sgllen for iob allocs on x86. If this is set less than
* the number of pages the buffer will take (taking into account
* alignment), it would force the allocator to try and allocate
* contiguous pages.
*/
#endif
/* bitmap to limit scsi_pkt allocation violation messages */
return (mod_install(&modlinkage));
}
/*
* there is no _fini() routine because this module is never unloaded
*/
int
{
}
static int
{
int rval = SCSIPROBE_EXISTS;
/*
* prepare rqsense packet
*/
goto out;
}
else
goto out;
}
/*
* The controller type is as yet unknown, so we
* have to do a throwaway non-extended request sense,
* and hope that that clears the check condition
* for that unit until we can find out what kind
* of drive it is. A non-extended request sense
* is specified by stating that the sense block
* has 0 length, which is taken to mean that it
* is four bytes in length.
*/
}
out:
if (rq_pkt) {
}
if (rq_bp) {
}
return (rval);
}
/*
*
* SCSI slave probe routine - provided as a service to target drivers
*
* Mostly attempts to allocate and fill sd inquiry data..
*/
int
{
int rval = SCSIPROBE_EXISTS;
/*
* the first test unit ready will tell us whether a target
* responded and if there was one, it will clear the unit attention
* condition
*/
return (SCSIPROBE_NOMEM_CB);
}
SCMD_TEST_UNIT_READY, 0, 0, 0);
else
/*
* scanner and processor devices can return a
* check condition here
*/
}
if (rval != SCSIPROBE_EXISTS) {
return (rval);
}
}
/*
* the second test unit ready, allows the host adapter to negotiate
* synchronous transfer period and offset
*/
else
}
/*
* do a rqsense if there was a check condition and ARQ was not done
*/
}
}
/*
* call scsi_probe to do the inquiry
*
* NOTE: there is minor difference with the old scsi_slave
* implementation: busy conditions are not handled in scsi_probe.
*/
if (rval == SCSIPROBE_EXISTS) {
} else {
return (rval);
}
}
/*
* Undo scsi_slave - older interface, but still supported
*
* as part of free of scsi_device(9S).
*/
/*ARGSUSED*/
void
{
}
/*
* Undo scsi_probe
*
* as part of free of scsi_device(9S).
*/
/*ARGSUSED*/
void
{
}
/*
* This is like scsi_poll, but only does retry for TRAN_BUSY.
*/
static int
{
int rval = -1;
int wait_usec;
int rc;
extern int do_polled_io;
}
/*
* Each TRAN_BUSY response waits scsi_test_busy_delay usec up to a
* maximum of scsi_test_busy_timeout.
*/
/* Initialize pkt status variables */
(scsi_test_busy_timeout == 0))
break;
/* transport busy, wait */
} else {
/* we busy wait during cpr_dump or interrupt threads */
}
}
if (rc != TRAN_ACCEPT) {
goto exit;
goto exit;
goto exit;
rval = 0;
} else {
rval = 0;
}
exit:
return (rval);
}
/*
* The implementation of scsi_probe now allows a particular
* HBA to intercept the call, for any post- or pre-processing
* it may need. The default, if the HBA does not override it,
* is to call scsi_hba_probe(), which retains the old functionality
* intact.
*/
int
{
int ret;
if (scsi_check_ss2_LUN_limit(sd) != 0) {
/*
* caller is trying to probe a strictly-SCSI-2 device
* with a LUN that is too large, so do not allow it
*/
return (SCSIPROBE_NORESP); /* skip probing this one */
}
} else {
}
if (ret == SCSIPROBE_EXISTS) {
/* is this a strictly-SCSI-2 node ?? */
}
return (ret);
}
/*
* probe scsi device using any available path
*
*/
int
{
}
/*
* probe scsi device using specific path
*
* scsi_hba_probe_pi does not do any test unit ready's which access the medium
* and could cause busy or not ready conditions.
* scsi_hba_probe_pi does 2 inquiries and a rqsense to clear unit attention
* and to allow sync negotiation to take place
* finally, scsi_hba_probe_pi does one more inquiry which should
* reliably tell us what kind of target we have.
* A scsi-2 compliant target should be able to return inquiry with 250ms
* and we actually wait more than a second after reset.
*/
int
{
int rval = SCSIPROBE_NOMEM;
int (*cb_flag)();
int pass = 1;
KM_SLEEP : KM_NOSLEEP));
goto out;
}
}
} else {
}
goto out;
}
goto out;
}
SCMD_INQUIRY, 0, SUN_INQSIZE, 0);
/*
* set transport path
*/
}
/*
* the first inquiry will tell us whether a target
* responded
*
* The FILL_SCSI1_LUN below will find "ansi_ver != 1" on first pass
* because of bzero initilization. If this assumption turns out to be
* incorrect after we have real sd_inq data (for lun0) we will do a
* second pass during which FILL_SCSI1_LUN will place lun in CDB.
*/
goto out;
} else {
/*
* retry one more time
*/
goto out;
}
}
}
/*
* if we are lucky, this inquiry succeeded
*/
goto done;
}
/*
* the second inquiry, allows the host adapter to negotiate
* synchronous transfer period and offset
*/
else
goto out;
}
/*
* if target is still busy, give up now
*/
goto out;
}
/*
* do a rqsense if there was a check condition and ARQ was not done
*/
/*
* prepare rqsense packet
* there is no real need for this because the
* check condition should have been cleared by now.
*/
goto out;
}
NULL);
goto out;
}
/*
* set transport path
*/
}
/*
* The FILL_SCSI1_LUN above will find "inq_ansi != 1"
* on first pass, see "again" comment above.
*
* The controller type is as yet unknown, so we
* have to do a throwaway non-extended request sense,
* and hope that that clears the check condition for
* that unit until we can find out what kind of drive
* it is. A non-extended request sense is specified
* by stating that the sense block has 0 length,
* which is taken to mean that it is four bytes in
* length.
*/
goto out;
}
}
}
/*
* At this point, we are guaranteed that something responded
* to this scsi bus target id. We don't know yet what
* kind of device it is, or even whether there really is
* a logical unit attached (as some SCSI target controllers
* lie about a unit being ready, e.g., the Emulex MD21).
*/
goto out;
}
goto out;
}
/*
* Okay we sent the INQUIRY command.
*
* If enough data was transferred, we count that the
* Inquiry command succeeded, else we have to assume
* that this is a non-CCS scsi target (or a nonexistent
*/
/*
* try a request sense if we have a pkt, otherwise
* just retry the inquiry one more time
*/
if (rq_pkt) {
}
/*
* retry inquiry
*/
goto out;
}
goto out;
}
}
done:
/*
* If we got a parity error on receive of inquiry data,
* we're just plain out of luck because we told the host
* adapter to not watch for parity errors.
*/
} else {
}
out:
/*
* If lun > 0 we need to figure out if this is a scsi-1 device where
* the "real" lun needs to be embedded into the cdb.
*/
pass++;
goto again;
/*
* invalid lun for scsi-1,
* return probe failure.
*/
}
if (rq_pkt) {
}
if (inq_pkt) {
}
if (rq_bp) {
}
if (inq_bp) {
}
return (rval);
}
/*
* Convert from a scsi_device structure pointer to a scsi_hba_tran structure
* pointer. The correct way to do this is
*
* #define DEVP_TO_TRAN(sd) ((sd)->sd_address.a_hba_tran)
*
* however we have some consumers that place their own vector in a_hba_tran. To
* avoid problems, we implement this using the sd_tran_safe. See
* scsi_hba_initchild for more details.
*/
/*
* Function, callable from SCSA framework, to get 'human' readable REPORTDEV
* addressing information from scsi_device properties.
*/
int
{
/* use deprecated tran_get_bus_addr interface if it is defined */
/* NOTE: tran_get_bus_addr is a poor name choice for interface */
}
/*
* Function, callable from HBA driver's tran_get_bus_addr(9E) implementation,
* to get standard form of human readable REPORTDEV addressing information
* from scsi_device properties.
*/
int
{
char *tgt_port;
/* get device unit-address properties */
SCSI_ADDR_PROP_TARGET, -1);
return (0); /* no target */
SCSI_ADDR_PROP_LUN, 0);
SCSI_ADDR_PROP_SFUNC, -1);
/*
* XXX should the default be to print this in decimal for
* "human readable" form, so it matches conf files?
*/
if (tgt_port) {
if (sfunc == -1)
"%s %s lun %" PRIx64,
else
} else {
if (sfunc == -1)
"%s %x lun %" PRIx64,
else
}
return (1);
}
/*
* scsi_ua_get: using properties, return "unit-address" string.
* Called by SCSA framework, may call HBAs tran function.
*/
int
{
char *eua;
/* See if we already have established the unit-address. */
return (1);
}
/* Use deprecated tran_get_name interface if it is defined. */
/* NOTE: tran_get_name is a poor name choice for interface */
/* Use generic property implementation */
}
/*
* scsi_hba_ua_get: using properties, return "unit-address" string.
* This function may be called from an HBAs tran function.
*
* Function to get "unit-address" in "name@unit-address" /devices path
* component form from the scsi_device unit-address properties on a node.
*
* NOTE: This function works in conjunction with scsi_hba_ua_set().
*/
int
{
char *tgt_port;
/* get device unit-address properties */
SCSI_ADDR_PROP_TARGET, -1);
return (0); /* no target */
SCSI_ADDR_PROP_LUN, 0);
SCSI_ADDR_PROP_SFUNC, -1);
if (tgt_port) {
if (sfunc == -1)
else
} else {
if (sfunc == -1)
else
}
return (1);
}
void
{
/*
* Create the following properties:
*
* inquiry-vendor-id Vendor id (INQUIRY data bytes 8-15)
* inquiry-product-id Product id (INQUIRY data bytes 16-31)
* inquiry-revision-id Product Rev level (INQUIRY data bytes 32-35)
*
* Note we don't support creation of these properties for scsi-1
* devices (as the vid, pid and revision were not defined) and we
* don't create the property if they are of zero length when
* stripped of Nulls and spaces.
*/
(void) scsi_device_prop_update_inqstring(sd,
(void) scsi_device_prop_update_inqstring(sd,
(void) scsi_device_prop_update_inqstring(sd,
}
}
/*
* Create 'inquiry' string properties. An 'inquiry' string gets special
* treatment to trim trailing blanks (etc) and ensure null termination.
*/
int
{
int ilen;
char *data_string;
int rv;
if (ilen <= 0)
return (DDI_PROP_INVAL_ARG);
/* ensure null termination */
return (rv);
}
/*
* This routine returns the true length of the inquiry properties that are to
* be created by removing the padded spaces at the end of the inquiry data.
* This routine was designed for trimming spaces from the vid, pid and revision
* which are defined as being left aligned. In addition, we return 0 length
* if the property is full of all 0's or spaces, indicating to the caller that
* the device was not ready to return the proper inquiry data as per note 65 in
* the scsi-2 spec.
*/
static int
{
int retval;
int trailer;
char *p;
/*
* The vid, pid and revision are left-aligned ascii fields within the
* inquiry data. Here we trim the end of these fields by discounting
* length associated with trailing spaces or NULL bytes. The remaining
* bytes shall be only graphics codes - 0x20 through 0x7e as per the
* scsi spec definition. If we have all 0's or spaces, we return 0
* length. For devices that store inquiry data on the device, they
* can return 0's or spaces in these fields until the data is avail-
* able from the device (See NOTE 65 in the scsi-2 specification
* around the inquiry command.) We don't want to create a property in
* the case of a device not able to return valid data.
*/
trailer = 1;
if (trailer) {
if ((*p == ' ') || (*p == '\0')) {
retval--;
continue;
}
trailer = 0;
}
/* each char must be within 0x20 - 0x7e */
if (*p < 0x20 || *p > 0x7e) {
retval = -1;
break;
}
}
return (retval);
}
/*
* Interfaces associated with SCSI_HBA_ADDR_COMPLEX
* per-scsi_device HBA private data support.
*/
struct scsi_device *
{
}
void
{
}
void *
{
return (sd->sd_hba_private);
}
/*
* probed *may* be a request to probe a strictly SCSI-2 target (with respect
* to LUNs) -- and this probe may be for a LUN number greater than 7,
* which can cause a hardware hang
*
* return 0 if the probe can proceed,
*/
static int
{
dev_info_t *pdevi =
int ret_val = 0; /* default return value */
int i;
/*
* check for what *might* be a problem probe, only we don't
*/
return (0); /* okay to probe this target */
}
/*
* this *might* be a problematic probe, so look to see
* if the inquiry data matches
*/
"SCSA pre-probe: scanning parent node name: %s ...\n",
/*
* look for a special property of our parent node that lists
* the targets under it for which we do *NOT* want to probe
* if LUN>7 -- if the property is found, look to see if our
* target ID is on that list
*/
/*
* no list, so it must be okay to probe this target.LUN
*/
"SCSA pre-probe: NO parent prop found\n");
} else {
for (i = 0; i < tgt_nelements; i++) {
/*
* we found a match, which means we do *NOT*
* want to probe the specified target.LUN
*/
ret_val = 1;
break;
}
}
#ifdef DEBUG
if (ret_val == 1) {
"SCSA pre-probe: marker node FOUND for "
"tgt.LUN=%d.%d, so SKIPPING it\n",
} else {
"SCSA pre-probe: NO marker node found"
" -- OK to probe\n");
}
#endif
}
return (ret_val);
}
/*
* this routine is called from near the end of scsi_probe(),
* to see if the just-probed node is on our list of strictly-SCSI-2 nodes,
* and if it is we mark our parent node with this information
*/
static void
{
int i;
const ss2_lun0_info_t *p;
int bad_target_found = 0;
/*
* if this inquiry data shows that we have a strictly-SCSI-2 device
* at LUN 0, then add it to our list of strictly-SCSI-2 devices,
* so that we can avoid probes where LUN>7 on this device later
*/
/*
* this can't possibly be a node we want to look at, since
* either LUN is greater than 0, target is greater than or
* equal to 16, device type
* is not processor, or SCSI level is not SCSI-2,
* so don't bother checking for a strictly SCSI-2
* (only 8 LUN) target
*/
return; /* don't care */
}
/*
* we have a node that has been probed that is: LUN=0, target<16,
* PROCESSOR-type SCSI target, and at the SCSI-2 level, so
* check INQ properties to see if it's in our list of strictly
* SCSI-2 targets
*
* comparison
*/
goto dun;
}
goto dun;
}
goto dun;
}
/*
* now that we have the INQUIRY properties from the device node,
* compare them with our known offenders
*
* Note: comparison is *CASE* *SENSITIVE*
*/
for (i = 0; i < scsi_probe_strict_s2_size; i++) {
p = &scsi_probe_strict_s2_list[i];
/*
* we found a match -- do NOT want to probe this one
*/
"SCSA post-probe: recording strict SCSI-2 node "
/*
* so we can find out about this node later
*/
bad_target_found = 1;
break;
}
}
/*
* either add remove target number from parent property
*/
dun:
}
}
}
}
/*
* update the parent node to add in the supplied tgt number to the target
* list property already present (if any)
*
* since the target list can never be longer than 16, and each target
* number is also small, we can save having to alloc memory by putting
* a 16-byte array on the stack and using it for property memory
*
* if "add_tgt" is set then add the target to the parent's property, else
* remove it (if present)
*/
static void
{
int i;
int update_result;
"SCSA post-probe: updating parent=%s property to %s tgt=%d\n",
if (add_tgt) {
/*
* we found an existing property -- we might need
* to add to it
*/
for (i = 0; i < nelements; i++) {
/* target already in list */
" tgt %d already listed\n", tgt);
return;
}
}
/*
* need to append our target number to end of list
* (no need sorting list, as it's so short)
*/
/*
* will this new entry fit ?? -- it should, since
* the array is 16-wide and only keep track of
* 16 targets, but check just in case
*/
if (new_nelements >= NTARGETS_WIDE) {
"internal error: no room "
"for more targets?\n");
return;
}
/* copy existing list then add our tgt number to end */
} else {
/*
* we need to remove our target number from the list,
* so copy all of the other target numbers,
* skipping ours
*/
int tgt_removed = 0;
new_nelements = 0;
for (i = 0; i < nelements; i++) {
tgt_list[i];
} else {
/* skip this target */
tgt_removed++;
}
}
if (!tgt_removed) {
" no need to remove tgt %d\n", tgt);
return;
}
}
} else {
/*
* no property yet
*/
if (add_tgt) {
/*
* create a property with just our tgt
*/
} else {
/*
* no list so no need to remove tgt from that list
*/
return;
}
}
#ifdef DEBUG
/*
*/
if (update_result != DDI_PROP_SUCCESS) {
} else {
if (add_tgt) {
"SCSA post-probe: added tgt=%d to parent "
"prop=\"%s\" (now %d entries)\n",
} else {
"SCSA post-probe: removed tgt=%d from parent "
"prop=\"%s\" (now %d entries)\n",
}
}
#endif
}
/* XXX BEGIN: find a better place for this: inquiry.h? */
/*
* Definitions used by device id registration routines
*/
/* size for devid inquiries */
#define MAX_INQUIRY_SIZE 0xF0
/* XXX END: find a better place for these */
/*
* Decorate devinfo node with identity properties using information obtained
* from device. These properties are used by device enumeration code to derive
* the devid, and guid for the device. These properties are also used to
* determine if a device should be enumerated under the physical HBA (PHCI) or
* the virtual HBA (VHCI, for mpxio support).
*
* Return zero on success. If commands that should succeed fail or allocations
* fail then return failure (non-zero). It is possible for this function to
* return success and not have decorated the node with any additional identity
* information if the device correctly responds indicating that they are not
* supported. When failure occurs the caller should consider not making the
* device accessible.
*/
int
{
int rval;
/* find out what pages are supported by device */
return (-1);
/* if available, collect page 80 data and add as property */
if (pg80) {
rval = -1;
goto out;
}
if (rval)
goto out; /* should have worked */
"failed to add page80 prop");
rval = -1;
goto out;
}
}
/* if available, collect page 83 data and add as property */
if (pg83) {
rval = -1;
goto out;
}
if (rval)
goto out; /* should have worked */
"failed to add page83 prop");
rval = -1;
goto out;
}
}
/* Commands worked, identity information that exists has been added. */
rval = 0;
/* clean up resources */
return (rval);
}
/*
* Send an INQUIRY command with the EVPD bit set and a page code of 0x00 to
* the device, returning zero on success. Returned INQUIRY data is used to
* determine which vital product pages are supported. The device idenity
* fails the EVPD inquiry then no pages are supported but the call succeeds.
* Return -1 (failure) if there were memory allocation failures or if a
* command faild that should have worked.
*/
static int
{
int counter;
int rval;
/* pages are not supported */
*ppg80 = 0;
*ppg83 = 0;
/*
* We'll set the page length to the maximum to save figuring it out
* with an additional call.
*/
return (-1); /* memory allocation problem */
/* issue page 0 (Supported VPD Pages) INQUIRY with evpd set */
/*
* Now we must validate that the device accepted the command (some
* devices do not support it) and if the idenity pages we are
* interested in are supported.
*/
if ((rval == 0) &&
/* Loop to find one of the 2 pages we need */
/*
* Pages are returned in ascending order, and 0x83 is the
* last page we are hoping to find.
*/
VPD_HEAD_OFFSET))) {
/*
* Add 3 because page_list[3] is the number of
* pages minus 3
*/
case 0x80:
*ppg80 = 1;
break;
case 0x83:
*ppg83 = 1;
break;
}
counter++;
}
}
return (0);
}
/*
* Send INQUIRY command with specified EVPD and page code. Return
* zero on success. On success, the amount of data transferred
* is returned in *lenp.
*/
static int
{
int (*cb_flag)();
int rval = -1;
if (lenp)
*lenp = 0;
else
goto out; /* memory allocation problem */
goto out; /* memory allocation problem */
/* form INQUIRY cdb with specified EVPD and page code */
SCMD_INQUIRY, 0, buflen, 0);
/*
* Issue inquiry command thru scsi_test
*
* NOTE: This is important data about device identity, not sure why
* NOPARITY is used. Also seems like we should check pkt_stat for
* STATE_XFERRED_DATA.
*/
if (lenp)
rval = 0;
}
if (inq_bp)
return (rval);
}