changeset 7152:4575049c1ae7 onnv_95

PSARC/2008/448 SATA Framework Interface Addition 6595488 nv_sata: add support for ATAPI devices 6718624 sata framework does not provide an interface for HBAs to safely do PIO operation 6718895 nv_sata: potential for disk operation buffer overwrites when using PIO 6726013 nv_sata: make device signature debugging message less obtrusive
author ap25164
date Mon, 21 Jul 2008 16:56:44 -0700
parents 8ef38730cb56
children 41bc17281c36
files usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c usr/src/uts/common/io/sata/impl/sata.c usr/src/uts/common/sys/sata/adapters/nv_sata/nv_sata.h usr/src/uts/common/sys/sata/sata_hba.h
diffstat 4 files changed, 640 insertions(+), 115 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c	Mon Jul 21 16:51:53 2008 -0700
+++ b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c	Mon Jul 21 16:56:44 2008 -0700
@@ -110,6 +110,8 @@
 static int nv_start_pio_out(nv_port_t *nvp, int slot);
 static void nv_intr_pio_in(nv_port_t *nvp, nv_slot_t *spkt);
 static void nv_intr_pio_out(nv_port_t *nvp, nv_slot_t *spkt);
+static int nv_start_pkt_pio(nv_port_t *nvp, int slot);
+static void nv_intr_pkt_pio(nv_port_t *nvp, nv_slot_t *nv_slotp);
 static int nv_start_dma(nv_port_t *nvp, int slot);
 static void nv_intr_dma(nv_port_t *nvp, struct nv_slot *spkt);
 static void nv_log(uint_t flag, nv_ctl_t *nvc, nv_port_t *nvp, char *fmt, ...);
@@ -151,6 +153,7 @@
     uint_t timeout_usec, int type_wait);
 static int nv_wait(nv_port_t *nvp, uchar_t onbits, uchar_t offbits,
     uint_t timeout_usec, int type_wait);
+static int nv_start_rqsense_pio(nv_port_t *nvp, nv_slot_t *nv_slotp);
 
 
 /*
@@ -215,6 +218,21 @@
 	NULL			/* power */
 };
 
+
+/*
+ * Request Sense CDB for ATAPI
+ */
+static const uint8_t nv_rqsense_cdb[16] = {
+	SCMD_REQUEST_SENSE,
+	0,
+	0,
+	0,
+	SATA_ATAPI_MIN_RQSENSE_LEN,
+	0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0	/* pad out to max CDB length */
+};
+
+
 static sata_tran_hotplug_ops_t nv_hotplug_ops;
 
 extern struct mod_ops mod_driverops;
@@ -1004,12 +1022,14 @@
 	    TICK_TO_MSEC(nv_lbolt - nvp->nvp_probe_time)));
 
 	/*
-	 * nv_sata only deals with ATA disks so far.  If it is
-	 * not an ATA disk, then just return.
+	 * nv_sata only deals with ATA disks and ATAPI CD/DVDs so far.  If
+	 * it is not either of those, then just return.
 	 */
