1 /*
2  * Copyright (c) 2006-2007 Douglas Gilbert.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <getopt.h>
37 #define __STDC_FORMAT_MACROS 1
38 #include <inttypes.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 
43 #include "sg_lib.h"
44 #include "sg_io_linux.h"
45 
46 /* This program uses a ATA PASS-THROUGH (16) SCSI command defined
47    by SAT to package an ATA READ LOG EXT (2Fh) command to fetch
48    log page 11h. That page contains SATA phy event counters.
49    For SAT see http://www.t10.org [draft prior to standard: sat-r09.pdf]
50    For ATA READ LOG EXT command see ATA-8/ACS at www.t13.org .
51    For SATA phy counter definitions see SATA 2.5 .
52 
53    Invocation: sg_sat_phy_event [-v] [-V] <device>
54 
55 */
56 
57 #define SAT_ATA_PASS_THROUGH16 0x85
58 #define SAT_ATA_PASS_THROUGH16_LEN 16
59 #define SAT_ATA_RETURN_DESC 9  /* ATA Return Descriptor */
60 
61 #define ATA_READ_LOG_EXT 0x2f
62 #define SATA_PHY_EVENT_LPAGE 0x11
63 #define READ_LOG_EXT_RESPONSE_LEN 512
64 
65 #define EBUFF_SZ 256
66 
67 static const char * version_str = "1.00 20070507";
68 
69 static struct option long_options[] = {
70         {"help", no_argument, 0, 'h'},
71         {"hex", no_argument, 0, 'H'},
72         {"ignore", no_argument, 0, 'i'},
73         {"raw", no_argument, 0, 'r'},
74         {"reset", no_argument, 0, 'R'},
75         {"verbose", no_argument, 0, 'v'},
76         {"version", no_argument, 0, 'V'},
77         {0, 0, 0, 0},
78 };
79 
usage()80 static void usage()
81 {
82     fprintf(stderr, "Usage: "
83           "sg_sat_phy_event [--help] [--hex] [--raw] [--reset] [--verbose]\n"
84           "                        [--version] DEVICE\n"
85           "  where:\n"
86           "    --help|-h       print this usage message then exit\n"
87           "    --hex|-H        output response in hex bytes, use twice for\n"
88           "                    hex words\n"
89           "    --ignore|-i     ignore identifier names, output id value "
90           "instead\n"
91           "    --raw|-r        output response in binary to stdout\n"
92           "    --reset|-R      reset counters (after read)\n"
93           "    --verbose|-v    increase verbosity\n"
94           "    --version|-V    print version string then exit\n\n"
95           "Sends an ATA READ LOG EXT command via a SAT pass through to "
96           "fetch\nlog page 11h which contains SATA phy event counters\n");
97 }
98 
99 struct phy_event_t {
100     int id;
101     const char * desc;
102 };
103 
104 static struct phy_event_t phy_event_arr[] = {
105     {0x1, "Command failed and ICRC error bit set in Error register"},
106     {0x2, "R_ERR(p) response for data FIS"},
107     {0x3, "R_ERR(p) response for device-to-host data FIS"},
108     {0x4, "R_ERR(p) response for host-to-device data FIS"},
109     {0x5, "R_ERR(p) response for non-data FIS"},
110     {0x6, "R_ERR(p) response for device-to-host non-data FIS"},
111     {0x7, "R_ERR(p) response for host-to-device non-data FIS"},
112     {0x8, "Device-to-host non-data FIS retries"},
113     {0x9, "Transition from drive PHYRDY to drive PHYRDYn"},
114     {0xa, "Signature device-to-host register FISes due to COMRESET"},
115     {0xb, "CRC errors within host-to-device FIS"},
116     {0xd, "non CRC errors within host-to-device FIS"},
117     {0xf, "R_ERR(p) response for host-to-device data FIS, CRC"},
118     {0x10, "R_ERR(p) response for host-to-device data FIS, non-CRC"},
119     {0x12, "R_ERR(p) response for host-to-device non-data FIS, CRC"},
120     {0x13, "R_ERR(p) response for host-to-device non-data FIS, non-CRC"},
121     {0xc00, "PM: host-to-device non-data FIS, R_ERR(p) due to collision"},
122     {0xc01, "PM: signature register - device-to-host FISes"},
123     {0xc02, "PM: corrupts CRC propagation of device-to-host FISes"},
124     {0x0, NULL},
125 };
126 
find_phy_desc(int id)127 static const char * find_phy_desc(int id)
128 {
129     const struct phy_event_t * pep;
130 
131     for (pep = phy_event_arr; pep->desc; ++pep) {
132         if ((id & 0xfff) == pep->id)
133             return pep->desc;
134     }
135     return NULL;
136 }
137 
dStrRaw(const char * str,int len)138 static void dStrRaw(const char* str, int len)
139 {
140     int k;
141 
142     for (k = 0 ; k < len; ++k)
143         printf("%c", str[k]);
144 }
145 
main(int argc,char * argv[])146 int main(int argc, char * argv[])
147 {
148     int sg_fd, c, k, j, ok, res, id, len, vendor;
149     unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
150                 {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
151                  0, 0, 0, 0, 0, 0, 0, 0};
152     sg_io_hdr_t io_hdr;
153     char * device_name = 0;
154     char ebuff[EBUFF_SZ];
155     unsigned char inBuff[READ_LOG_EXT_RESPONSE_LEN];
156     unsigned char sense_buffer[64];
157     int hex = 0;
158     int ignore = 0;
159     int raw = 0;
160     int reset = 0;
161     int verbose = 0;
162     int extend = 0;
163     int chk_cond = 0;   /* set to 1 to read register(s) back */
164     int protocol = 4;   /* PIO data-in */
165     int t_dir = 1;      /* 0 -> to device, 1 -> from device */
166     int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
167     int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
168     const unsigned char * cucp;
169     int ret = 0;
170     uint64_t ull;
171     const char * cp;
172 
173     memset(inBuff, 0, sizeof(inBuff));
174     while (1) {
175         int option_index = 0;
176 
177         c = getopt_long(argc, argv, "hHirRvV",
178                         long_options, &option_index);
179         if (c == -1)
180             break;
181 
182         switch (c) {
183         case 'h':
184             usage();
185             exit(0);
186         case 'H':
187             ++hex;
188             break;
189         case 'i':
190             ++ignore;
191             break;
192         case 'r':
193             ++raw;
194             break;
195         case 'R':
196             ++reset;
197             break;
198         case 'v':
199             ++verbose;
200             break;
201         case 'V':
202             fprintf(stderr, "version: %s\n", version_str);
203             exit(0);
204         default:
205             fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c);
206             usage();
207             return SG_LIB_SYNTAX_ERROR;
208         }
209     }
210     if (optind < argc) {
211         if (NULL == device_name) {
212             device_name = argv[optind];
213             ++optind;
214         }
215         if (optind < argc) {
216             for (; optind < argc; ++optind)
217                 fprintf(stderr, "Unexpected extra argument: %s\n",
218                         argv[optind]);
219             usage();
220             return SG_LIB_SYNTAX_ERROR;
221         }
222     }
223     if (0 == device_name) {
224         fprintf(stderr, "no DEVICE name detected\n");
225         usage();
226         return SG_LIB_SYNTAX_ERROR;
227     }
228 
229     if ((sg_fd = open(device_name, O_RDWR)) < 0) {
230         snprintf(ebuff, EBUFF_SZ,
231                  "sg_sat_phy_event: error opening file: %s", device_name);
232         perror(ebuff);
233         return SG_LIB_FILE_ERROR;
234     }
235 
236     /* Prepare SCSI ATA PASS-THROUGH COMMAND (16) command */
237     if (reset > 0)
238         aptCmdBlk[4] = 1;                       /* features (7:0) */
239     aptCmdBlk[6] = 1;                           /* sector count */
240     aptCmdBlk[8] = SATA_PHY_EVENT_LPAGE;        /* lba_low (7:0) */
241     aptCmdBlk[14] = ATA_READ_LOG_EXT;           /* command */
242     aptCmdBlk[1] = (protocol << 1) | extend;
243     aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
244                    (byte_block << 2) | t_length;
245     if (verbose) {
246         fprintf(stderr, "    ata pass through(16) cdb: ");
247         for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
248             fprintf(stderr, "%02x ", aptCmdBlk[k]);
249         fprintf(stderr, "\n");
250     }
251 
252     memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
253     io_hdr.interface_id = 'S';
254     io_hdr.cmd_len = sizeof(aptCmdBlk);
255     /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
256     io_hdr.mx_sb_len = sizeof(sense_buffer);
257     io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
258     io_hdr.dxfer_len = READ_LOG_EXT_RESPONSE_LEN;
259     io_hdr.dxferp = inBuff;
260     io_hdr.cmdp = aptCmdBlk;
261     io_hdr.sbp = sense_buffer;
262     io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
263     /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
264     /* io_hdr.pack_id = 0; */
265     /* io_hdr.usr_ptr = NULL; */
266 
267     if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
268         perror("sg_sat_phy_event: SG_IO ioctl error");
269         close(sg_fd);
270         return SG_LIB_CAT_OTHER;
271     }
272 
273     /* now for the error processing */
274     ok = 0;
275     ret = sg_err_category3(&io_hdr);
276     switch (ret) {
277     case SG_LIB_CAT_CLEAN:
278         ok = 1;
279         break;
280     case SG_LIB_CAT_RECOVERED:
281         if (verbose)
282             sg_chk_n_print3(">>> ATA_16 command", &io_hdr, 1);
283         /* check for ATA Return Descriptor */
284         cucp = sg_scsi_sense_desc_find(io_hdr.sbp, io_hdr.sb_len_wr,
285                                        SAT_ATA_RETURN_DESC);
286         if (cucp && (cucp[3])) {
287             if (cucp[3] & 0x4) {
288                 fprintf(stderr, "error in returned FIS: aborted command\n");
289                 break;
290             }
291         }
292         ret = 0;
293         ok = 1;         /* not sure what is happening so output response */
294         if (0 == verbose) {
295             fprintf(stderr, ">>> Recovered error on ATA_16, may have "
296                     "failed\n");
297             fprintf(stderr, "    Add '-v' for more information\n");
298         }
299         break;
300     default: /* won't bother decoding other categories */
301         sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
302         break;
303     }
304 
305     if (ok) { /* output result if it is available */
306         if (raw > 0)
307             dStrRaw((const char *)inBuff, 512);
308         else {
309             if (verbose && hex)
310                 fprintf(stderr, "Response to READ LOG EXT (page=11h):\n");
311             if (1 == hex)
312                 dStrHex((const char *)inBuff, 512, 0);
313             else if (hex > 1)
314                 dWordHex((const unsigned short *)inBuff, 256, 0,
315                          sg_is_big_endian());
316             else {
317                 printf("SATA phy event counters:\n");
318                 for (k = 4; k < 512; k += (len + 2)) {
319                     id = (inBuff[k + 1] << 8) + inBuff[k];
320                     if (0 == id)
321                         break;
322                     len = ((id >> 12) & 0x7) * 2;
323                     vendor = !!(id & 0x8000);
324                     id = id & 0xfff;
325                     ull = 0;
326                     for (j = len - 1; j >= 0; --j) {
327                         if (j < (len - 1))
328                             ull <<= 8;
329                         ull |= inBuff[k + 2 + j];
330                     }
331                     cp = NULL;
332                     if ((0 == vendor) && (0 == ignore))
333                         cp = find_phy_desc(id);
334                     if (cp)
335                         printf("  %s: %" PRIu64 "\n", cp, ull);
336                     else
337                         printf("  id=0x%x, vendor=%d, data_len=%d, "
338                                "val=%" PRIu64 "\n", id, vendor, len, ull);
339                 }
340             }
341         }
342     }
343     res = close(sg_fd);
344     if (res < 0) {
345         fprintf(stderr, "close error: %s\n", safe_strerror(-res));
346         if (0 == ret)
347             return SG_LIB_FILE_ERROR;
348     }
349     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
350 }
351