aboutsummaryrefslogtreecommitdiff
path: root/drivers/ata/libata-scsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-scsi.c')
-rw-r--r--drivers/ata/libata-scsi.c459
1 files changed, 333 insertions, 126 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 567859ce0512..8b61d63ab0be 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -270,11 +270,52 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR,
ata_scsi_park_show, ata_scsi_park_store);
EXPORT_SYMBOL_GPL(dev_attr_unload_heads);
-static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq)
+void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd,
+ u8 sk, u8 asc, u8 ascq)
{
+ bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE);
+
+ if (!cmd)
+ return;
+
cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
- scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq);
+ scsi_build_sense_buffer(d_sense, cmd->sense_buffer, sk, asc, ascq);
+}
+
+void ata_scsi_set_sense_information(struct ata_device *dev,
+ struct scsi_cmnd *cmd,
+ const struct ata_taskfile *tf)
+{
+ u64 information;
+
+ if (!cmd)
+ return;
+
+ information = ata_tf_read_block(tf, dev);
+ if (information == U64_MAX)
+ return;
+
+ scsi_set_sense_information(cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, information);
+}
+
+static void ata_scsi_set_invalid_field(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u16 field, u8 bit)
+{
+ ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x24, 0x0);
+ /* "Invalid field in cbd" */
+ scsi_set_sense_field_pointer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ field, bit, 1);
+}
+
+static void ata_scsi_set_invalid_parameter(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u16 field)
+{
+ /* "Invalid field in parameter list" */
+ ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x26, 0x0);
+ scsi_set_sense_field_pointer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ field, 0xff, 0);
}
static ssize_t
@@ -364,10 +405,10 @@ struct device_attribute *ata_common_sdev_attrs[] = {
};
EXPORT_SYMBOL_GPL(ata_common_sdev_attrs);
-static void ata_scsi_invalid_field(struct scsi_cmnd *cmd)
+static void ata_scsi_invalid_field(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u16 field)
{
- ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0);
- /* "Invalid field in cbd" */
+ ata_scsi_set_invalid_field(dev, cmd, field, 0xff);
cmd->scsi_done(cmd);
}
@@ -980,6 +1021,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc)
unsigned char *sb = cmd->sense_buffer;
unsigned char *desc = sb + 8;
int verbose = qc->ap->ops->error_handler == NULL;
+ u8 sense_key, asc, ascq;
memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
@@ -992,47 +1034,71 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc)
if (qc->err_mask ||
tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) {
ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature,
- &sb[1], &sb[2], &sb[3], verbose);
- sb[1] &= 0x0f;
+ &sense_key, &asc, &ascq, verbose);
+ ata_scsi_set_sense(qc->dev, cmd, sense_key, asc, ascq);
} else {
- sb[1] = RECOVERED_ERROR;
- sb[2] = 0;
- sb[3] = 0x1D;
+ /*
+ * ATA PASS-THROUGH INFORMATION AVAILABLE
+ * Always in descriptor format sense.
+ */
+ scsi_build_sense_buffer(1, cmd->sense_buffer,
+ RECOVERED_ERROR, 0, 0x1D);
}
- /*
- * Sense data is current and format is descriptor.
- */
- sb[0] = 0x72;
-
- desc[0] = 0x09;
-
- /* set length of additional sense data */
- sb[7] = 14;
- desc[1] = 12;
-
- /*
- * Copy registers into sense buffer.
- */
- desc[2] = 0x00;
- desc[3] = tf->feature; /* == error reg */
- desc[5] = tf->nsect;
- desc[7] = tf->lbal;
- desc[9] = tf->lbam;
- desc[11] = tf->lbah;
- desc[12] = tf->device;
- desc[13] = tf->command; /* == status reg */
+ if ((cmd->sense_buffer[0] & 0x7f) >= 0x72) {
+ u8 len;
+
+ /* descriptor format */
+ len = sb[7];
+ desc = (char *)scsi_sense_desc_find(sb, len + 8, 9);
+ if (!desc) {
+ if (SCSI_SENSE_BUFFERSIZE < len + 14)
+ return;
+ sb[7] = len + 14;
+ desc = sb + 8 + len;
+ }
+ desc[0] = 9;
+ desc[1] = 12;
+ /*
+ * Copy registers into sense buffer.
+ */
+ desc[2] = 0x00;
+ desc[3] = tf->feature; /* == error reg */
+ desc[5] = tf->nsect;
+ desc[7] = tf->lbal;
+ desc[9] = tf->lbam;
+ desc[11] = tf->lbah;
+ desc[12] = tf->device;
+ desc[13] = tf->command; /* == status reg */
- /*
- * Fill in Extend bit, and the high order bytes
- * if applicable.
- */
- if (tf->flags & ATA_TFLAG_LBA48) {
- desc[2] |= 0x01;
- desc[4] = tf->hob_nsect;
- desc[6] = tf->hob_lbal;
- desc[8] = tf->hob_lbam;
- desc[10] = tf->hob_lbah;
+ /*
+ * Fill in Extend bit, and the high order bytes
+ * if applicable.
+ */
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ desc[2] |= 0x01;
+ desc[4] = tf->hob_nsect;
+ desc[6] = tf->hob_lbal;
+ desc[8] = tf->hob_lbam;
+ desc[10] = tf->hob_lbah;
+ }
+ } else {
+ /* Fixed sense format */
+ desc[0] = tf->feature;
+ desc[1] = tf->command; /* status */
+ desc[2] = tf->device;
+ desc[3] = tf->nsect;
+ desc[0] = 0;
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ desc[8] |= 0x80;
+ if (tf->hob_nsect)
+ desc[8] |= 0x40;
+ if (tf->hob_lbal || tf->hob_lbam || tf->hob_lbah)
+ desc[8] |= 0x20;
+ }
+ desc[9] = tf->lbal;
+ desc[10] = tf->lbam;
+ desc[11] = tf->lbah;
}
}
@@ -1052,41 +1118,35 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc)
struct scsi_cmnd *cmd = qc->scsicmd;
struct ata_taskfile *tf = &qc->result_tf;
unsigned char *sb = cmd->sense_buffer;
- unsigned char *desc = sb + 8;
int verbose = qc->ap->ops->error_handler == NULL;
u64 block;
+ u8 sense_key, asc, ascq;
memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
- /* sense data is current and format is descriptor */
- sb[0] = 0x72;
-
/* Use ata_to_sense_error() to map status register bits
* onto sense key, asc & ascq.
*/
if (qc->err_mask ||
tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) {
ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature,
- &sb[1], &sb[2], &sb[3], verbose);
- sb[1] &= 0x0f;
+ &sense_key, &asc, &ascq, verbose);
+ ata_scsi_set_sense(dev, cmd, sense_key, asc, ascq);
+ } else {
+ /* Could not decode error */
+ ata_dev_warn(dev, "could not decode error status 0x%x err_mask 0x%x\n",
+ tf->command, qc->err_mask);
+ ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0);
+ return;
}
block = ata_tf_read_block(&qc->result_tf, dev);
+ if (block == U64_MAX)
+ return;
- /* information sense data descriptor */
- sb[7] = 12;
- desc[0] = 0x00;
- desc[1] = 10;
-
- desc[2] |= 0x80; /* valid */
- desc[6] = block >> 40;
- desc[7] = block >> 32;
- desc[8] = block >> 24;
- desc[9] = block >> 16;
- desc[10] = block >> 8;
- desc[11] = block;
+ scsi_set_sense_information(sb, SCSI_SENSE_BUFFERSIZE, block);
}
static void ata_scsi_sdev_config(struct scsi_device *sdev)
@@ -1343,19 +1403,29 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
struct scsi_cmnd *scmd = qc->scsicmd;
struct ata_taskfile *tf = &qc->tf;
const u8 *cdb = scmd->cmnd;
+ u16 fp;
+ u8 bp = 0xff;
- if (scmd->cmd_len < 5)
+ if (scmd->cmd_len < 5) {
+ fp = 4;
goto invalid_fld;
+ }
tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
tf->protocol = ATA_PROT_NODATA;
if (cdb[1] & 0x1) {
; /* ignore IMMED bit, violates sat-r05 */
}
- if (cdb[4] & 0x2)
+ if (cdb[4] & 0x2) {
+ fp = 4;
+ bp = 1;
goto invalid_fld; /* LOEJ bit set not supported */
- if (((cdb[4] >> 4) & 0xf) != 0)
+ }
+ if (((cdb[4] >> 4) & 0xf) != 0) {
+ fp = 4;
+ bp = 3;
goto invalid_fld; /* power conditions not supported */
+ }
if (cdb[4] & 0x1) {
tf->nsect = 1; /* 1 sector, lba=0 */
@@ -1401,8 +1471,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
return 0;
invalid_fld:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0);
- /* "Invalid field in cbd" */
+ ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp);
return 1;
skip:
scmd->result = SAM_STAT_GOOD;
@@ -1553,20 +1622,27 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc)
const u8 *cdb = scmd->cmnd;
u64 block;
u32 n_block;
+ u16 fp;
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf->protocol = ATA_PROT_NODATA;
if (cdb[0] == VERIFY) {
- if (scmd->cmd_len < 10)
+ if (scmd->cmd_len < 10) {
+ fp = 9;
goto invalid_fld;
+ }
scsi_10_lba_len(cdb, &block, &n_block);
} else if (cdb[0] == VERIFY_16) {
- if (scmd->cmd_len < 16)
+ if (scmd->cmd_len < 16) {
+ fp = 15;
goto invalid_fld;
+ }
scsi_16_lba_len(cdb, &block, &n_block);
- } else
+ } else {
+ fp = 0;
goto invalid_fld;
+ }
if (!n_block)
goto nothing_to_do;
@@ -1640,12 +1716,11 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc)
return 0;
invalid_fld:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0);
- /* "Invalid field in cbd" */
+ ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff);
return 1;
out_of_range:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0);
+ ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x0);
/* "Logical Block Address out of range" */
return 1;
@@ -1680,6 +1755,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
u64 block;
u32 n_block;
int rc;
+ u16 fp = 0;
if (cdb[0] == WRITE_10 || cdb[0] == WRITE_6 || cdb[0] == WRITE_16)
tf_flags |= ATA_TFLAG_WRITE;
@@ -1688,16 +1764,20 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
switch (cdb[0]) {
case READ_10:
case WRITE_10:
- if (unlikely(scmd->cmd_len < 10))
+ if (unlikely(scmd->cmd_len < 10)) {
+ fp = 9;
goto invalid_fld;
+ }
scsi_10_lba_len(cdb, &block, &n_block);
if (cdb[1] & (1 << 3))
tf_flags |= ATA_TFLAG_FUA;
break;
case READ_6:
case WRITE_6:
- if (unlikely(scmd->cmd_len < 6))
+ if (unlikely(scmd->cmd_len < 6)) {
+ fp = 5;
goto invalid_fld;
+ }
scsi_6_lba_len(cdb, &block, &n_block);
/* for 6-byte r/w commands, transfer length 0
@@ -1708,14 +1788,17 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
break;
case READ_16:
case WRITE_16:
- if (unlikely(scmd->cmd_len < 16))
+ if (unlikely(scmd->cmd_len < 16)) {
+ fp = 15;
goto invalid_fld;
+ }
scsi_16_lba_len(cdb, &block, &n_block);
if (cdb[1] & (1 << 3))
tf_flags |= ATA_TFLAG_FUA;
break;
default:
DPRINTK("no-byte command\n");
+ fp = 0;
goto invalid_fld;
}
@@ -1742,12 +1825,11 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
goto out_of_range;
/* treat all other errors as -EINVAL, fall through */
invalid_fld:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0);
- /* "Invalid field in cbd" */
+ ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff);
return 1;
out_of_range:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0);
+ ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x0);
/* "Logical Block Address out of range" */
return 1;
@@ -1784,6 +1866,8 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
((cdb[2] & 0x20) || need_sense))
ata_gen_passthru_sense(qc);
+ else if (qc->flags & ATA_QCFLAG_SENSE_VALID)
+ cmd->result = SAM_STAT_CHECK_CONDITION;
else if (need_sense)
ata_gen_ata_sense(qc);
else
@@ -2317,6 +2401,7 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)
/**
* ata_msense_ctl_mode - Simulate MODE SENSE control mode page
+ * @dev: ATA device of interest
* @buf: output buffer
* @changeable: whether changeable parameters are requested
*
@@ -2325,9 +2410,12 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)
* LOCKING:
* None.
*/
-static unsigned int ata_msense_ctl_mode(u8 *buf, bool changeable)
+static unsigned int ata_msense_ctl_mode(struct ata_device *dev, u8 *buf,
+ bool changeable)
{
modecpy(buf, def_control_mpage, sizeof(def_control_mpage), changeable);
+ if (changeable && (dev->flags & ATA_DFLAG_D_SENSE))
+ buf[2] |= (1 << 2); /* Descriptor sense requested */
return sizeof(def_control_mpage);
}
@@ -2395,7 +2483,8 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
};
u8 pg, spg;
unsigned int ebd, page_control, six_byte;
- u8 dpofua;
+ u8 dpofua, bp = 0xff;
+ u16 fp;
VPRINTK("ENTER\n");
@@ -2414,6 +2503,8 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
case 3: /* saved */
goto saving_not_supp;
default:
+ fp = 2;
+ bp = 6;
goto invalid_fld;
}
@@ -2428,8 +2519,10 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
* No mode subpages supported (yet) but asking for _all_
* subpages may be valid
*/
- if (spg && (spg != ALL_SUB_MPAGES))
+ if (spg && (spg != ALL_SUB_MPAGES)) {
+ fp = 3;
goto invalid_fld;
+ }
switch(pg) {
case RW_RECOVERY_MPAGE:
@@ -2441,16 +2534,17 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
break;
case CONTROL_MPAGE:
- p += ata_msense_ctl_mode(p, page_control == 1);
+ p += ata_msense_ctl_mode(args->dev, p, page_control == 1);
break;
case ALL_MPAGES:
p += ata_msense_rw_recovery(p, page_control == 1);
p += ata_msense_caching(args->id, p, page_control == 1);
- p += ata_msense_ctl_mode(p, page_control == 1);
+ p += ata_msense_ctl_mode(args->dev, p, page_control == 1);
break;
default: /* invalid page code */
+ fp = 2;
goto invalid_fld;
}
@@ -2480,12 +2574,11 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
return 0;
invalid_fld:
- ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x24, 0x0);
- /* "Invalid field in cbd" */
+ ata_scsi_set_invalid_field(dev, args->cmd, fp, bp);
return 1;
saving_not_supp:
- ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x39, 0x0);
+ ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x39, 0x0);
/* "Saving parameters not supported" */
return 1;
}
@@ -2942,9 +3035,12 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
struct scsi_cmnd *scmd = qc->scsicmd;
struct ata_device *dev = qc->dev;
const u8 *cdb = scmd->cmnd;
+ u16 fp;
- if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN)
+ if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN) {
+ fp = 1;
goto invalid_fld;
+ }
/* enable LBA */
tf->flags |= ATA_TFLAG_LBA;
@@ -3008,8 +3104,10 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
case ATA_CMD_READ_LONG_ONCE:
case ATA_CMD_WRITE_LONG:
case ATA_CMD_WRITE_LONG_ONCE:
- if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1)
+ if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1) {
+ fp = 1;
goto invalid_fld;
+ }
qc->sect_size = scsi_bufflen(scmd);
break;
@@ -3072,12 +3170,16 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
ata_qc_set_pc_nbytes(qc);
/* We may not issue DMA commands if no DMA mode is set */
- if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0)
+ if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0) {
+ fp = 1;
goto invalid_fld;
+ }
/* sanity check for pio multi commands */
- if ((cdb[1] & 0xe0) && !is_multi_taskfile(tf))
+ if ((cdb[1] & 0xe0) && !is_multi_taskfile(tf)) {
+ fp = 1;
goto invalid_fld;
+ }
if (is_multi_taskfile(tf)) {
unsigned int multi_count = 1 << (cdb[1] >> 5);
@@ -3098,8 +3200,10 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
* ->set_dmamode(), and ->post_set_mode() hooks).
*/
if (tf->command == ATA_CMD_SET_FEATURES &&
- tf->feature == SETFEATURES_XFER)
+ tf->feature == SETFEATURES_XFER) {
+ fp = (cdb[0] == ATA_16) ? 4 : 3;
goto invalid_fld;
+ }
/*
* Filter TPM commands by default. These provide an
@@ -3116,14 +3220,15 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
* so that we comply with the TC consortium stated goal that the user
* can turn off TC features of their system.
*/
- if (tf->command >= 0x5C && tf->command <= 0x5F && !libata_allow_tpm)
+ if (tf->command >= 0x5C && tf->command <= 0x5F && !libata_allow_tpm) {
+ fp = (cdb[0] == ATA_16) ? 14 : 9;
goto invalid_fld;
+ }
return 0;
invalid_fld:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00);
- /* "Invalid field in cdb" */
+ ata_scsi_set_invalid_field(dev, scmd, fp, 0xff);
return 1;
}
@@ -3137,25 +3242,32 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
u32 n_block;
u32 size;
void *buf;
+ u16 fp;
+ u8 bp = 0xff;
/* we may not issue DMA commands if no DMA mode is set */
if (unlikely(!dev->dma_mode))
- goto invalid_fld;
+ goto invalid_opcode;
- if (unlikely(scmd->cmd_len < 16))
+ if (unlikely(scmd->cmd_len < 16)) {
+ fp = 15;
goto invalid_fld;
+ }
scsi_16_lba_len(cdb, &block, &n_block);
/* for now we only support WRITE SAME with the unmap bit set */
- if (unlikely(!(cdb[1] & 0x8)))
+ if (unlikely(!(cdb[1] & 0x8))) {
+ fp = 1;
+ bp = 3;
goto invalid_fld;
+ }
/*
* WRITE SAME always has a sector sized buffer as payload, this
* should never be a multiple entry S/G list.
*/
if (!scsi_sg_count(scmd))
- goto invalid_fld;
+ goto invalid_param_len;
buf = page_address(sg_page(scsi_sglist(scmd)));
size = ata_set_lba_range_entries(buf, 512, block, n_block);
@@ -3186,9 +3298,16 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
return 0;
- invalid_fld:
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00);
- /* "Invalid field in cdb" */
+invalid_fld:
+ ata_scsi_set_invalid_field(dev, scmd, fp, bp);
+ return 1;
+invalid_param_len:
+ /* "Parameter list length error" */
+ ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0);
+ return 1;
+invalid_opcode:
+ /* "Invalid command operation code" */
+ ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0);
return 1;
}
@@ -3197,6 +3316,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
* @qc: Storage for translated ATA taskfile
* @buf: input buffer
* @len: number of valid bytes in the input buffer
+ * @fp: out parameter for the failed field on error
*
* Prepare a taskfile to modify caching information for the device.
*
@@ -3204,20 +3324,26 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
* None.
*/
static int ata_mselect_caching(struct ata_queued_cmd *qc,
- const u8 *buf, int len)
+ const u8 *buf, int len, u16 *fp)
{
struct ata_taskfile *tf = &qc->tf;
struct ata_device *dev = qc->dev;
char mpage[CACHE_MPAGE_LEN];
u8 wce;
+ int i;
/*
* The first two bytes of def_cache_mpage are a header, so offsets
* in mpage are off by 2 compared to buf. Same for len.
*/
- if (len != CACHE_MPAGE_LEN - 2)
+ if (len != CACHE_MPAGE_LEN - 2) {
+ if (len < CACHE_MPAGE_LEN - 2)
+ *fp = len;
+ else
+ *fp = CACHE_MPAGE_LEN - 2;
return -EINVAL;
+ }
wce = buf[0] & (1 << 2);
@@ -3225,10 +3351,14 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc,
* Check that read-only bits are not modified.
*/
ata_msense_caching(dev->id, mpage, false);
- mpage[2] &= ~(1 << 2);
- mpage[2] |= wce;
- if (memcmp(mpage + 2, buf, CACHE_MPAGE_LEN - 2) != 0)
- return -EINVAL;
+ for (i = 0; i < CACHE_MPAGE_LEN - 2; i++) {
+ if (i == 0)
+ continue;
+ if (mpage[i + 2] != buf[i]) {
+ *fp = i;
+ return -EINVAL;
+ }
+ }
tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
tf->protocol = ATA_PROT_NODATA;
@@ -3239,6 +3369,62 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc,
}
/**
+ * ata_mselect_control - Simulate MODE SELECT for control page
+ * @qc: Storage for translated ATA taskfile
+ * @buf: input buffer
+ * @len: number of valid bytes in the input buffer
+ * @fp: out parameter for the failed field on error
+ *
+ * Prepare a taskfile to modify caching information for the device.
+ *
+ * LOCKING:
+ * None.
+ */
+static int ata_mselect_control(struct ata_queued_cmd *qc,
+ const u8 *buf, int len, u16 *fp)
+{
+ struct ata_device *dev = qc->dev;
+ char mpage[CONTROL_MPAGE_LEN];
+ u8 d_sense;
+ int i;
+
+ /*
+ * The first two bytes of def_control_mpage are a header, so offsets
+ * in mpage are off by 2 compared to buf. Same for len.
+ */
+
+ if (len != CONTROL_MPAGE_LEN - 2) {
+ if (len < CONTROL_MPAGE_LEN - 2)
+ *fp = len;
+ else
+ *fp = CONTROL_MPAGE_LEN - 2;
+ return -EINVAL;
+ }
+
+ d_sense = buf[0] & (1 << 2);
+
+ /*
+ * Check that read-only bits are not modified.
+ */
+ ata_msense_ctl_mode(dev, mpage, false);
+ for (i = 0; i < CONTROL_MPAGE_LEN - 2; i++) {
+ if (i == 0)
+ continue;
+ if (mpage[2 + i] != buf[i]) {
+ *fp = i;
+ return -EINVAL;
+ }
+ }
+ if (d_sense & (1 << 2))
+ dev->flags |= ATA_DFLAG_D_SENSE;
+ else
+ dev->flags &= ~ATA_DFLAG_D_SENSE;
+ qc->scsicmd->result = SAM_STAT_GOOD;
+ qc->scsicmd->scsi_done(qc->scsicmd);
+ return 0;
+}
+
+/**
* ata_scsiop_mode_select - Simulate MODE SELECT 6, 10 commands
* @qc: Storage for translated ATA taskfile
*
@@ -3257,27 +3443,36 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
u8 pg, spg;
unsigned six_byte, pg_len, hdr_len, bd_len;
int len;
+ u16 fp = (u16)-1;
+ u8 bp = 0xff;
VPRINTK("ENTER\n");
six_byte = (cdb[0] == MODE_SELECT);
if (six_byte) {
- if (scmd->cmd_len < 5)
+ if (scmd->cmd_len < 5) {
+ fp = 4;
goto invalid_fld;
+ }
len = cdb[4];
hdr_len = 4;
} else {
- if (scmd->cmd_len < 9)
+ if (scmd->cmd_len < 9) {
+ fp = 8;
goto invalid_fld;
+ }
len = (cdb[7] << 8) + cdb[8];
hdr_len = 8;
}
/* We only support PF=1, SP=0. */
- if ((cdb[1] & 0x11) != 0x10)
+ if ((cdb[1] & 0x11) != 0x10) {
+ fp = 1;
+ bp = (cdb[1] & 0x01) ? 1 : 5;
goto invalid_fld;
+ }
/* Test early for possible overrun. */
if (!scsi_sg_count(scmd) || scsi_sglist(scmd)->length < len)
@@ -3298,8 +3493,11 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
p += hdr_len;
if (len < bd_len)
goto invalid_param_len;
- if (bd_len != 0 && bd_len != 8)
+ if (bd_len != 0 && bd_len != 8) {
+ fp = (six_byte) ? 3 : 6;
+ fp += bd_len + hdr_len;
goto invalid_param;
+ }
len -= bd_len;
p += bd_len;
@@ -3330,18 +3528,29 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
* No mode subpages supported (yet) but asking for _all_
* subpages may be valid
*/
- if (spg && (spg != ALL_SUB_MPAGES))
+ if (spg && (spg != ALL_SUB_MPAGES)) {
+ fp = (p[0] & 0x40) ? 1 : 0;
+ fp += hdr_len + bd_len;
goto invalid_param;
+ }
if (pg_len > len)
goto invalid_param_len;
switch (pg) {
case CACHE_MPAGE:
- if (ata_mselect_caching(qc, p, pg_len) < 0)
+ if (ata_mselect_caching(qc, p, pg_len, &fp) < 0) {
+ fp += hdr_len + bd_len;
goto invalid_param;
+ }
+ break;
+ case CONTROL_MPAGE:
+ if (ata_mselect_control(qc, p, pg_len, &fp) < 0) {
+ fp += hdr_len + bd_len;
+ goto invalid_param;
+ }
break;
-
default: /* invalid page code */
+ fp = bd_len + hdr_len;
goto invalid_param;
}
@@ -3355,18 +3564,16 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
return 0;
invalid_fld:
- /* "Invalid field in CDB" */
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0);
+ ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp);
return 1;
invalid_param:
- /* "Invalid field in parameter list" */
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x26, 0x0);
+ ata_scsi_set_invalid_parameter(qc->dev, scmd, fp);
return 1;
invalid_param_len:
/* "Parameter list length error" */
- ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x1a, 0x0);
+ ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0);
return 1;
skip:
@@ -3570,12 +3777,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
switch(scsicmd[0]) {
/* TODO: worth improving? */
case FORMAT_UNIT:
- ata_scsi_invalid_field(cmd);
+ ata_scsi_invalid_field(dev, cmd, 0);
break;
case INQUIRY:
- if (scsicmd[1] & 2) /* is CmdDt set? */
- ata_scsi_invalid_field(cmd);
+ if (scsicmd[1] & 2) /* is CmdDt set? */
+ ata_scsi_invalid_field(dev, cmd, 1);
else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
else switch (scsicmd[2]) {
@@ -3601,7 +3808,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2);
break;
default:
- ata_scsi_invalid_field(cmd);
+ ata_scsi_invalid_field(dev, cmd, 2);
break;
}
break;
@@ -3619,7 +3826,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
else
- ata_scsi_invalid_field(cmd);
+ ata_scsi_invalid_field(dev, cmd, 1);
break;
case REPORT_LUNS:
@@ -3627,7 +3834,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
break;
case REQUEST_SENSE:
- ata_scsi_set_sense(cmd, 0, 0, 0);
+ ata_scsi_set_sense(dev, cmd, 0, 0, 0);
cmd->result = (DRIVER_SENSE << 24);
cmd->scsi_done(cmd);
break;
@@ -3651,12 +3858,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
if ((tmp8 == 0x4) && (!scsicmd[3]) && (!scsicmd[4]))
ata_scsi_rbuf_fill(&args, ata_scsiop_noop);
else
- ata_scsi_invalid_field(cmd);
+ ata_scsi_invalid_field(dev, cmd, 1);
break;
/* all other commands */
default:
- ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0);
+ ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0);
/* "Invalid command operation code" */
cmd->scsi_done(cmd);
break;