-	if (nvp->nvp_type != SATA_DTYPE_ATADISK) {
-		nv_cmn_err(CE_WARN, nvc, nvp, "Driver currently handles only"
-		    " disks.  Signature acquired was %X", nvp->nvp_signature);
+	if ((nvp->nvp_type != SATA_DTYPE_ATADISK) &&
+	    (nvp->nvp_type != SATA_DTYPE_ATAPICD)) {
+		NVLOG((NVDBG_PROBE, nvc, nvp, "Driver currently handles only"
+		    " disks/CDs/DVDs.  Signature acquired was %X",
+		    nvp->nvp_signature));
 		mutex_exit(&nvp->nvp_mutex);
 
 		return (SATA_SUCCESS);
@@ -1095,16 +1115,6 @@
 		return (SATA_TRAN_PORT_ERROR);
 	}
 
-	if (spkt->satapkt_device.satadev_type == SATA_DTYPE_ATAPICD) {
-		ASSERT(nvp->nvp_type == SATA_DTYPE_ATAPICD);
-		nv_cmn_err(CE_WARN, nvc, nvp,
-		    "optical devices not supported");
-		spkt->satapkt_reason = SATA_PKT_CMD_UNSUPPORTED;
-		mutex_exit(&nvp->nvp_mutex);
-
-		return (SATA_TRAN_CMD_UNSUPPORTED);
-	}
-
 	if (spkt->satapkt_device.satadev_type == SATA_DTYPE_PMULT) {
 		ASSERT(nvp->nvp_type == SATA_DTYPE_PMULT);
 		nv_cmn_err(CE_WARN, nvc, nvp,
@@ -1706,6 +1716,25 @@
 		NVLOG((NVDBG_DELIVER, nvc,  nvp, "DMA command"));
 		nv_slotp->nvslot_start = nv_start_dma;
 		nv_slotp->nvslot_intr = nv_intr_dma;
+	} else if (spkt->satapkt_cmd.satacmd_cmd_reg == SATAC_PACKET) {
+		NVLOG((NVDBG_DELIVER, nvc,  nvp, "packet command"));
+		nv_slotp->nvslot_start = nv_start_pkt_pio;
+		nv_slotp->nvslot_intr = nv_intr_pkt_pio;
+		if ((direction == SATA_DIR_READ) ||
+		    (direction == SATA_DIR_WRITE)) {
+			nv_slotp->nvslot_byte_count =
+			    spkt->satapkt_cmd.satacmd_bp->b_bcount;
+			nv_slotp->nvslot_v_addr =
+			    spkt->satapkt_cmd.satacmd_bp->b_un.b_addr;
+			/*
+			 * Freeing DMA resources allocated by the framework
+			 * now to avoid buffer overwrite (dma sync) problems
+			 * when the buffer is released at command completion.
+			 * Primarily an issue on systems with more than
+			 * 4GB of memory.
+			 */
+			sata_free_dma_resources(spkt);
+		}
 	} else if (direction == SATA_DIR_NODATA_XFER) {
 		NVLOG((NVDBG_DELIVER, nvc, nvp, "non-data command"));
 		nv_slotp->nvslot_start = nv_start_nodata;
@@ -1718,6 +1747,14 @@
 		    spkt->satapkt_cmd.satacmd_bp->b_bcount;
 		nv_slotp->nvslot_v_addr =
 		    spkt->satapkt_cmd.satacmd_bp->b_un.b_addr;
+		/*
+		 * Freeing DMA resources allocated by the framework now to
+		 * avoid buffer overwrite (dma sync) problems when the buffer
+		 * is released at command completion.  This is not an issue
+		 * for write because write does not update the buffer.
+		 * Primarily an issue on systems with more than 4GB of memory.
+		 */
+		sata_free_dma_resources(spkt);
 	} else if (direction == SATA_DIR_WRITE) {
 		NVLOG((NVDBG_DELIVER, nvc, nvp, "pio out command"));
 		nv_slotp->nvslot_start = nv_start_pio_out;
@@ -2071,12 +2108,12 @@
 	}
 
 
-	stran.sata_tran_hba_rev = SATA_TRAN_HBA_REV;
+	stran.sata_tran_hba_rev = SATA_TRAN_HBA_REV_2;
 	stran.sata_tran_hba_dip = nvc->nvc_dip;
 	stran.sata_tran_hba_dma_attr = &buffer_dma_attr;
 	stran.sata_tran_hba_num_cports = NV_NUM_CPORTS;
 	stran.sata_tran_hba_features_support =
-	    SATA_CTLF_HOTPLUG | SATA_CTLF_ASN;
+	    SATA_CTLF_HOTPLUG | SATA_CTLF_ASN | SATA_CTLF_ATAPI;
 	stran.sata_tran_hba_qdepth = NV_QUEUE_SLOTS;
 	stran.sata_tran_probe_port = nv_sata_probe;
 	stran.sata_tran_start = nv_sata_start;
@@ -3851,6 +3888,114 @@
 
 
 /*
+ * start a ATAPI Packet command (PIO data in or out)
+ */
+static int
+nv_start_pkt_pio(nv_port_t *nvp, int slot)
+{
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	sata_cmd_t *satacmd = &spkt->satapkt_cmd;
+
+	NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+	    "nv_start_pkt_pio: start"));
+
+	/*
+	 * Write the PACKET command to the command register.  Normally
+	 * this would be done through nv_program_taskfile_regs().  It
+	 * is done here because some values need to be overridden.
+	 */
+
+	/* select the drive */
+	nv_put8(cmdhdl, nvp->nvp_drvhd, satacmd->satacmd_device_reg);
+
+	/* make certain the drive selected */
+	if (nv_wait(nvp, SATA_STATUS_DRDY, SATA_STATUS_BSY,
+	    NV_SEC2USEC(5), 0) == B_FALSE) {
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_start_pkt_pio: drive select failed"));
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	/*
+	 * The command is always sent via PIO, despite whatever the SATA
+	 * framework sets in the command.  Overwrite the DMA bit to do this.
+	 * Also, overwrite the overlay bit to be safe (it shouldn't be set).
+	 */
+	nv_put8(cmdhdl, nvp->nvp_feature, 0);	/* deassert DMA and OVL */
+
+	/* set appropriately by the sata framework */
+	nv_put8(cmdhdl, nvp->nvp_hcyl, satacmd->satacmd_lba_high_lsb);
+	nv_put8(cmdhdl, nvp->nvp_lcyl, satacmd->satacmd_lba_mid_lsb);
+	nv_put8(cmdhdl, nvp->nvp_sect, satacmd->satacmd_lba_low_lsb);
+	nv_put8(cmdhdl, nvp->nvp_count, satacmd->satacmd_sec_count_lsb);
+
+	/* initiate the command by writing the command register last */
+	nv_put8(cmdhdl, nvp->nvp_cmd, spkt->satapkt_cmd.satacmd_cmd_reg);
+
+	/* Give the host controller time to do its thing */
+	NV_DELAY_NSEC(400);
+
+	/*
+	 * Wait for the device to indicate that it is ready for the command
+	 * ATAPI protocol state - HP0: Check_Status_A
+	 */
+
+	if (nv_wait3(nvp, SATA_STATUS_DRQ, SATA_STATUS_BSY, /* okay */
+	    SATA_STATUS_ERR, SATA_STATUS_BSY, /* cmd failed */
+	    SATA_STATUS_DF, SATA_STATUS_BSY, /* drive failed */
+	    4000000, 0) == B_FALSE) {
+		/*
+		 * Either an error or device fault occurred or the wait
+		 * timed out.  According to the ATAPI protocol, command
+		 * completion is also possible.  Other implementations of
+		 * this protocol don't handle this last case, so neither
+		 * does this code.
+		 */
+
+		if (nv_get8(cmdhdl, nvp->nvp_status) &
+		    (SATA_STATUS_ERR | SATA_STATUS_DF)) {
+			spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_start_pkt_pio: device error (HP0)"));
+		} else {
+			spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_start_pkt_pio: timeout (HP0)"));
+		}
+
+		nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+		nv_complete_io(nvp, spkt, 0);
+		nv_reset(nvp);
+
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	/*
+	 * Put the ATAPI command in the data register
+	 * ATAPI protocol state - HP1: Send_Packet
+	 */
+
+	ddi_rep_put16(cmdhdl, (ushort_t *)spkt->satapkt_cmd.satacmd_acdb,
+	    (ushort_t *)nvp->nvp_data,
+	    (spkt->satapkt_cmd.satacmd_acdb_len >> 1), DDI_DEV_NO_AUTOINCR);
+
+	/*
+	 * See you in nv_intr_pkt_pio.
+	 * ATAPI protocol state - HP3: INTRQ_wait
+	 */
+
+	NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+	    "nv_start_pkt_pio: exiting into HP3"));
+
+	return (SATA_TRAN_ACCEPTED);
+}
+
+
+/*
  * Interrupt processing for a non-data ATA command.
  */
 static void
