From c2d06938d9214d7fc3a352585a6905072012f399 Mon Sep 17 00:00:00 2001 From: mlelstv Date: Thu, 30 May 2019 21:32:08 +0000 Subject: [PATCH] Add support for ATA command pass-through to SCSI devices. --- sbin/atactl/atactl.c | 265 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 6 deletions(-) diff --git a/sbin/atactl/atactl.c b/sbin/atactl/atactl.c index 3ff0bee2be31..a10fcf913cba 100644 --- a/sbin/atactl/atactl.c +++ b/sbin/atactl/atactl.c @@ -1,4 +1,4 @@ -/* $NetBSD: atactl.c,v 1.82 2019/03/03 04:51:57 mrg Exp $ */ +/* $NetBSD: atactl.c,v 1.83 2019/05/30 21:32:08 mlelstv Exp $ */ /*- * Copyright (c) 1998, 2019 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ #include #ifndef lint -__RCSID("$NetBSD: atactl.c,v 1.82 2019/03/03 04:51:57 mrg Exp $"); +__RCSID("$NetBSD: atactl.c,v 1.83 2019/05/30 21:32:08 mlelstv Exp $"); #endif @@ -54,6 +54,9 @@ __RCSID("$NetBSD: atactl.c,v 1.82 2019/03/03 04:51:57 mrg Exp $"); #include #include +#include +#include + struct ata_smart_error { struct { uint8_t device_control; @@ -90,6 +93,53 @@ struct ata_smart_errorlog { uint8_t checksum; } __packed; +#define SCSI_ATA_PASS_THROUGH_16 0x85 +struct scsi_ata_pass_through_16 { + uint8_t opcode; + uint8_t byte2; +#define SATL_NODATA 0x06 +#define SATL_PIO_IN 0x08 +#define SATL_PIO_OUT 0x0a +#define SATL_EXTEND 0x01 + uint8_t byte3; +#define SATL_CKCOND 0x20 +#define SATL_READ 0x08 +#define SATL_BLOCKS 0x04 +#define SATL_LEN(x) ((x) & 0x03) + uint8_t features[2]; + uint8_t sector_count[2]; + uint8_t lba[6]; + uint8_t device; + uint8_t ata_cmd; + uint8_t control; +} __packed; + +#define SCSI_ATA_PASS_THROUGH_12 0xa1 +struct scsi_ata_pass_through_12 { + uint8_t opcode; + uint8_t byte2; + uint8_t byte3; + uint8_t features[1]; + uint8_t sector_count[1]; + uint8_t lba[3]; + uint8_t device; + uint8_t ata_cmd; + uint8_t reserved; + uint8_t control; +} __packed; + +struct scsi_ata_return_descriptor { + uint8_t descr; +#define SCSI_ATA_RETURN_DESCRIPTOR 9 + uint8_t additional_length; + uint8_t extend; + uint8_t error; + uint8_t sector_count[2]; + uint8_t lba[6]; + uint8_t device; + uint8_t status; +} __packed; + struct command { const char *cmd_name; const char *arg_names; @@ -103,6 +153,8 @@ struct bitinfo { __dead static void usage(void); static void ata_command(struct atareq *); +static int satl_command(struct atareq *, int); +static const uint8_t *satl_return_desc(const uint8_t *, size_t, uint8_t); static void print_bitinfo(const char *, const char *, u_int, const struct bitinfo *); static void print_bitinfo2(const char *, const char *, u_int, u_int, @@ -119,6 +171,7 @@ static void fillataparams(void); static int is_smart(void); static int fd; /* file descriptor for device */ +static int use_satl; /* tunnel through SATL */ static const char *dvname; /* device name */ static char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ static const char *cmdname; /* command user issued */ @@ -531,10 +584,25 @@ ata_command(struct atareq *req) { int error; - error = ioctl(fd, ATAIOCCOMMAND, req); - - if (error == -1) - err(1, "ATAIOCCOMMAND failed"); + switch (use_satl) { + case 0: + error = ioctl(fd, ATAIOCCOMMAND, req); + if (error == 0) + break; + if (errno != ENOTTY) + err(1, "ATAIOCCOMMAND failed"); + use_satl = 1; + /* FALLTHROUGH */ + case 1: + error = satl_command(req, 16); + if (error == 0) + return; + use_satl = 2; + /* FALLTHROUGH */ + case 2: + (void) satl_command(req, 12); + return; + } switch (req->retsts) { @@ -561,6 +629,191 @@ ata_command(struct atareq *req) } } +/* + * Wrapper that calls SCIOCCOMMAND for a tunneled ATA command + */ +static int +satl_command(struct atareq *req, int cmdlen) +{ + scsireq_t sreq; + int error; + union { + struct scsi_ata_pass_through_12 cmd12; + struct scsi_ata_pass_through_16 cmd16; + } c; + uint8_t b2, b3; + const uint8_t *desc; + + b2 = SATL_NODATA; + if (req->datalen > 0) { + if (req->flags & ATACMD_READ) + b2 = SATL_PIO_IN; + else + b2 = SATL_PIO_OUT; + } + + b3 = SATL_BLOCKS; + if (req->datalen > 0) { + b3 |= 2; /* sector count holds count */ + } else { + b3 |= SATL_CKCOND; + } + if (req->datalen == 0 || req->flags & ATACMD_READ) + b3 |= SATL_READ; + + switch (cmdlen) { + case 16: + c.cmd16.opcode = SCSI_ATA_PASS_THROUGH_16; + c.cmd16.byte2 = b2; + c.cmd16.byte3 = b3; + c.cmd16.features[0] = 0; + c.cmd16.features[1] = req->features; + c.cmd16.sector_count[0] = 0; + c.cmd16.sector_count[1] = req->sec_count; + c.cmd16.lba[0] = 0; + c.cmd16.lba[1] = req->sec_num; + c.cmd16.lba[2] = 0; + c.cmd16.lba[3] = req->cylinder; + c.cmd16.lba[4] = 0; + c.cmd16.lba[5] = req->cylinder >> 8; + c.cmd16.device = 0; + c.cmd16.ata_cmd = req->command; + c.cmd16.control = 0; + break; + case 12: + c.cmd12.opcode = SCSI_ATA_PASS_THROUGH_12; + c.cmd12.byte2 = b2; + c.cmd12.byte3 = b3; + c.cmd12.features[0] = req->features; + c.cmd12.sector_count[0] = req->sec_count; + c.cmd12.lba[0] = req->sec_num; + c.cmd12.lba[1] = req->cylinder; + c.cmd12.lba[2] = req->cylinder >> 8; + c.cmd12.device = 0; + c.cmd12.reserved = 0; + c.cmd12.ata_cmd = req->command; + c.cmd12.control = 0; + break; + default: + fprintf(stderr, "ATA command with bad length\n"); + exit(1); + } + + memset(&sreq, 0, sizeof(sreq)); + memcpy(sreq.cmd, &c, cmdlen); + sreq.cmdlen = cmdlen; + sreq.databuf = req->databuf; + sreq.datalen = req->datalen; + sreq.senselen = sizeof(sreq.sense); + sreq.timeout = req->timeout; + + if (sreq.datalen > 0) { + if (req->flags & ATACMD_READ) + sreq.flags |= SCCMD_READ; + if (req->flags & ATACMD_WRITE) + sreq.flags |= SCCMD_WRITE; + } + + error = ioctl(fd, SCIOCCOMMAND, &sreq); + if (error == -1) + err(1, "SCIOCCOMMAND failed"); + + req->datalen = sreq.datalen_used; + req->retsts = ATACMD_OK; + req->error = 0; + + switch (sreq.retsts) { + case SCCMD_OK: + return 0; + case SCCMD_TIMEOUT: + fprintf(stderr, "SATL command timed out\n"); + exit(1); + case SCCMD_BUSY: + fprintf(stderr, "SATL command returned busy\n"); + exit(1); + case SCCMD_SENSE: + desc = NULL; + switch (SSD_RCODE(sreq.sense[0])) { + case 0x00: + return 0; + case 0x70: + if (sreq.sense[2] == SKEY_NO_SENSE) + return 0; + if (sreq.sense[2] == SKEY_ILLEGAL_REQUEST) + return 1; + break; + case 0x72: + case 0x73: + desc = satl_return_desc(sreq.sense, sreq.senselen_used, + SCSI_ATA_RETURN_DESCRIPTOR); + break; + default: + break; + } + + if (desc && desc[1] >= 12) { + req->sec_count = desc[5]; + req->sec_num = desc[7]; + req->head = (desc[12] & 0xf0) | + ((desc[7] >> 24) & 0x0f); + req->cylinder = desc[11] << 8 | desc[9]; + req->retsts = desc[13]; + req->error = desc[3]; + return 0; + } + + fprintf(stderr, "SATL command error: rcode %02x key %u\n", + SSD_RCODE(sreq.sense[0]), + SSD_SENSE_KEY(sreq.sense[2])); + if (desc) { + int i, n; + n = desc[1]+2; + printf("ATA Return Descriptor:"); + for (i=0; i