view usr/src/cmd/iscsi/iscsitgtd/t10_spc.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * 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	"@(#)t10_spc.c	1.10	07/11/21 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"

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;
	uint8_t			*rbp;		/* temporary var */
	struct scsi_inquiry	*inq;
	uint32_t		len;
	uint32_t		page83_len;
	uint32_t		rqst_len;
	uint32_t		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.
		 * Version descriptor details also should be included
		 * with the inquiry response. So, rtn_len should consider
		 * version descriptor details in standard inquiry format
		 * as per SPC-3 Revision 23, Section 6.4.2 Table 81
		 */

		rtn_len = min(rqst_len, max(sizeof (*inq),
		    (SPC_INQ_VD_IDX + SPC_INQ_VD_LEN)));

		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_tpgt));
			rbp[3]	= lobyte(loword(cmd->c_lu->l_targ->s_tpgt));
			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;
	int		len;
	int		len_network;
	int		select;
	int		lun_idx;
	int		lun_val;
	char		*str;
	tgt_node_t	*targ;
	tgt_node_t	*lun_list;
	tgt_node_t	*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;
	int			alloc_len;
	int			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_tpgt));
		tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tpgt));
		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);
}

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 <= T10_MAX_LUNS) {

		/*
		 * 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);
}