@@ -4041,6 +4186,259 @@
 
 
 /*
+ * ATAPI PACKET command, PIO in/out interrupt
+ *
+ * Under normal circumstances, one of four different interrupt scenarios
+ * will result in this function being called:
+ *
+ * 1. Packet command data transfer
+ * 2. Packet command completion
+ * 3. Request sense data transfer
+ * 4. Request sense command completion
+ */
+static void
+nv_intr_pkt_pio(nv_port_t *nvp, nv_slot_t *nv_slotp)
+{
+	uchar_t	status;
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	int direction = sata_cmdp->satacmd_flags.sata_data_direction;
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	uint16_t ctlr_count;
+	int count;
+
+	/* ATAPI protocol state - HP2: Check_Status_B */
+
+	status = nv_get8(cmdhdl, nvp->nvp_status);
+	NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+	    "nv_intr_pkt_pio: status 0x%x", status));
+
+	if (status & SATA_STATUS_BSY) {
+		if ((nv_slotp->nvslot_flags & NVSLOT_RQSENSE) != 0) {
+			nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+			spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+		} else {
+			nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+			spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+
+			nv_reset(nvp);
+		}
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: busy - status 0x%x", status));
+
+		return;
+	}
+
+	if ((status & SATA_STATUS_DF) != 0) {
+		/*
+		 * On device fault, just clean up and bail.  Request sense
+		 * will just default to its NO SENSE initialized value.
+		 */
+
+		if ((nv_slotp->nvslot_flags & NVSLOT_RQSENSE) == 0) {
+			nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+		}
+
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+		spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+
+		sata_cmdp->satacmd_status_reg = nv_get8(ctlhdl,
+		    nvp->nvp_altstatus);
+		sata_cmdp->satacmd_error_reg = nv_get8(cmdhdl,
+		    nvp->nvp_error);
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: device fault"));
+
+		return;
+	}
+
+	if ((status & SATA_STATUS_ERR) != 0) {
+		/*
+		 * On command error, figure out whether we are processing a
+		 * request sense.  If so, clean up and bail.  Otherwise,
+		 * do a REQUEST SENSE.
+		 */
+
+		if ((nv_slotp->nvslot_flags & NVSLOT_RQSENSE) == 0) {
+			nv_slotp->nvslot_flags |= NVSLOT_RQSENSE;
+			if (nv_start_rqsense_pio(nvp, nv_slotp) ==
+			    NV_FAILURE) {
+				nv_copy_registers(nvp, &spkt->satapkt_device,
+				    spkt);
+				nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+				spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+			}
+
+			sata_cmdp->satacmd_status_reg = nv_get8(ctlhdl,
+			    nvp->nvp_altstatus);
+			sata_cmdp->satacmd_error_reg = nv_get8(cmdhdl,
+			    nvp->nvp_error);
+		} else {
+			nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+			spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+
+			nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+		}
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: error (status 0x%x)", status));
+
+		return;
+	}
+
+	if ((nv_slotp->nvslot_flags & NVSLOT_RQSENSE) != 0) {
+		/*
+		 * REQUEST SENSE command processing
+		 */
+
+		if ((status & (SATA_STATUS_DRQ)) != 0) {
+			/* ATAPI state - HP4: Transfer_Data */
+
+			/* read the byte count from the controller */
+			ctlr_count =
+			    (uint16_t)nv_get8(cmdhdl, nvp->nvp_hcyl) << 8;
+			ctlr_count |= nv_get8(cmdhdl, nvp->nvp_lcyl);
+
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_intr_pkt_pio: ctlr byte count - %d",
+			    ctlr_count));
+
+			if (ctlr_count == 0) {
+				/* no data to transfer - some devices do this */
+
+				spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+				nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+
+				NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+				    "nv_intr_pkt_pio: done (no data)"));
+
+				return;
+			}
+
+			count = min(ctlr_count, SATA_ATAPI_RQSENSE_LEN);
+
+			/* transfer the data */
+			ddi_rep_get16(cmdhdl,
+			    (ushort_t *)nv_slotp->nvslot_rqsense_buff,
+			    (ushort_t *)nvp->nvp_data, (count >> 1),
+			    DDI_DEV_NO_AUTOINCR);
+
+			/* consume residual bytes */
+			ctlr_count -= count;
+
+			if (ctlr_count > 0) {
+				for (; ctlr_count > 0; ctlr_count -= 2)
+					(void) ddi_get16(cmdhdl,
+					    (ushort_t *)nvp->nvp_data);
+			}
+
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_intr_pkt_pio: transition to HP2"));
+		} else {
+			/* still in ATAPI state - HP2 */
+
+			/*
+			 * In order to avoid clobbering the rqsense data
+			 * set by the SATA framework, the sense data read
+			 * from the device is put in a separate buffer and
+			 * copied into the packet after the request sense
+			 * command successfully completes.
+			 */
+			bcopy(nv_slotp->nvslot_rqsense_buff,
+			    spkt->satapkt_cmd.satacmd_rqsense,
+			    SATA_ATAPI_RQSENSE_LEN);
+
+			nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+			spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_intr_pkt_pio: request sense done"));
+		}
+
+		return;
+	}
+
+	/*
+	 * Normal command processing
+	 */
+
+	if ((status & (SATA_STATUS_DRQ)) != 0) {
+		/* ATAPI protocol state - HP4: Transfer_Data */
+
+		/* read the byte count from the controller */
+		ctlr_count = (uint16_t)nv_get8(cmdhdl, nvp->nvp_hcyl) << 8;
+		ctlr_count |= nv_get8(cmdhdl, nvp->nvp_lcyl);
+
+		if (ctlr_count == 0) {
+			/* no data to transfer - some devices do this */
+
+			spkt->satapkt_reason = SATA_PKT_COMPLETED;
+			nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_intr_pkt_pio: done (no data)"));
+
+			return;
+		}
+
+		count = min(ctlr_count, nv_slotp->nvslot_byte_count);
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: drive_bytes 0x%x", ctlr_count));
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: byte_count 0x%x",
+		    nv_slotp->nvslot_byte_count));
+
+		/* transfer the data */
+
+		if (direction == SATA_DIR_READ) {
+			ddi_rep_get16(cmdhdl,
+			    (ushort_t *)nv_slotp->nvslot_v_addr,
+			    (ushort_t *)nvp->nvp_data, (count >> 1),
+			    DDI_DEV_NO_AUTOINCR);
+
+			ctlr_count -= count;
+
+			if (ctlr_count > 0) {
+				/* consume remainding bytes */
+
+				for (; ctlr_count > 0;
+				    ctlr_count -= 2)
+					(void) ddi_get16(cmdhdl,
+					    (ushort_t *)nvp->nvp_data);
+
+				NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+				    "nv_intr_pkt_pio: bytes remained"));
+			}
+		} else {
+			ddi_rep_put16(cmdhdl,
+			    (ushort_t *)nv_slotp->nvslot_v_addr,
+			    (ushort_t *)nvp->nvp_data, (count >> 1),
+			    DDI_DEV_NO_AUTOINCR);
+		}
+
+		nv_slotp->nvslot_v_addr += count;
+		nv_slotp->nvslot_byte_count -= count;
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: transition to HP2"));
+	} else {
+		/* still in ATAPI state - HP2 */
+
+		spkt->satapkt_reason = SATA_PKT_COMPLETED;
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_intr_pkt_pio: done"));
+	}
+}
+
+
+/*
  * ATA command, DMA data in/out
  */
 static void
