Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sbin/atactl Add support for ATA command pass-through to SCSI...



details:   https://anonhg.NetBSD.org/src/rev/84fdfd6d2580
branches:  trunk
changeset: 451685:84fdfd6d2580
user:      mlelstv <mlelstv%NetBSD.org@localhost>
date:      Thu May 30 21:32:08 2019 +0000

description:
Add support for ATA command pass-through to SCSI devices.

diffstat:

 sbin/atactl/atactl.c |  265 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 259 insertions(+), 6 deletions(-)

diffs (truncated from 321 to 300 lines):

diff -r dbc938f4f6be -r 84fdfd6d2580 sbin/atactl/atactl.c
--- a/sbin/atactl/atactl.c      Thu May 30 21:27:58 2019 +0000
+++ b/sbin/atactl/atactl.c      Thu May 30 21:32:08 2019 +0000
@@ -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 <sys/cdefs.h>
 
 #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 @@
 #include <dev/ata/atareg.h>
 #include <sys/ataio.h>
 
+#include <dev/scsipi/scsi_spc.h>
+#include <sys/scsiio.h>
+
 struct ata_smart_error {
        struct {
                uint8_t device_control;
@@ -90,6 +93,53 @@
        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 @@
 
 __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 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 @@
 {
        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) {
 
@@ -562,6 +630,191 @@
 }
 
 /*
+ * 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<n; ++i)
+                               printf(" %02x",desc[i]);
+                       printf("\n");
+               }
+               exit(1);
+       default:
+               fprintf(stderr, "SCSIIOCCOMMAND returned unknown result code "
+                       "%d\n", sreq.retsts);
+               exit(1);
+       }
+}
+
+static const uint8_t *
+satl_return_desc(const uint8_t *sense, size_t len, uint8_t type)
+{
+       const uint8_t *p, *endp;
+       size_t l, extra;
+       
+       if (len < 8)
+               return NULL;
+       extra = sense[7];
+       len -= 8;
+       if (extra < len)
+               len = extra;
+       if (len < 2)
+               return NULL;
+
+       switch (sense[0]) {



Home | Main Index | Thread Index | Old Index