This is purely for comment and testing, not for merging (yet?). A common recipe in several vendor drivers (either GPL'd, or I have NDA'd access to use them as a documentation-like reference) for ATAPI was slightly different from ours. This recipe can be found in atapi_tf_xfer_size(), and it's slightly different from Alan's. This patch also adds some byte count checks, consolidated ata_pio_use_silly() use, and adds a dmadir-related FIXME. The 'xfer-size' branch of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git xfer-size to receive the following updates: drivers/ata/libata-core.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata-eh.c | 11 +-------- drivers/ata/libata-scsi.c | 48 +++++++++------------------------------- drivers/ata/libata.h | 7 ++++++ 4 files changed, 72 insertions(+), 47 deletions(-) Jeff Garzik (1): [libata] Standardize ATAPI byte count [limit] handling diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 63035d7..5c616a8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5258,6 +5258,55 @@ next_sg: goto next_sg; } +void atapi_tf_xfer_size(struct ata_taskfile *tf, + unsigned int total_xfer_len, + bool dma) +{ + if (dma) { + tf->protocol = ATA_PROT_ATAPI_DMA; + tf->feature |= ATAPI_PKT_DMA; + tf->lbam = 0; + tf->lbah = 0; + } else { + tf->protocol = ATA_PROT_ATAPI; + tf->feature &= ~ATAPI_PKT_DMA; + if (total_xfer_len <= 0xffff) { + tf->lbam = total_xfer_len; + tf->lbah = total_xfer_len >> 8; + } else { + tf->lbam = 0xff; + tf->lbah = 0xff; + } + } +} + +static bool atapi_valid_bcount(const struct ata_taskfile *tf, + const struct ata_taskfile *result_tf) +{ + unsigned int ibyte, obyte, lo, hi; + + lo = tf->lbam; + hi = tf->lbam; + ibyte = (hi << 8) | lo; + + lo = result_tf->lbam; + hi = result_tf->lbam; + obyte = (hi << 8) | lo; + + if (unlikely(obyte > ibyte)) + return false; + if (unlikely(obyte == 0)) + return false; + if (unlikely(obyte > 0xfffe)) + return false; + + /* another test, omitted due to lack of data point: + * obyte should be even, if it is not the last DRQ block + */ + + return true; +} + /** * atapi_pio_bytes - Transfer data from/to the ATAPI device. * @qc: Command on going @@ -5282,6 +5331,10 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc) * So, the correctness of qc->result_tf is not affected. */ ap->ops->tf_read(ap, &qc->result_tf); + + if (!atapi_valid_bcount(&qc->tf, &qc->result_tf)) + goto err_out; + ireason = qc->result_tf.nsect; bc_lo = qc->result_tf.lbam; bc_hi = qc->result_tf.lbah; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 8d64f8f..505b2e9 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1352,16 +1352,7 @@ static unsigned int atapi_eh_request_sense(struct ata_queued_cmd *qc) tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf.command = ATA_CMD_PACKET; - - /* is it pointless to prefer PIO for "safety reasons"? */ - if (ap->flags & ATA_FLAG_PIO_DMA) { - tf.protocol = ATA_PROT_ATAPI_DMA; - tf.feature |= ATAPI_PKT_DMA; - } else { - tf.protocol = ATA_PROT_ATAPI; - tf.lbam = (8 * 1024) & 0xff; - tf.lbah = (8 * 1024) >> 8; - } + atapi_tf_xfer_size(&tf, SCSI_SENSE_BUFFERSIZE, ata_pio_use_silly(ap)); return ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE, sense_buf, SCSI_SENSE_BUFFERSIZE, 0); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index fc89590..bec28c2 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2313,12 +2313,6 @@ static void atapi_sense_complete(struct ata_queued_cmd *qc) ata_qc_free(qc); } -/* is it pointless to prefer PIO for "safety reasons"? */ -static inline int ata_pio_use_silly(struct ata_port *ap) -{ - return (ap->flags & ATA_FLAG_PIO_DMA); -} - static void atapi_request_sense(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; @@ -2346,15 +2340,9 @@ static void atapi_request_sense(struct ata_queued_cmd *qc) qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; qc->tf.command = ATA_CMD_PACKET; + atapi_tf_xfer_size(&qc->tf, SCSI_SENSE_BUFFERSIZE, + ata_pio_use_silly(ap)); - if (ata_pio_use_silly(ap)) { - qc->tf.protocol = ATA_PROT_ATAPI_DMA; - qc->tf.feature |= ATAPI_PKT_DMA; - } else { - qc->tf.protocol = ATA_PROT_ATAPI; - qc->tf.lbam = SCSI_SENSE_BUFFERSIZE; - qc->tf.lbah = 0; - } qc->nbytes = SCSI_SENSE_BUFFERSIZE; qc->complete_fn = atapi_sense_complete; @@ -2462,7 +2450,6 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) struct ata_device *dev = qc->dev; int using_pio = (dev->flags & ATA_DFLAG_PIO); int nodata = (scmd->sc_data_direction == DMA_NONE); - unsigned int nbytes; memset(qc->cdb, 0, dev->cdb_len); memcpy(qc->cdb, scmd->cmnd, scmd->cmd_len); @@ -2482,31 +2469,18 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) if (!using_pio && ata_check_atapi_dma(qc)) using_pio = 1; - /* Some controller variants snoop this value for Packet transfers - to do state machine and FIFO management. Thus we want to set it - properly, and for DMA where it is effectively meaningless */ - nbytes = min(qc->nbytes, (unsigned int)63 * 1024); - - qc->tf.lbam = (nbytes & 0xFF); - qc->tf.lbah = (nbytes >> 8); - - if (using_pio || nodata) { - /* no data, or PIO data xfer */ - if (nodata) - qc->tf.protocol = ATA_PROT_ATAPI_NODATA; - else - qc->tf.protocol = ATA_PROT_ATAPI; - } else { - /* DMA data xfer */ - qc->tf.protocol = ATA_PROT_ATAPI_DMA; - qc->tf.feature |= ATAPI_PKT_DMA; + if (nodata) + qc->tf.protocol = ATA_PROT_ATAPI_NODATA; + else + atapi_tf_xfer_size(&qc->tf, qc->nbytes, !using_pio); - if (atapi_dmadir && (scmd->sc_data_direction != DMA_TO_DEVICE)) - /* some SATA bridges need us to indicate data xfer direction */ - qc->tf.feature |= ATAPI_DMADIR; + /* FIXME: also check dmadir capability bit added in ATA-7 */ + if (atapi_dmadir && (qc->tf.protocol == ATA_PROT_ATAPI_DMA) && + (scmd->sc_data_direction != DMA_TO_DEVICE)) { + /* some SATA bridges need us to indicate data xfer direction */ + qc->tf.feature |= ATAPI_DMADIR; } - /* FIXME: We need to translate 0x05 READ_BLOCK_LIMITS to a MODE_SENSE as ATAPI tape drives don't get this right otherwise */ return 0; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 0e6cf3a..f62029a 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -103,6 +103,8 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy); extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); +extern void atapi_tf_xfer_size(struct ata_taskfile *tf, + unsigned int total_xfer_len, bool dma); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI @@ -188,5 +190,10 @@ extern void ata_eh_finish(struct ata_port *ap); /* libata-sff.c */ extern u8 ata_irq_on(struct ata_port *ap); +/* is it pointless to prefer PIO for "safety reasons"? */ +static inline int ata_pio_use_silly(struct ata_port *ap) +{ + return (ap->flags & ATA_FLAG_PIO_DMA); +} #endif /* __LIBATA_H__ */ - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html