@@ -4389,7 +4787,8 @@
 		nv_read_signature(nvp);
 
 		if (nvp->nvp_signature != 0) {
-			if (nvp->nvp_type == SATA_DTYPE_ATADISK) {
+			if ((nvp->nvp_type == SATA_DTYPE_ATADISK) ||
+			    (nvp->nvp_type == SATA_DTYPE_ATAPICD)) {
 				nvp->nvp_state |= NV_PORT_RESTORE;
 				nv_port_state_change(nvp,
 				    SATA_EVNT_DEVICE_RESET,
@@ -4847,3 +5246,101 @@
 		    SATA_ADDR_CPORT, 0);
 	}
 }
+
+
+/*
+ * Get request sense data and stuff it the command's sense buffer.
+ * Start a request sense command in order to get sense data to insert
+ * in the sata packet's rqsense buffer.  The command completion
+ * processing is in nv_intr_pkt_pio.
+ *
+ * The sata framework provides a function to allocate and set-up a
+ * request sense packet command. The reasons it is not being used here is:
+ * a) it cannot be called in an interrupt context and this function is
+ *    called in an interrupt context.
+ * b) it allocates DMA resources that are not used here because this is
+ *    implemented using PIO.
+ *
+ * If, in the future, this is changed to use DMA, the sata framework should
+ * be used to allocate and set-up the error retrieval (request sense)
+ * command.
+ */
+static int
+nv_start_rqsense_pio(nv_port_t *nvp, nv_slot_t *nv_slotp)
+{
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *satacmd = &spkt->satapkt_cmd;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	int cdb_len = spkt->satapkt_cmd.satacmd_acdb_len;
+
+	NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+	    "nv_start_rqsense_pio: start"));
+
+	/* clear the local request sense buffer before starting the command */
+	bzero(nv_slotp->nvslot_rqsense_buff, SATA_ATAPI_RQSENSE_LEN);
+
+	/* Write the request sense PACKET command */
+
+	/* select the drive */
+	nv_put8(cmdhdl, nvp->nvp_drvhd, satacmd->satacmd_device_reg);
+
+	/* make certain the drive selected */
+	if (nv_wait(nvp, SATA_STATUS_DRDY, SATA_STATUS_BSY,
+	    NV_SEC2USEC(5), 0) == B_FALSE) {
+		NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+		    "nv_start_rqsense_pio: drive select failed"));
+		return (NV_FAILURE);
+	}
+
+	/* set up the command */
+	nv_put8(cmdhdl, nvp->nvp_feature, 0);	/* deassert DMA and OVL */
+	nv_put8(cmdhdl, nvp->nvp_hcyl, SATA_ATAPI_MAX_BYTES_PER_DRQ >> 8);
+	nv_put8(cmdhdl, nvp->nvp_lcyl, SATA_ATAPI_MAX_BYTES_PER_DRQ & 0xff);
+	nv_put8(cmdhdl, nvp->nvp_sect, 0);
+	nv_put8(cmdhdl, nvp->nvp_count, 0);	/* no tag */
+
+	/* initiate the command by writing the command register last */
+	nv_put8(cmdhdl, nvp->nvp_cmd, SATAC_PACKET);
+
+	/* Give the host ctlr time to do its thing, according to ATA/ATAPI */
+	NV_DELAY_NSEC(400);
+
+	/*
+	 * Wait for the device to indicate that it is ready for the command
+	 * ATAPI protocol state - HP0: Check_Status_A
+	 */
+
+	if (nv_wait3(nvp, SATA_STATUS_DRQ, SATA_STATUS_BSY, /* okay */
+	    SATA_STATUS_ERR, SATA_STATUS_BSY, /* cmd failed */
+	    SATA_STATUS_DF, SATA_STATUS_BSY, /* drive failed */
+	    4000000, 0) == B_FALSE) {
+		if (nv_get8(cmdhdl, nvp->nvp_status) &
+		    (SATA_STATUS_ERR | SATA_STATUS_DF)) {
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_start_rqsense_pio: rqsense dev error (HP0)"));
+		} else {
+			NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+			    "nv_start_rqsense_pio: rqsense timeout (HP0)"));
+		}
+
+		nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+		nv_complete_io(nvp, spkt, 0);
+		nv_reset(nvp);
+
+		return (NV_FAILURE);
+	}
+
+	/*
+	 * Put the ATAPI command in the data register
+	 * ATAPI protocol state - HP1: Send_Packet
+	 */
+
+	ddi_rep_put16(cmdhdl, (ushort_t *)nv_rqsense_cdb,
+	    (ushort_t *)nvp->nvp_data,
+	    (cdb_len >> 1), DDI_DEV_NO_AUTOINCR);
+
+	NVLOG((NVDBG_ATAPI, nvp->nvp_ctlp, nvp,
+	    "nv_start_rqsense_pio: exiting into HP3"));
+
+	return (NV_SUCCESS);
+}
--- a/usr/src/uts/common/io/sata/impl/sata.c	Mon Jul 21 16:51:53 2008 -0700
+++ b/usr/src/uts/common/io/sata/impl/sata.c	Mon Jul 21 16:56:44 2008 -0700
@@ -257,6 +257,7 @@
 static	void sata_pkt_free(sata_pkt_txlate_t *);
 static	int sata_dma_buf_setup(sata_pkt_txlate_t *, int, int (*)(caddr_t),
     caddr_t, ddi_dma_attr_t *);
