[PATCH 3/3] ata: libata-scsi: generate SAT sense data for ATA PASS-THROUGH(32)
From: shao.mingyin
Date: Wed Nov 04 2026 - 09:21:53 EST
From: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
ata_scsi_qc_complete() detects ATA pass-through commands by the ATA_12
and ATA_16 opcodes only. ATA PASS-THROUGH (32) uses the variable
length CDB opcode 7Fh with service action ATA_32, so for those commands
the function takes the ordinary completion path: the CK_COND bit is
never honored and the ATA Status Return sense descriptor is never
built. A successful command with CK_COND set is reported as GOOD
instead of CHECK CONDITION, and a failed command gets the generic ATA
sense data without the ATA register values.
The byte that carries T_LENGTH and CK_COND is byte 2 for 12- and
16-byte CDBs, but byte 11 for a 32-byte CDB, whose byte 2 is part of the
variable length CDB header. Detect ATA PASS-THROUGH (32) through its
service action and read CK_COND from the correct byte.
Fixes: b1ffbf854e08 ("libata: Support for an ATA PASS-THROUGH(32) command.")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
---
Tested on 7.3.0-next-20261030 (QEMU/TCG guest) with an ATA disk
(ata_piix, /dev/sda); no special hardware is needed.
A 32-byte ATA PASS-THROUGH(32) READ SECTORS CDB with CK_COND set in
cdb[11] and a 512-byte data-in buffer is sent with SG_IO. The same
CDB with CK_COND clear is the control and completes GOOD on both.
unpatched patched
status 0x00, no sense data status 0x02 (CHECK CONDITION)
sk 0x01, asc/ascq 0x00/0x1d
ATA descriptor 0x09 present
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index a61fb0331279..97adeecc7736 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1888,9 +1888,12 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
struct ata_link *link = qc->dev->link;
struct scsi_cmnd *cmd = qc->scsicmd;
u8 *cdb = cmd->cmnd;
+ u16 cdb_offset = cdb[0] == VARIABLE_LENGTH_CMD ? 9 : 0;
bool have_sense = qc->flags & ATA_QCFLAG_SENSE_VALID;
- bool is_ata_passthru = cdb[0] == ATA_16 || cdb[0] == ATA_12;
- bool is_ck_cond_request = cdb[2] & 0x20;
+ bool is_ata_passthru = cdb[0] == ATA_16 || cdb[0] == ATA_12 ||
+ (cdb[0] == VARIABLE_LENGTH_CMD &&
+ get_unaligned_be16(&cdb[8]) == ATA_32);
+ bool is_ck_cond_request = cdb[2 + cdb_offset] & 0x20;
bool is_error = qc->err_mask != 0;
/* For ATA pass thru (SAT) commands, generate a sense block if
@@ -3389,6 +3392,6 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
tf->protocol = ata_scsi_map_proto(cdb[1 + cdb_offset]);
if (tf->protocol == ATA_PROT_UNKNOWN) {
- fp = 1;
+ fp = 1 + cdb_offset;
goto invalid_fld;
}