t10_spc.c revision 2cb5f2d8d3edf24c4304283afef8e0558d839bbd
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* []------------------------------------------------------------------[]
* | SPC-3 Support Functions |
* | These routines are not directly called by the SAM-3 layer. Those |
* | who write device emulation modules are free to call these routine |
* | to carry out housekeeping chores. |
* []------------------------------------------------------------------[]
*/
#include <sys/types.h>
#include <sys/asynch.h>
#include <sys/param.h>
#include <strings.h>
#include <unistd.h>
#include <sys/scsi/generic/sense.h>
#include <sys/scsi/generic/status.h>
#include <sys/scsi/generic/inquiry.h>
#include <sys/scsi/generic/mode.h>
#include <sys/scsi/generic/commands.h>
#include "t10.h"
#include "t10_spc.h"
#include "target.h"
static void spc_free(emul_handle_t id);
/*
* []----
* | spc_unsupported -- generic routine to indicate we don't support this cmd
* []----
*/
/*ARGSUSED*/
void
spc_unsupported(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
char debug[80];
(void) snprintf(debug, sizeof (debug),
"SAM%d LUN%d Command 0x%x (%s) unsupported\n",
cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
cdb[0], cmd->c_lu->l_cmd_table[cdb[0]].cmd_name != NULL ?
cmd->c_lu->l_cmd_table[cdb[0]].cmd_name : "No description");
queue_str(mgmtq, Q_STE_ERRS, msg_log, debug);
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, 0x20, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
}
/*
* []----
* | spc_tur -- test unit ready
* []----
*/
/*ARGSUSED*/
void
spc_tur(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
/*
* SPC-3 Revision 21c, section 6.31
* Reserve bit checks
*/
if (cdb[1] || cdb[2] || cdb[3] || cdb[4] ||
SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
} else
trans_send_complete(cmd, STATUS_GOOD);
}
/*
* []----
* | spc_request_sense --
* []----
*/
/*ARGSUSED*/
void
spc_request_sense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
/* ---- Check for reserved bit conditions ---- */
if ((cdb[1] & 0xfe) || cdb[2] || cdb[3] ||
SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
} else {
/*
* Since we always run with autosense enabled there's
* no sense data to return. That may change in the future
* if we decide to add such support, but for now return
* success always.
*/
spc_sense_create(cmd, 0, 0);
trans_send_complete(cmd, STATUS_GOOD);
}
}
/*
* []----
* | spc_inquiry -- Standard INQUIRY command
* []----
*/
/*ARGSUSED*/
void
spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
uint8_t *rsp_buf,
*rbp; /* temporary var */
struct scsi_inquiry *inq;
uint32_t len,
page83_len,
rqst_len,
rtn_len;
struct vpd_hdr *vhp;
struct vpd_desc vd;
size_t scsi_len;
t10_lu_common_t *lu = cmd->c_lu->l_common;
void *v;
uint16_t *vdv;
extended_inq_data_t *eid;
/*
* Information obtained from:
* SPC-3 Revision 21c
* Section 6.4.1 INQUIRY command
* Need to generate a CHECK CONDITION with ILLEGAL REQUEST
* and INVALID FIELD IN CDB (0x24/0x00) if any of the following is
* true.
* (1) If the EVPD bit is not set, then the page code must be zero.
* (2) If any bit other than EVPD is set.
* (3) If any of the reserved bits in the CONTROL byte are set.
*/
if (((cdb[1] == 0) && (cdb[2] != 0)) || (cdb[1] & ~1) ||
SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
rqst_len = cdb[3] << 8 | cdb[4];
/*
* Zero length is not an error and we should just acknowledge
* the operation.
*/
if (rqst_len == 0) {
trans_send_complete(cmd, STATUS_GOOD);
return;
}
/*
* We send back a total of six Vital Product Data descriptors
* plus associated data. Three are EUI values, two are for AULA
* support being simple 4 byte values, and one is the IQN string.
* NOTE: The IQN string is just an artifact of when the target
* was created. Once FC support is added, the t_name would be
* either a "naa." or "eui." string.
*/
scsi_len = ((strlen(cmd->c_lu->l_targ->s_targ_base) + 1) + 3) & ~3;
page83_len = (sizeof (struct vpd_desc) * 6) + scsi_len +
(lu->l_guid_len * 3) + (sizeof (uint32_t) * 2);
/*
* We always allocate enough space so that the code can create
* either the full inquiry data or page 0x83 data.
*/
len = sizeof (struct vpd_hdr) + page83_len;
len = max(rqst_len, max(sizeof (*inq), len));
len = max(len, sizeof (*eid));
/*
* Allocate space with an alignment that will work for any casting.
*/
if ((v = memalign(sizeof (void *), len)) == NULL) {
trans_send_complete(cmd, STATUS_BUSY);
return;
}
bzero(v, len);
rsp_buf = (uint8_t *)v;
/*
* EVPD not set returns the standard inquiry data.
*/
if (cdb[1] == 0) {
/*
* Return whatever is the smallest amount between the
* INQUIRY data or the amount requested.
*/
rtn_len = min(rqst_len, sizeof (*inq));
inq = (struct scsi_inquiry *)rsp_buf;
/*
* SPC-3 Revision 21c, Section 6.4.2 .. Table 82 -- Version
* This target will comply with T10/1416-D
*/
inq->inq_ansi = SPC_INQ_VERS_SPC_3;
inq->inq_len = sizeof (*inq) - 4;
inq->inq_dtype = lu->l_dtype;
inq->inq_normaca = 0;
inq->inq_rdf = SPC_INQ_RDF;
inq->inq_cmdque = 1;
inq->inq_linked = 0;
/*
* JIST Requires that we show support for hierarchical
* support (HiSup).
*/
inq->inq_hisup = 1;
/*
* SPC-4, revision 1a, section 6.4.2
* Stand INQUIRY Data
* To support MPxIO we enable ALUA support and identify
* all paths to this device as being active/optimized
* we defaults to symmertrical devices.
*/
inq->inq_tpgs = 1;
(void) snprintf(inq->inq_vid,
sizeof (inq->inq_vid) + sizeof (inq->inq_pid) +
sizeof (inq->inq_revision), "%-8s%-16s%-4s",
lu->l_vid, lu->l_pid,
DEFAULT_REVISION);
/*
* SPC-3 Revision 21c, section 6.4.2
* Table 85 -- Version Descriptor values
* Starting at byte 58 there are up to 8 version
* descriptors which are 16bits in size.
*
* NOTE: The ordering of these values is according
* to the standard. First comes the architectural
* version and then followed by: physical transport,
* SCSI transport, primary command set version, and
* finally the device type command set.
*/
vdv = &((uint16_t *)v)[29];
/* SAM-3 T10/1561-D rev 14 */
*vdv++ = htons(SPC_INQ_VD_SAM3);
/* physical transport code unknown */
*vdv++ = htons(0x0000);
*vdv++ = htons(cmd->c_lu->l_targ->s_trans_vers);
/* SPC ANSI X3.301:1997 */
*vdv++ = htons(SPC_INQ_VD_SPC3);
if (lu->l_dtype == DTYPE_DIRECT) {
/* SBC-2 T10/1417-D rev 5a */
*vdv++ = htons(SPC_INQ_VD_SBC2);
} else if (lu->l_dtype == DTYPE_SEQUENTIAL) {
/* SSC-2 (no version) */
*vdv++ = htons(SPC_INQ_VD_SSC3);
} else if (lu->l_dtype == DTYPE_OSD) {
/* OSD T10/1355-D revision 10 */
*vdv++ = htons(SPC_INQ_VD_OSD);
}
} else {
/* ---- Common information returned with all page types ---- */
rsp_buf[0] = lu->l_dtype;
rsp_buf[1] = cdb[2];
queue_prt(mgmtq, Q_STE_NONIO, "SPC%d INQUIRY Page%x request\n",
lu->l_num, cdb[2]);
switch (cdb[2]) {
default:
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
case SPC_INQ_PAGE0:
/*
* SPC-3 Revision 21c Section 7.6.10
* EVPD page 0 returns information about which pages
* are supported. We support page 0x00 and 0x83.
* NOTE: The value found in byte[3] is the returned
* page length as defined by (n - 3) where 'n' is
* the last valid byte. In this case 5.
*/
rsp_buf[3] = 4;
rsp_buf[4] = SPC_INQ_PAGE0;
rsp_buf[5] = SPC_INQ_PAGE80;
rsp_buf[6] = SPC_INQ_PAGE83;
rsp_buf[7] = SPC_INQ_PAGE86;
/*
* Return the smallest amount of data between the
* requested amount and the size of the Page83 data.
*/
rtn_len = min(rqst_len, sizeof (struct vpd_hdr) +
rsp_buf[3]);
break;
case SPC_INQ_PAGE80:
/*
* Return the smallest amount of data between the
* requested amount and the size of the Page80 data.
*/
rtn_len = min(rqst_len, sizeof (struct vpd_hdr) + 4);
rsp_buf[3] = 4;
rsp_buf[4] = 0x20;
rsp_buf[5] = 0x20;
rsp_buf[6] = 0x20;
rsp_buf[7] = 0x20;
break;
case SPC_INQ_PAGE83:
/*
* Return the smallest amount of data between the
* requested amount and the size of the Page83 data.
*/
rtn_len = min(rqst_len, sizeof (struct vpd_hdr) +
page83_len);
/*
* Information obtained from:
* SPC-3 Revision 21c
* Section 7.6.4.1
*/
/* ---- VPD header ---- */
vhp = (struct vpd_hdr *)v;
vhp->device_type = lu->l_dtype;
vhp->periph_qual = SPC_INQUIRY_PERIPH_CONN;
vhp->page_code = cdb[2];
vhp->page_len[0] = hibyte(loword(page83_len));
vhp->page_len[1] = lobyte(loword(page83_len));
rbp = (uint8_t *)v + sizeof (*vhp);
/* ---- VPD descriptor ---- */
/*
* SPC-4 revision 1a, section 7.6.3.8
* Target port group designator format
*/
vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
vd.id_type = SPC_INQUIRY_ID_TYPE_TARG_PORT;
vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
vd.association = SPC_INQUIRY_ASSOC_TARGPORT;
vd.piv = 1;
vd.len = 4;
bcopy(&vd, rbp, sizeof (vd));
rbp += sizeof (vd);
len = SPC_DEFAULT_TPG;
rbp[2] = hibyte(loword(len));
rbp[3] = lobyte(loword(len));
rbp += vd.len;
/* ---- VPD descriptor ---- */
/*
* SPC-4, revision 1a, section 7.6.3.7
* Relative target port designator format
*/
vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
vd.id_type = SPC_INQUIRY_ID_TYPE_RELATIVE;
vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
vd.association = SPC_INQUIRY_ASSOC_TARGPORT;
vd.piv = 1;
vd.len = 4;
bcopy(&vd, rbp, sizeof (vd));
rbp += sizeof (vd);
rbp[2] = hibyte(loword(cmd->c_lu->l_targ->s_tp_grp));
rbp[3] = lobyte(loword(cmd->c_lu->l_targ->s_tp_grp));
rbp += vd.len;
/* ---- VPD descriptor ---- */
vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
vd.id_type = SPC_INQUIRY_ID_TYPE_EUI;
vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
vd.association = SPC_INQUIRY_ASSOC_LUN;
vd.piv = 1;
vd.len = lu->l_guid_len;
bcopy(&vd, rbp, sizeof (vd));
rbp += sizeof (vd);
bcopy(lu->l_guid, &rbp[0], lu->l_guid_len);
rbp += lu->l_guid_len;
/* ---- VPD descriptor ---- */
vd.code_set = SPC_INQUIRY_CODE_SET_UTF8;
vd.id_type = SPC_INQUIRY_ID_TYPE_SCSI;
vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
vd.association = SPC_INQUIRY_ASSOC_LUN;
vd.piv = 1;
vd.len = scsi_len;
bcopy(&vd, rbp, sizeof (vd));
rbp += sizeof (vd);
/*
* SPC-3 revision 23, section 7.6.3.11
* Use the string length and not the scsi_len because
* we've rounded up the length to a multiple of four
* as required by the specification.
*/
bcopy(cmd->c_lu->l_targ->s_targ_base, &rbp[0],
strlen(cmd->c_lu->l_targ->s_targ_base));
rbp += vd.len;
/* ---- VPD descriptor ---- */
vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
vd.id_type = SPC_INQUIRY_ID_TYPE_EUI;
vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
vd.association = SPC_INQUIRY_ASSOC_TARG;
vd.piv = 1;
vd.len = lu->l_guid_len;
bcopy(&vd, rbp, sizeof (vd));
rbp += sizeof (vd);
/*
* XXX Is this right XXX
* Should we be using some other name.
*/
bcopy(lu->l_guid, &rbp[0], lu->l_guid_len);
rbp += lu->l_guid_len;
/* ---- VPD descriptor ---- */
vd.code_set = SPC_INQUIRY_CODE_SET_BINARY;
vd.id_type = SPC_INQUIRY_ID_TYPE_EUI;
vd.proto_id = SPC_INQUIRY_PROTOCOL_ISCSI;
vd.association = SPC_INQUIRY_ASSOC_TARGPORT;
vd.piv = 1;
vd.len = lu->l_guid_len;
bcopy(&vd, rbp, sizeof (vd));
rbp += sizeof (vd);
/*
* XXX Is this right XXX
* Should we be using some other name.
*/
bcopy(lu->l_guid, &rbp[0], lu->l_guid_len);
/*
* rbp is updated here even though nobody will
* currently use it. Currently is the optertive word
* here. If for some reason we add another VDP
* then the pointer will be correct.
*/
rbp += lu->l_guid_len;
break;
case SPC_INQ_PAGE86:
/*
* Return the smallest amount of data between the
* requested amount and the size of the Page86 data.
*/
rtn_len = min(rqst_len, sizeof (*eid));
eid = (extended_inq_data_t *)v;
eid->ei_hdr.device_type = lu->l_dtype;
eid->ei_hdr.page_code = cdb[2];
eid->ei_hdr.page_len[1] = 60; /* defined by spec */
/*
* At this point in time we don't support any of the
* extended data attributes. We should support
* the task management bits though.
*/
break;
}
}
if (trans_send_datain(cmd, (char *)rsp_buf, rtn_len, 0, spc_free,
True, rsp_buf) == False) {
trans_send_complete(cmd, STATUS_BUSY);
}
}
/*
* []----
* | spc_mselect -- Generic MODE SELECT command
* []----
*/
/*ARGSUSED*/
void
spc_mselect(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
char *buf;
/*
* SPC-3 revision 21c, section 6.7
* Reserve bit checks
*/
if ((cdb[1] & 0xee) || cdb[2] || cdb[3] ||
SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
if (cdb[4] == 0) {
trans_send_complete(cmd, STATUS_GOOD);
return;
}
if (((buf = (char *)calloc(1, cdb[4])) == NULL) ||
(trans_rqst_dataout(cmd, buf, cdb[4], 0, buf,
spc_free) == False)) {
trans_send_complete(cmd, STATUS_BUSY);
}
}
/*
* []----
* | spc_mselect_data -- DataIn phase of MODE SELECT command
* []----
*/
/*ARGSUSED*/
void
spc_mselect_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
size_t data_len)
{
struct mode_control_scsi3 mode_ctl_page;
struct mode_header hdr;
bcopy(data, &hdr, sizeof (hdr));
bcopy(data + sizeof (hdr) + hdr.bdesc_length, &mode_ctl_page,
sizeof (mode_ctl_page));
switch (mode_ctl_page.mode_page.code) {
case MODE_SENSE_CONTROL:
/*
* SPC-3 revision 21c, section 7.4.6
* Table 239 describes the fields. We're only interested
* in the descriptor sense bit.
*/
if (mode_ctl_page.d_sense == 1) {
cmd->c_lu->l_dsense_enabled = True;
} else {
cmd->c_lu->l_dsense_enabled = False;
}
break;
default:
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
trans_send_complete(cmd, STATUS_GOOD);
}
/*
* []----
* | spc_report_luns --
* []----
*/
/*ARGSUSED*/
void
spc_report_luns(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
int expected_data;
uint8_t *buf = NULL;
int entries = 0,
len,
len_network,
select,
lun_idx,
lun_val;
char *str;
tgt_node_t *targ,
*lun_list,
*lun;
/*
* SPC-3 Revision 21c section 6.21
* Error checking.
*/
if (cdb[1] || cdb[3] || cdb[4] ||
(cdb[2] & ~SPC_RPT_LUNS_SELECT_MASK) || cdb[5] || cdb[10] ||
SAM_CONTROL_BYTE_RESERVED(cdb[11])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
expected_data = cdb[6] << 24 | cdb[7] << 16 | cdb[8] << 8 | cdb[9];
if (expected_data < 16) {
/*
* The allocation length should be at least 16 according
* to SPC-3.
*/
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
select = cdb[2];
targ = NULL;
while ((targ = tgt_node_next(targets_config, XML_ELEMENT_TARG, targ)) !=
NULL) {
if (tgt_find_value_str(targ, XML_ELEMENT_INAME, &str) ==
False) {
goto error;
}
if (strcmp(str, cmd->c_lu->l_targ->s_targ_base) == 0) {
free(str);
break;
} else
free(str);
}
if (!targ)
goto error;
if ((lun_list = tgt_node_next(targ, XML_ELEMENT_LUNLIST, NULL)) == NULL)
goto error;
lun = NULL;
while ((lun = tgt_node_next(lun_list, XML_ELEMENT_LUN, lun)) != NULL)
entries++;
len = entries * SCSI_REPORTLUNS_ADDRESS_SIZE;
if ((buf = (uint8_t *)calloc(1, MAX(expected_data, len))) == NULL) {
trans_send_complete(cmd, STATUS_BUSY);
return;
}
len_network = htonl(len);
bcopy(&len_network, buf, sizeof (len_network));
if (expected_data >= (len + SCSI_REPORTLUNS_ADDRESS_SIZE)) {
lun_idx = SCSI_REPORTLUNS_ADDRESS_SIZE;
lun = NULL;
while ((lun = tgt_node_next(lun_list, XML_ELEMENT_LUN, lun)) !=
NULL) {
if (tgt_find_value_int(lun, XML_ELEMENT_LUN,
&lun_val) == False)
goto error;
if (spc_encode_lu_addr(&buf[lun_idx], select,
lun_val) == False)
continue;
lun_idx += SCSI_REPORTLUNS_ADDRESS_SIZE;
}
if (trans_send_datain(cmd, (char *)buf,
len + SCSI_REPORTLUNS_ADDRESS_SIZE, 0, spc_free, True,
buf) == False) {
trans_send_complete(cmd, STATUS_BUSY);
}
} else {
/*
* This will return the size needed to complete this request
* and a implicit LUN zero.
*/
if (trans_send_datain(cmd, (char *)buf, 16, 0, spc_free, True,
buf) == False) {
trans_send_complete(cmd, STATUS_BUSY);
}
}
return;
error:
if (buf)
free(buf);
spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0);
trans_send_complete(cmd, STATUS_CHECK);
}
/*ARGSUSED*/
void
spc_report_tpgs(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
rtpg_hdr_t *r;
rtpg_desc_t *dp;
rtpg_targ_desc_t *tp;
int rqst_len,
alloc_len,
i;
t10_lu_common_t *lu = cmd->c_lu->l_common;
t10_lu_impl_t *lu_per;
if (disable_tpgs == True) {
spc_unsupported(cmd, cdb, cdb_len);
return;
}
/*
* Reserve bit checks
*/
if ((cdb[1] & 0xe0) || (cdb[1] & ~SPC_MI_SVC_MASK) ||
((cdb[1] & SPC_MI_SVC_MASK) != SPC_MI_SVC_RTPG) ||
cdb[2] || cdb[3] || cdb[4] || cdb[5] ||
cdb[10] || SAM_CONTROL_BYTE_RESERVED(cdb[11])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
/*
* We only have one target port group which it's size is
* accounted for in rtpg_hdr_t. Take the number of tgts
* and subtract one since the first is accounted for in
* rtpg_targ_desc_t
*/
alloc_len = ((avl_numnodes(&lu->l_all_open) - 1) *
sizeof (rtpg_targ_desc_t)) + sizeof (rtpg_hdr_t);
/*
* Make sure that we have enough room to store everything
* that we want to, but only returned the requested about
* of data which is why d->d_len is set to the request amount.
* A client could issue a REPORT_TPGS with a length of 4 bytes
* which would be just enough to see how much space is actually
* used.
*/
rqst_len = cdb[6] << 24 | cdb[7] << 16 |
cdb[8] << 8 | cdb[9];
if (rqst_len == 0) {
trans_send_complete(cmd, STATUS_GOOD);
return;
}
if ((r = (rtpg_hdr_t *)calloc(1, alloc_len)) == NULL) {
trans_send_complete(cmd, STATUS_BUSY);
return;
}
i = alloc_len - sizeof (r->len);
r->len[0] = hibyte(hiword(i));
r->len[1] = lobyte(hiword(i));
r->len[2] = hibyte(loword(i));
r->len[3] = lobyte(loword(i));
dp = &r->desc_list[0];
dp->tpg_cnt = avl_numnodes(&lu->l_all_open);
dp->status_code = 0;
dp->access_state = 0; /* Active/optimized */
dp->pref = 1;
dp->t_sup = 1;
dp->u_sup = 1;
dp->s_sup = 1;
dp->an_sup = 0;
dp->ao_sup = 1;
i = SPC_DEFAULT_TPG;
dp->tpg[0] = hibyte(loword(i));
dp->tpg[1] = lobyte(loword(i));
tp = &dp->targ_list[0];
lu_per = avl_first(&lu->l_all_open);
do {
tp->rel_tpi[0] = hibyte(loword(lu_per->l_targ->s_tp_grp));
tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tp_grp));
lu_per = AVL_NEXT(&lu->l_all_open, lu_per);
tp++;
} while (lu_per != NULL);
if (trans_send_datain(cmd, (char *)r, MIN(rqst_len, alloc_len), 0,
spc_free, True, (char *)r) == False) {
trans_send_complete(cmd, STATUS_BUSY);
}
}
/*ARGSUSED*/
void
spc_send_diag(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
/*
* SPC-3 Revision 21c, section 6.27
* Reserve bit checks
*/
if ((cdb[1] & ~SPC_SEND_DIAG_SELFTEST) || cdb[2] || cdb[3] || cdb[4] ||
SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
/*
* There's no diagnostics to be run at this time. So, always
* return success. If, at some point in the future, it's determined
* that something can be done which is meaningful then place the
* code here.
*/
trans_send_complete(cmd, STATUS_GOOD);
}
static void
spc_free(emul_handle_t id)
{
free(id);
}
/*
* []----
* | spc_cmd_offline -- return IN_PROGRESS for media related commands
* |
* | During LU initialization the device is in an offline state. When
* | offlined only non-media related commands are allowed to proceed.
* | TEST_UNIT_READY is considered a media command since it must return
* | a CHECK_CONDITION if a media command would do so.
* []----
*/
void
spc_cmd_offline(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
scsi_cmd_table_t *e;
int old_dtype;
e = &cmd->c_lu->l_cmd_table[cdb[0]];
switch (cdb[0]) {
case SCMD_TEST_UNIT_READY:
case SCMD_START_STOP:
case SCMD_READ:
case SCMD_READ_G1:
case SCMD_READ_G4:
case SCMD_WRITE:
case SCMD_WRITE_G1:
case SCMD_WRITE_G4:
#ifdef FULL_DEBUG
queue_prt(mgmtq, Q_STE_IO, "SPC%x LUN%d Cmd %s\n",
cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
e->cmd_name == NULL ? "(no name)" : e->cmd_name);
#endif
spc_sense_create(cmd, KEY_NOT_READY, 0);
spc_sense_ascq(cmd, SPC_ASC_IN_PROG, SPC_ASCQ_IN_PROG);
trans_send_complete(cmd, STATUS_CHECK);
break;
case SCMD_INQUIRY:
/*
* While the device is being initialized any inquiry commands
* will return an unknown device type. This will cause the
* transport to hold off further plumbing until the
* initialization is complete and we send the inventory change
* notice.
*/
old_dtype = cmd->c_lu->l_common->l_dtype;
cmd->c_lu->l_common->l_dtype = 0x1f;
(*e->cmd_start)(cmd, cdb, cdb_len);
cmd->c_lu->l_common->l_dtype = old_dtype;
break;
default:
(*e->cmd_start)(cmd, cdb, cdb_len);
break;
}
}
/*
* []----
* | spc_sense_create -- allocate sense structure
* |
* | If additional header sense length is requested and the I_T_Q has
* | enabled descriptor sense data allocate the space.
* []----
*/
void
spc_sense_create(t10_cmd_t *cmd, int sense_key, int addl_sense_len)
{
char *buf;
int size;
/*
* It's possible under certain conditions -- namely a malloc error
* when setting up a command -- that the pointer to the ITL structure
* isn't valid. If that's the case don't attempt to dereference it
* to look at the dsense_enabled flag.
*/
if ((cmd->c_lu != NULL) && (cmd->c_lu->l_dsense_enabled == True)) {
struct scsi_descr_sense_hdr d;
if ((buf = (char *)calloc(1,
sizeof (d) + 2 + addl_sense_len)) == NULL)
return;
size = sizeof (d) + addl_sense_len;
bzero(&d, sizeof (d));
d.ds_class = CLASS_EXTENDED_SENSE;
d.ds_code = CODE_FMT_DESCR_CURRENT;
d.ds_key = sense_key;
d.ds_addl_sense_length = addl_sense_len;
bcopy(&d, &buf[2], sizeof (d));
} else {
struct scsi_extended_sense e;
if ((buf = (char *)calloc(1, sizeof (e) + 2)) == NULL)
return;
size = sizeof (e);
bzero(&e, sizeof (e));
e.es_class = CLASS_EXTENDED_SENSE;
e.es_code = CODE_FMT_FIXED_CURRENT;
e.es_key = sense_key;
bcopy(&e, &buf[2], size);
}
/* ---- First two bytes of the sense store the length ---- */
buf[0] = hibyte(loword(size));
buf[1] = lobyte(loword(size));
cmd->c_cmd_sense = buf;
cmd->c_cmd_sense_len = size + 2;
}
/*
* []----
* | spc_sense_raw -- copy an existing sense buffer for return.
* |
* | If an emulation module already has a sense buffer there's no need
* | to decode the sense data and call the various spc_sense_ routines
* | to reencode the information.
* []----
*/
void
spc_sense_raw(t10_cmd_t *cmd, uchar_t *sense_buf, size_t sense_len)
{
ushort_t s = (ushort_t)sense_len;
if ((cmd->c_cmd_sense = malloc(sense_len + 2)) == NULL)
return;
bcopy(sense_buf, &cmd->c_cmd_sense[2], sense_len);
cmd->c_cmd_sense[0] = hibyte(s);
cmd->c_cmd_sense[1] = lobyte(s);
cmd->c_cmd_sense_len = s + 2;
}
void
spc_sense_ascq(t10_cmd_t *cmd, int asc, int ascq)
{
struct scsi_extended_sense s;
struct scsi_descr_sense_hdr d;
bcopy(&cmd->c_cmd_sense[2], &s, sizeof (s));
switch (s.es_code) {
case CODE_FMT_DESCR_CURRENT:
case CODE_FMT_DESCR_DEFERRED:
bcopy(&cmd->c_cmd_sense[2], &d, sizeof (d));
d.ds_add_code = asc;
d.ds_qual_code = ascq;
bcopy(&d, &cmd->c_cmd_sense[2], sizeof (d));
break;
default:
s.es_add_code = asc;
s.es_qual_code = ascq;
bcopy(&s, &cmd->c_cmd_sense[2], sizeof (s));
break;
}
}
void
spc_sense_info(t10_cmd_t *cmd, uint64_t info)
{
struct scsi_information_sense_descr isd;
struct scsi_extended_sense s;
char *p;
uint32_t fixed_info = (uint32_t)info;
switch (cmd->c_cmd_sense[2] & 0x0f) {
case CODE_FMT_DESCR_CURRENT:
case CODE_FMT_DESCR_DEFERRED:
isd.isd_descr_type = DESCR_INFORMATION;
isd.isd_addl_length = 0x0a;
isd.isd_valid = 1;
isd.isd_information[0] = (info >> 56) & 0xff;
isd.isd_information[1] = (info >> 48) & 0xff;
isd.isd_information[2] = (info >> 40) & 0xff;
isd.isd_information[3] = (info >> 32) & 0xff;
isd.isd_information[4] = (info >> 24) & 0xff;
isd.isd_information[5] = (info >> 16) & 0xff;
isd.isd_information[6] = (info >> 8) & 0xff;
isd.isd_information[7] = info & 0xff;
p = &cmd->c_cmd_sense[2] + sizeof (struct scsi_descr_sense_hdr);
bcopy(&isd, p, sizeof (isd));
break;
case CODE_FMT_VENDOR_SPECIFIC:
case CODE_FMT_FIXED_CURRENT:
case CODE_FMT_FIXED_DEFERRED:
default:
bcopy(&cmd->c_cmd_sense[2], &s, sizeof (s));
s.es_valid = 1;
if (info > FIXED_SENSE_ADDL_INFO_LEN) {
s.es_info_1 = 0xff;
s.es_info_2 = 0xff;
s.es_info_3 = 0xff;
s.es_info_4 = 0xff;
} else {
s.es_info_1 = hibyte(hiword(fixed_info));
s.es_info_2 = lobyte(hiword(fixed_info));
s.es_info_3 = hibyte(loword(fixed_info));
s.es_info_4 = lobyte(loword(fixed_info));
}
bcopy(&s, &cmd->c_cmd_sense[2], sizeof (s));
break;
}
}
void
spc_sense_flags(t10_cmd_t *cmd, int flags)
{
struct scsi_extended_sense s;
bcopy(&cmd->c_cmd_sense[2], &s, sizeof (s));
if (flags & SPC_SENSE_EOM)
s.es_eom = 1;
if (flags & SPC_SENSE_FM)
s.es_filmk = 1;
if (flags & SPC_SENSE_ILI)
s.es_ili = 1;
bcopy(&s, &cmd->c_cmd_sense[2], sizeof (s));
}
/*
* []----
* | spc_decode_lu_addr -- Decodes LU addressing as specified in SAM-3
* []----
*/
Boolean_t
spc_decode_lu_addr(uint8_t *buf, int len, uint32_t *val)
{
uint32_t lun;
if (len < 2)
return (False);
switch (buf[0] & SCSI_REPORTLUNS_ADDRESS_MASK) {
case SCSI_REPORTLUNS_ADDRESS_PERIPHERAL:
case SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE:
lun = ((buf[0] & 0x3f) << 8) | (buf[1] & 0xff);
break;
/*
* Since we never encode a LUN using this method, we
* shouldn't receive it back.
*/
case SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT:
return (False);
case SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT:
switch (buf[0] & SCSI_REPORTLUNS_ADDRESS_EXTENDED_MASK) {
case SCSI_REPORTLUNS_ADDRESS_EXTENDED_2B:
lun = buf[1] & 0xff;
break;
case SCSI_REPORTLUNS_ADDRESS_EXTENDED_4B:
if (len < 4)
return (False);
lun = buf[1] << 16 | buf[2] << 8 | (buf[3] & 0xff);
break;
case SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B:
if (len < 6)
return (False);
/*
* This should be able to handle a 40-bit LUN,
* but since our LUNs are only 32-bit we don't
* bother to decode buf[1]. This is okay since
* we generate the LUN in the first place.
*/
lun = buf[2] << 24 | buf[3] << 16 |
buf[4] << 8 | (buf[5] & 0xff);
break;
case SCSI_REPORTLUNS_ADDRESS_EXTENDED_8B:
/*
* Since we current don't support larger than
* 32-bit LUNs we'll never create an extended
* address using this format. So, if we are to
* get this format in, just return an error.
*/
return (False);
break;
}
}
*val = lun;
return (True);
}
/*
* []----
* | spc_encode_lu_addr -- encode, based on SAM-3/SPC-3 specs, a LUN
* |
* | NOTE: This routine only deals with 32-bit logical unit numbers.
* | If this program ever switches to using larger values we need to
* | simply deal with those formats.
* []----
*/
Boolean_t
spc_encode_lu_addr(uint8_t *buf, int select_field, uint32_t lun)
{
if (lun < 256) {
/*
* SAM-3 revision 14, Section 4.9.6.
* No bus identifier for our luns.
*/
buf[0] = SCSI_REPORTLUNS_ADDRESS_PERIPHERAL;
buf[1] = lun;
} else if (lun < 0x3fff) {
/*
* SAM-3 revision 14, Section 4.9.7.
* 14-bit flat address space.
*/
buf[0] = SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE |
(lun >> 8 & 0x3f);
buf[1] = lun & 0xff;
} else if (select_field == SCSI_REPORTLUNS_SELECT_ALL) {
buf[0] = SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT |
SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B;
/*
* 32-bit limitation. This format should be able to
* handle a 40-bit LUN.
*/
buf[1] = 0;
buf[2] = lun >> 24 & 0xff;
buf[3] = lun >> 16 & 0xff;
buf[4] = lun >> 8 & 0xff;
buf[5] = lun & 0xff;
} else
/*
* Either the user hasn't requested extended unit addressing
* or the LU is greater than 32bits. Since internally we
* only have 32bit numbers it's more likely that the initiator
* hasn't selected a correct report format.
*/
return (False);
return (True);
}