+static	void sata_common_free_dma_rsrcs(sata_pkt_txlate_t *);
 static	int sata_probe_device(sata_hba_inst_t *, sata_device_t *);
 static	sata_drive_info_t *sata_get_device_info(sata_hba_inst_t *,
     sata_device_t *);
@@ -1950,7 +1951,6 @@
 		if ((bp == NULL) || (bp->b_bcount == 0)) {
 			return (pkt);
 		}
-		ASSERT(spx->txlt_buf_dma_handle != NULL);
 
 		/* Pkt is available already: spx->txlt_scsi_pkt == pkt; */
 	}
@@ -2600,31 +2600,8 @@
 
 	spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
 
-	if (spx->txlt_buf_dma_handle != NULL) {
-		if (spx->txlt_tmp_buf != NULL)  {
-			ASSERT(spx->txlt_tmp_buf_handle != 0);
-			/*
-			 * Intermediate DMA buffer was allocated.
-			 * Free allocated buffer and associated access handle.
-			 */
-			ddi_dma_mem_free(&spx->txlt_tmp_buf_handle);
-			spx->txlt_tmp_buf = NULL;
-		}
-		/*
-		 * Free DMA resources - cookies and handles
-		 */
-		if (spx->txlt_dma_cookie_list != NULL) {
-			if (spx->txlt_dma_cookie_list !=
-			    &spx->txlt_dma_cookie) {
-				(void) kmem_free(spx->txlt_dma_cookie_list,
-				    spx->txlt_dma_cookie_list_len *
-				    sizeof (ddi_dma_cookie_t));
-				spx->txlt_dma_cookie_list = NULL;
-			}
-		}
-		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
-		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
-	}
+	sata_common_free_dma_rsrcs(spx);
+
 	spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = NULL;
 	sata_pkt_free(spx);
 
