Mercurial > illumos > git > illumos-joyent
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