1 /*
2 * Copyright (c) 2006-2018 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.03 20180220";
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 uint8_t * str,int len)138 static void dStrRaw(const uint8_t * 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 uint8_t apt_cdb[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 uint8_t inBuff[READ_LOG_EXT_RESPONSE_LEN];
156 uint8_t 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 uint8_t * 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 apt_cdb[4] = 1; /* features (7:0) */
239 apt_cdb[6] = 1; /* sector count */
240 apt_cdb[8] = SATA_PHY_EVENT_LPAGE; /* lba_low (7:0) */
241 apt_cdb[14] = ATA_READ_LOG_EXT; /* command */
242 apt_cdb[1] = (protocol << 1) | extend;
243 apt_cdb[2] = (chk_cond << 5) | (t_dir << 3) | (byte_block << 2) |
244 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 ", apt_cdb[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(apt_cdb);
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 = apt_cdb;
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(inBuff, 512);
308 else {
309 if (verbose && hex)
310 fprintf(stderr, "Response to READ LOG EXT (page=11h):\n");
311 if (1 == hex)
312 hex2stdout(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