@@ -2647,32 +2624,7 @@
 	ASSERT(pkt != NULL);
 	spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
 
-	if (spx->txlt_buf_dma_handle != NULL) {
-		if (spx->txlt_tmp_buf != NULL)  {
-			/*
-			 * Intermediate DMA buffer was allocated.
-			 * Free allocated buffer and associated access handle.
-			 */
-			ddi_dma_mem_free(&spx->txlt_tmp_buf_handle);
-			spx->txlt_tmp_buf = NULL;
-		}
-		/*
-		 * Free DMA resources - cookies and handles
-		 */
-		/* ASSERT(spx->txlt_dma_cookie_list != NULL); */
-		if (spx->txlt_dma_cookie_list != NULL) {
-			if (spx->txlt_dma_cookie_list !=
-			    &spx->txlt_dma_cookie) {
-				(void) kmem_free(spx->txlt_dma_cookie_list,
-				    spx->txlt_dma_cookie_list_len *
-				    sizeof (ddi_dma_cookie_t));
-				spx->txlt_dma_cookie_list = NULL;
-			}
-		}
-		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
-		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
-		spx->txlt_buf_dma_handle = NULL;
-	}
+	sata_common_free_dma_rsrcs(spx);
 }
 
 /*
@@ -7536,13 +7488,18 @@
 		SATADBG1(SATA_DBG_ATAPI, sata_hba,
 		    "sata_get_atapi_inquiry_data: "
 		    "Packet completed successfully - ret: %02x", rval);
-		/*
-		 * Sync buffer. Handle is in usual place in translate struct.
+		if (spx->txlt_buf_dma_handle != NULL) {
+			/*
+			 * Sync buffer. Handle is in usual place in translate
+			 * struct.
+			 */
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORCPU);
+			ASSERT(rval == DDI_SUCCESS);
+		}
+		/*
 		 * Normal completion - copy data into caller's buffer
 		 */
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORCPU);
-		ASSERT(rval == DDI_SUCCESS);
 		bcopy(bp->b_un.b_addr, (uint8_t *)inq,
 		    sizeof (struct scsi_inquiry));
 #ifdef SATA_DEBUG
@@ -7727,12 +7684,14 @@
 	}
 	mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
 
-	/*
-	 * Sync buffer. Handle is in usual place in translate struct.
-	 */
-	rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-	    DDI_DMA_SYNC_FORCPU);
-	ASSERT(rval == DDI_SUCCESS);
+	if (spx->txlt_buf_dma_handle != NULL) {
+		/*
+		 * Sync buffer. Handle is in usual place in translate struct.
+		 */
+		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+		    DDI_DMA_SYNC_FORCPU);
+		ASSERT(rval == DDI_SUCCESS);
+	}
 	if (spkt->satapkt_reason == SATA_PKT_COMPLETED) {
 		sata_log(sata_hba_inst, CE_WARN,
 		    "sata_test_atapi_packet_command: "
@@ -9570,25 +9529,26 @@
 sata_free_local_buffer(sata_pkt_txlate_t *spx)
 {
 	ASSERT(spx->txlt_sata_pkt != NULL);
-	ASSERT(spx->txlt_dma_cookie_list != NULL);
-	ASSERT(spx->txlt_dma_cookie_list_len != 0);
-	ASSERT(spx->txlt_buf_dma_handle != NULL);
 	ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp != NULL);
 
 	spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 0;
 	spx->txlt_sata_pkt->satapkt_cmd.satacmd_dma_cookie_list = NULL;
 
-	/* Free DMA resources */
-	(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
-	ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
-	spx->txlt_buf_dma_handle = 0;
-
-	if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
-		kmem_free(spx->txlt_dma_cookie_list,
-		    spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t));
-		spx->txlt_dma_cookie_list = NULL;
-		spx->txlt_dma_cookie_list_len = 0;
-	}
+	if (spx->txlt_buf_dma_handle != NULL) {
+		/* Free DMA resources */
+		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
+		ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
+		spx->txlt_buf_dma_handle = 0;
+
+		if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
+			kmem_free(spx->txlt_dma_cookie_list,
+			    spx->txlt_dma_cookie_list_len *
+			    sizeof (ddi_dma_cookie_t));
+			spx->txlt_dma_cookie_list = NULL;
+			spx->txlt_dma_cookie_list_len = 0;
+		}
+	}
+
 	/* Free buffer */
 	scsi_free_consistent_buf(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp);
 	spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = NULL;
@@ -10111,6 +10071,58 @@
 	return (DDI_SUCCESS);
 }
 
+/*
+ * Common routine for releasing DMA resources
+ */
+static void
+sata_common_free_dma_rsrcs(sata_pkt_txlate_t *spx)
+{
+	if (spx->txlt_buf_dma_handle != NULL) {
+		if (spx->txlt_tmp_buf != NULL)  {
+			/*
+			 * Intermediate DMA buffer was allocated.
+			 * Free allocated buffer and associated access handle.
+			 */
+			ddi_dma_mem_free(&spx->txlt_tmp_buf_handle);
+			spx->txlt_tmp_buf = NULL;
+		}
+		/*
+		 * Free DMA resources - cookies and handles
+		 */
+		/* ASSERT(spx->txlt_dma_cookie_list != NULL); */
+		if (spx->txlt_dma_cookie_list != NULL) {
+			if (spx->txlt_dma_cookie_list !=
+			    &spx->txlt_dma_cookie) {
+				(void) kmem_free(spx->txlt_dma_cookie_list,
+				    spx->txlt_dma_cookie_list_len *
+				    sizeof (ddi_dma_cookie_t));
+				spx->txlt_dma_cookie_list = NULL;
+			}
+		}
+		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
+		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
+		spx->txlt_buf_dma_handle = NULL;
+	}
+}
+
+/*
+ * Free DMA resources
+ * Used by the HBA driver to release DMA resources that it does not use.
+ *
+ * Returns Void
+ */
+void
+sata_free_dma_resources(sata_pkt_t *sata_pkt)
+{
+	sata_pkt_txlate_t *spx;
+
+	if (sata_pkt == NULL)
+		return;
+
+	spx = (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
+
+	sata_common_free_dma_rsrcs(spx);
+}
 
 /*
  * Fetch Device Identify data.
@@ -10203,9 +10215,11 @@
 
 	if (rval == SATA_TRAN_ACCEPTED &&
 	    spkt->satapkt_reason == SATA_PKT_COMPLETED) {
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORKERNEL);
-		ASSERT(rval == DDI_SUCCESS);
+		if (spx->txlt_buf_dma_handle != NULL) {
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORKERNEL);
+			ASSERT(rval == DDI_SUCCESS);
+		}
 		if ((((sata_id_t *)(bp->b_un.b_addr))->ai_config &
 		    SATA_INCOMPLETE_DATA) == SATA_INCOMPLETE_DATA) {
 			SATA_LOG_D((sata_hba_inst, CE_WARN,
@@ -12946,9 +12960,11 @@
 	} else {
 		mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
 		    sdinfo->satadrv_addr.cport)));
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORKERNEL);
-		ASSERT(rval == DDI_SUCCESS);
+		if (spx->txlt_buf_dma_handle != NULL) {
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORKERNEL);
+			ASSERT(rval == DDI_SUCCESS);
+		}
 		bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)smart_data,
 		    sizeof (struct smart_data));
 	}
@@ -13051,9 +13067,11 @@
 		mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
 		    sdinfo->satadrv_addr.cport)));
 
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORKERNEL);
-		ASSERT(rval == DDI_SUCCESS);
+		if (spx->txlt_buf_dma_handle != NULL) {
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORKERNEL);
+			ASSERT(rval == DDI_SUCCESS);
+		}
 		bcopy(scmd->satacmd_bp->b_un.b_addr,
 		    (uint8_t *)ext_selftest_log,
 		    sizeof (struct smart_ext_selftest_log));
@@ -13152,9 +13170,11 @@
 	} else {
 		mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
 		    sdinfo->satadrv_addr.cport)));
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORKERNEL);
-		ASSERT(rval == DDI_SUCCESS);
+		if (spx->txlt_buf_dma_handle != NULL) {
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORKERNEL);
+			ASSERT(rval == DDI_SUCCESS);
+		}
 		bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)selftest_log,
 		    sizeof (struct smart_selftest_log));
 		rval = 0;
@@ -13252,9 +13272,11 @@
 		mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
 		    sdinfo->satadrv_addr.cport)));
 
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORKERNEL);
-		ASSERT(rval == DDI_SUCCESS);
+		if (spx->txlt_buf_dma_handle != NULL) {
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORKERNEL);
+			ASSERT(rval == DDI_SUCCESS);
+		}
 		bcopy(scmd->satacmd_bp->b_un.b_addr, smart_log, log_size * 512);
 		rval = 0;
 	}
@@ -13351,9 +13373,11 @@
 	} else {
 		mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
 		    sdinfo->satadrv_addr.cport)));
-		rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
-		    DDI_DMA_SYNC_FORKERNEL);
-		ASSERT(rval == DDI_SUCCESS);
+		if (spx->txlt_buf_dma_handle != NULL) {
+			rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
+			    DDI_DMA_SYNC_FORKERNEL);
+			ASSERT(rval == DDI_SUCCESS);
+		}
 		bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)logdir,
 		    sizeof (struct read_log_ext_directory));
 		rval = 0;
--- a/usr/src/uts/common/sys/sata/adapters/nv_sata/nv_sata.h	Mon Jul 21 16:51:53 2008 -0700
+++ b/usr/src/uts/common/sys/sata/adapters/nv_sata/nv_sata.h	Mon Jul 21 16:56:44 2008 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -174,6 +174,7 @@
 	caddr_t		nvslot_v_addr;	/* I/O buffer address */
 	size_t		nvslot_byte_count; /* # bytes left to read/write */
 	sata_pkt_t	*nvslot_spkt;
+	uint8_t		nvslot_rqsense_buff[SATA_ATAPI_RQSENSE_LEN];
 	clock_t		nvslot_stime;
 	int		(*nvslot_start)(nv_port_t *nvp, int queue);
 	void		(*nvslot_intr)(nv_port_t *nvp,
@@ -186,7 +187,8 @@
  * nvslot_flags
  */
 #define	NVSLOT_COMPLETE 0x01
-#define	NVSLOT_NCQ	0x02 /* NCQ is active */
+#define	NVSLOT_NCQ	0x02	/* NCQ is active */
+#define	NVSLOT_RQSENSE	0x04	/* processing request sense */
 
 /*
  * state values for nv_attach
@@ -228,6 +230,7 @@
 #define	NVDBG_COOKIES	0x1000
 #define	NVDBG_HOT	0x2000
 #define	NVDBG_PROBE	0x4000
+#define	NVDBG_ATAPI	0x8000
 
 #ifdef DEBUG
 #define	NVLOG(a) nv_log a
--- a/usr/src/uts/common/sys/sata/sata_hba.h	Mon Jul 21 16:51:53 2008 -0700
+++ b/usr/src/uts/common/sys/sata/sata_hba.h	Mon Jul 21 16:56:44 2008 -0700
@@ -709,6 +709,7 @@
 void 	sata_hba_event_notify(dev_info_t *, sata_device_t *, int);
 sata_pkt_t *sata_get_error_retrieval_pkt(dev_info_t *, sata_device_t *, int);
 void	sata_free_error_retrieval_pkt(sata_pkt_t *);
+void	sata_free_dma_resources(sata_pkt_t *);
 
 
 #ifdef	__cplusplus