1 /*
2 * Copyright (c) 2018-2021 Douglas Gilbert
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * This program issues a NVMe Identify command (controller or namespace)
10 * or a Device self-test command via the "SCSI" pass-through interface of
11 * this package's sg_utils library. That interface is primarily shown in
12 * the ../include/sg_pt.h header file.
13 *
14 */
15
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <getopt.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #define __STDC_FORMAT_MACROS 1
29 #include <inttypes.h>
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "sg_lib.h"
36 #include "sg_pt.h"
37 #include "sg_pt_nvme.h"
38 #include "sg_cmds_basic.h"
39 #include "sg_unaligned.h"
40 #include "sg_pr2serr.h"
41
42 static const char * version_str = "1.07 20210225";
43
44
45 #define ME "sg_tst_nvme: "
46
47 #define SENSE_BUFF_LEN 32 /* Arbitrary, only need 16 bytes for NVME
48 * (and SCSI at least 18) currently */
49 #define SENSE_BUFF_NVME_LEN 16 /* 4 DWords, little endian, as byte string */
50
51 #define INQUIRY_CMD 0x12 /* SCSI command to get VPD page 0x83 */
52 #define INQUIRY_CMDLEN 6
53 #define INQUIRY_MAX_RESP_LEN 252
54
55 #define VPD_DEVICE_ID 0x83
56
57 #define NVME_NSID_ALL 0xffffffff
58
59 #define DEF_TIMEOUT_SECS 60
60
61
62 static struct option long_options[] = {
63 {"ctl", no_argument, 0, 'c'},
64 {"dev-id", no_argument, 0, 'd'},
65 {"dev_id", no_argument, 0, 'd'},
66 {"help", no_argument, 0, 'h'},
67 {"long", no_argument, 0, 'l'},
68 {"maxlen", required_argument, 0, 'm'},
69 {"nsid", required_argument, 0, 'n'},
70 {"self-test", required_argument, 0, 's'},
71 {"self_test", required_argument, 0, 's'},
72 {"to-ms", required_argument, 0, 't'},
73 {"to_ms", required_argument, 0, 't'},
74 {"verbose", no_argument, 0, 'v'},
75 {"version", no_argument, 0, 'V'},
76 {0, 0, 0, 0},
77 };
78
79 /* Assume index is less than 16 */
80 static const char * sg_ansi_version_arr[16] =
81 {
82 "no conformance claimed",
83 "SCSI-1", /* obsolete, ANSI X3.131-1986 */
84 "SCSI-2", /* obsolete, ANSI X3.131-1994 */
85 "SPC", /* withdrawn, ANSI INCITS 301-1997 */
86 "SPC-2", /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
87 "SPC-3", /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
88 "SPC-4", /* ANSI INCITS 513-2015 */
89 "SPC-5",
90 "ecma=1, [8h]",
91 "ecma=1, [9h]",
92 "ecma=1, [Ah]",
93 "ecma=1, [Bh]",
94 "reserved [Ch]",
95 "reserved [Dh]",
96 "reserved [Eh]",
97 "reserved [Fh]",
98 };
99
100 #define MAX_DEV_NAMES 8
101
102 static const char * dev_name_arr[MAX_DEV_NAMES] = {
103 NULL,
104 NULL,
105 NULL,
106 NULL,
107 NULL,
108 NULL,
109 NULL,
110 NULL,
111 };
112
113 static int next_dev_name_pos = 0;
114
115
116 static void
usage()117 usage()
118 {
119 pr2serr("Usage: sg_tst_nvme [--ctl] [--dev-id] [--help] [--long] "
120 "[--maxlen=LEN]\n"
121 " [--nsid=ID] [--self-test=ST] [--to-ms=TO] "
122 "[--verbose]\n"
123 " [--version] DEVICE [DEVICE ...]\n"
124 " where:\n"
125 " --ctl|-c only do Identify controller command\n"
126 " --dev-id|-d do SCSI INQUIRY for device "
127 " identification\n"
128 " VPD page (0x83) via own SNTL\n"
129 " --help|-h print out usage message\n"
130 " --long|-l add more detail to decoded output\n"
131 " --maxlen=LEN| -m LEN allocation length for SCSI devices\n"
132 " --nsid=ID| -n ID do Identify namespace with nsid set to "
133 "ID; if ID\n"
134 " is 0 then try to get nsid from "
135 "DEVICE.\n"
136 " Can also be used with self-test (def: "
137 "0)\n"
138 " --self-test=ST|-s ST do (or abort) device self-test, ST "
139 "can be:\n"
140 " 0: do nothing\n"
141 " 1: do short (background) "
142 "self-test\n"
143 " 2: do long self-test\n"
144 " 15: abort self-test in "
145 "progress\n"
146 " if nsid is 0 then test controller "
147 "only\n"
148 " if nsid is 0xffffffff (-1) then test "
149 "controller\n"
150 " and all namespaces\n"
151 " --to-ms=TO|-t TO command timeout in milliseconds (def: "
152 "60,000)\n"
153 " --verbose|-v increase verbosity\n"
154 " --version|-V print version string then exit\n\n"
155 "Performs a NVME Identify or Device self-test Admin command on "
156 "each DEVICE.\nCan also simulate a SCSI device identification VPD "
157 "page [0x83] via\na local SNTL. --nsid= accepts '-1' for "
158 "0xffffffff which means all.\n"
159 );
160 }
161
162 static void
show_nvme_id_ctl(const uint8_t * dinp,const char * dev_name,int do_long,uint32_t * max_nsid_p)163 show_nvme_id_ctl(const uint8_t *dinp, const char *dev_name, int do_long,
164 uint32_t * max_nsid_p)
165 {
166 bool got_fguid;
167 uint8_t ver_min, ver_ter, mtds;
168 uint16_t ver_maj, oacs, oncs;
169 uint32_t k, ver, max_nsid, npss, j, n, m;
170 uint64_t sz1, sz2;
171 const uint8_t * up;
172
173 max_nsid = sg_get_unaligned_le32(dinp + 516); /* NN */
174 if (max_nsid_p)
175 *max_nsid_p = max_nsid;
176 printf("Identify controller for %s:\n", dev_name);
177 printf(" Model number: %.40s\n", (const char *)(dinp + 24));
178 printf(" Serial number: %.20s\n", (const char *)(dinp + 4));
179 printf(" Firmware revision: %.8s\n", (const char *)(dinp + 64));
180 ver = sg_get_unaligned_le32(dinp + 80);
181 ver_maj = (ver >> 16);
182 ver_min = (ver >> 8) & 0xff;
183 ver_ter = (ver & 0xff);
184 printf(" Version: %u.%u", ver_maj, ver_min);
185 if ((ver_maj > 1) || ((1 == ver_maj) && (ver_min > 2)) ||
186 ((1 == ver_maj) && (2 == ver_min) && (ver_ter > 0)))
187 printf(".%u\n", ver_ter);
188 else
189 printf("\n");
190 oacs = sg_get_unaligned_le16(dinp + 256);
191 if (0x1ff & oacs) {
192 printf(" Optional admin command support:\n");
193 if (0x100 & oacs)
194 printf(" Doorbell buffer config\n");
195 if (0x80 & oacs)
196 printf(" Virtualization management\n");
197 if (0x40 & oacs)
198 printf(" NVMe-MI send and NVMe-MI receive\n");
199 if (0x20 & oacs)
200 printf(" Directive send and directive receive\n");
201 if (0x10 & oacs)
202 printf(" Device self-test\n");
203 if (0x8 & oacs)
204 printf(" Namespace management and attachment\n");
205 if (0x4 & oacs)
206 printf(" Firmware download and commit\n");
207 if (0x2 & oacs)
208 printf(" Format NVM\n");
209 if (0x1 & oacs)
210 printf(" Security send and receive\n");
211 } else
212 printf(" No optional admin command support\n");
213 oncs = sg_get_unaligned_le16(dinp + 256);
214 if (0x7f & oncs) {
215 printf(" Optional NVM command support:\n");
216 if (0x40 & oncs)
217 printf(" Timestamp feature\n");
218 if (0x20 & oncs)
219 printf(" Reservations\n");
220 if (0x10 & oncs)
221 printf(" Save and Select fields non-zero\n");
222 if (0x8 & oncs)
223 printf(" Write zeroes\n");
224 if (0x4 & oncs)
225 printf(" Dataset management\n");
226 if (0x2 & oncs)
227 printf(" Write uncorrectable\n");
228 if (0x1 & oncs)
229 printf(" Compare\n");
230 } else
231 printf(" No optional NVM command support\n");
232 printf(" PCI vendor ID VID/SSVID: 0x%x/0x%x\n",
233 sg_get_unaligned_le16(dinp + 0),
234 sg_get_unaligned_le16(dinp + 2));
235 printf(" IEEE OUI Identifier: 0x%x\n",
236 sg_get_unaligned_le24(dinp + 73));
237 got_fguid = ! sg_all_zeros(dinp + 112, 16);
238 if (got_fguid) {
239 printf(" FGUID: 0x%02x", dinp[112]);
240 for (k = 1; k < 16; ++k)
241 printf("%02x", dinp[112 + k]);
242 printf("\n");
243 } else if (do_long)
244 printf(" FGUID: 0x0\n");
245 printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(dinp + 78));
246 if (do_long) {
247 printf(" Management endpoint capabilities, over a PCIe port: %d\n",
248 !! (0x2 & dinp[255]));
249 printf(" Management endpoint capabilities, over a SMBus/I2C port: "
250 "%d\n", !! (0x1 & dinp[255]));
251 }
252 printf(" Number of namespaces: %u\n", max_nsid);
253 sz1 = sg_get_unaligned_le64(dinp + 280); /* lower 64 bits */
254 sz2 = sg_get_unaligned_le64(dinp + 288); /* upper 64 bits */
255 if (sz2)
256 printf(" Total NVM capacity: huge ...\n");
257 else if (sz1)
258 printf(" Total NVM capacity: %" PRIu64 " bytes\n", sz1);
259 mtds = dinp[77];
260 printf(" Maximum data transfer size: ");
261 if (mtds)
262 printf("%u pages\n", 1U << mtds);
263 else
264 printf("<unlimited>\n");
265
266 if (do_long) {
267 const char * const non_op = "does not process I/O";
268 const char * const operat = "processes I/O";
269 const char * cp;
270
271 printf(" Total NVM capacity: 0 bytes\n");
272 npss = dinp[263] + 1;
273 up = dinp + 2048;
274 for (k = 0; k < npss; ++k, up += 32) {
275 n = sg_get_unaligned_le16(up + 0);
276 n *= (0x1 & up[3]) ? 1 : 100; /* unit: 100 microWatts */
277 j = n / 10; /* unit: 1 milliWatts */
278 m = j % 1000;
279 j /= 1000;
280 cp = (0x2 & up[3]) ? non_op : operat;
281 printf(" Power state %u: Max power: ", k);
282 if (0 == j) {
283 m = n % 10;
284 n /= 10;
285 printf("%u.%u milliWatts, %s\n", n, m, cp);
286 } else
287 printf("%u.%03u Watts, %s\n", j, m, cp);
288 n = sg_get_unaligned_le32(up + 4);
289 if (0 == n)
290 printf(" [ENLAT], ");
291 else
292 printf(" ENLAT=%u, ", n);
293 n = sg_get_unaligned_le32(up + 8);
294 if (0 == n)
295 printf("[EXLAT], ");
296 else
297 printf("EXLAT=%u, ", n);
298 n = 0x1f & up[12];
299 printf("RRT=%u, ", n);
300 n = 0x1f & up[13];
301 printf("RRL=%u, ", n);
302 n = 0x1f & up[14];
303 printf("RWT=%u, ", n);
304 n = 0x1f & up[15];
305 printf("RWL=%u\n", n);
306 }
307 }
308 }
309
310 static const char * rperf[] = {"Best", "Better", "Good", "Degraded"};
311
312 static void
show_nvme_id_ns(const uint8_t * dinp,uint32_t nsid,const char * dev_name,int do_long)313 show_nvme_id_ns(const uint8_t * dinp, uint32_t nsid, const char *dev_name,
314 int do_long)
315 {
316 bool got_eui_128 = false;
317 uint32_t u, k, off, num_lbaf, flbas, flba_info, md_size, lb_size;
318 uint64_t ns_sz, eui_64;
319
320 printf("Identify namespace %u for %s:\n", nsid, dev_name);
321 num_lbaf = dinp[25] + 1; /* spec says this is "0's based value" */
322 flbas = dinp[26] & 0xf; /* index of active LBA format (for this ns) */
323 ns_sz = sg_get_unaligned_le64(dinp + 0);
324 eui_64 = sg_get_unaligned_be64(dinp + 120); /* N.B. EUI is big endian */
325 if (! sg_all_zeros(dinp + 104, 16))
326 got_eui_128 = true;
327 printf(" Namespace size/capacity: %" PRIu64 "/%" PRIu64
328 " blocks\n", ns_sz, sg_get_unaligned_le64(dinp + 8));
329 printf(" Namespace utilization: %" PRIu64 " blocks\n",
330 sg_get_unaligned_le64(dinp + 16));
331 if (got_eui_128) { /* N.B. big endian */
332 printf(" NGUID: 0x%02x", dinp[104]);
333 for (k = 1; k < 16; ++k)
334 printf("%02x", dinp[104 + k]);
335 printf("\n");
336 } else if (do_long)
337 printf(" NGUID: 0x0\n");
338 if (eui_64)
339 printf(" EUI-64: 0x%" PRIx64 "\n", eui_64); /* N.B. big endian */
340 printf(" Number of LBA formats: %u\n", num_lbaf);
341 printf(" Index LBA size: %u\n", flbas);
342 for (k = 0, off = 128; k < num_lbaf; ++k, off += 4) {
343 printf(" LBA format %u support:", k);
344 if (k == flbas)
345 printf(" <-- active\n");
346 else
347 printf("\n");
348 flba_info = sg_get_unaligned_le32(dinp + off);
349 md_size = flba_info & 0xffff;
350 lb_size = flba_info >> 16 & 0xff;
351 if (lb_size > 31) {
352 pr2serr("%s: logical block size exponent of %u implies a LB "
353 "size larger than 4 billion bytes, ignore\n", __func__,
354 lb_size);
355 continue;
356 }
357 lb_size = 1U << lb_size;
358 ns_sz *= lb_size;
359 ns_sz /= 500*1000*1000;
360 if (ns_sz & 0x1)
361 ns_sz = (ns_sz / 2) + 1;
362 else
363 ns_sz = ns_sz / 2;
364 u = (flba_info >> 24) & 0x3;
365 printf(" Logical block size: %u bytes\n", lb_size);
366 printf(" Approximate namespace size: %" PRIu64 " GB\n", ns_sz);
367 printf(" Metadata size: %u bytes\n", md_size);
368 printf(" Relative performance: %s [0x%x]\n", rperf[u], u);
369 }
370 }
371
372 /* Invokes a NVMe Admin command via sg_utils library pass-through that will
373 * potentially fetch data from the device (din). Returns 0 -> success,
374 * various SG_LIB_* positive values or negated errno values.
375 * SG_LIB_NVME_STATUS is returned if the NVMe status is non-zero. */
376 static int
nvme_din_admin_cmd(struct sg_pt_base * ptvp,const uint8_t * cmdp,uint32_t cmd_len,const char * cmd_str,uint8_t * dip,int di_len,int timeout_ms,uint16_t * sct_scp,int vb)377 nvme_din_admin_cmd(struct sg_pt_base * ptvp, const uint8_t *cmdp,
378 uint32_t cmd_len, const char *cmd_str, uint8_t *dip,
379 int di_len, int timeout_ms, uint16_t *sct_scp, int vb)
380 {
381 int res, k;
382 uint16_t sct_sc = 0;
383 uint32_t result, clen;
384 uint8_t sense_b[SENSE_BUFF_NVME_LEN] SG_C_CPP_ZERO_INIT;
385 uint8_t ucmd[128];
386 char b[32];
387
388 snprintf(b, sizeof(b), "%s", cmd_str);
389 clen = (cmd_len > sizeof(ucmd)) ? sizeof(ucmd) : cmd_len;
390 memcpy(ucmd, cmdp, clen);
391 if (vb > 1) {
392 pr2serr(" %s cdb:\n", b);
393 hex2stderr(ucmd, clen, -1);
394 }
395 set_scsi_pt_cdb(ptvp, ucmd, clen);
396 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
397 if (dip && (di_len > 0))
398 set_scsi_pt_data_in(ptvp, dip, di_len);
399 res = do_scsi_pt(ptvp, -1, -timeout_ms, vb);
400 if (res) {
401 if (res < 0) {
402 res = sg_convert_errno(-res);
403 goto err_out;
404 } else {
405 if (SCSI_PT_DO_BAD_PARAMS == res)
406 pr2serr("%s: bad parameters to do_scsi_pt()\n", __func__);
407 else if (SCSI_PT_DO_TIMEOUT == res)
408 pr2serr("%s: timeout in do_scsi_pt()\n", __func__);
409 else if (SCSI_PT_DO_NVME_STATUS == res) {
410 sct_sc = get_scsi_pt_status_response(ptvp);
411 res = SG_LIB_NVME_STATUS;
412 goto nvme_status_err;
413 } else
414 pr2serr("%s: unknown error (%d) from do_scsi_pt()\n",
415 __func__, res);
416 }
417 res = SG_LIB_FILE_ERROR;
418 goto err_out;
419 }
420
421 if ((vb > 2) && dip && di_len) {
422 k = get_scsi_pt_resid(ptvp);
423 pr2serr(" Data in buffer [%d bytes]:\n", di_len - k);
424 if (di_len > k)
425 hex2stderr(dip, di_len - k, -1);
426 if (vb > 3)
427 pr2serr(" do_scsi_pt(nvme): res=%d resid=%d\n", res, k);
428 }
429 sct_sc = get_scsi_pt_status_response(ptvp);
430 result = get_pt_result(ptvp);
431 k = get_scsi_pt_sense_len(ptvp);
432 if (vb) {
433 pr2serr("Status: 0x%x [SCT<<8 + SC], Result: 0x%x, Completion Q:\n",
434 sct_sc, result);
435 if (k > 0)
436 hex2stderr(sense_b, k, -1);
437 }
438 nvme_status_err:
439 if (sct_scp)
440 *sct_scp = sct_sc;
441 err_out:
442 return res;
443 }
444
445 static void
std_inq_decode(const char * prefix,uint8_t * b,int len,int vb)446 std_inq_decode(const char * prefix, uint8_t * b, int len, int vb)
447 {
448 int pqual, n;
449
450 if (len < 4)
451 return;
452 pqual = (b[0] & 0xe0) >> 5;
453 if (0 == pqual)
454 printf("%s:\n", prefix);
455 else if (1 == pqual)
456 printf("%s: [qualifier indicates no connected LU]\n", prefix);
457 else if (3 == pqual)
458 printf("%s: [qualifier indicates not capable of supporting LU]\n",
459 prefix);
460 else
461 printf("%s: [reserved or vendor specific qualifier [%d]]\n",
462 prefix, pqual);
463 printf(" PQual=%d Device_type=%d RMB=%d LU_CONG=%d "
464 "version=0x%02x ", pqual, b[0] & 0x1f, !!(b[1] & 0x80),
465 !!(b[1] & 0x40), (unsigned int)b[2]);
466 printf(" [%s]\n", sg_ansi_version_arr[b[2] & 0xf]);
467 printf(" [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d "
468 " Resp_data_format=%d\n",
469 !!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20),
470 !!(b[3] & 0x10), b[3] & 0x0f);
471 if (len < 5)
472 return;
473 n = b[4] + 5;
474 if (vb)
475 pr2serr(">> requested %d bytes, %d bytes available\n", len, n);
476 printf(" SCCS=%d ACC=%d TPGS=%d 3PC=%d Protect=%d ",
477 !!(b[5] & 0x80), !!(b[5] & 0x40), ((b[5] & 0x30) >> 4),
478 !!(b[5] & 0x08), !!(b[5] & 0x01));
479 printf(" [BQue=%d]\n EncServ=%d ", !!(b[6] & 0x80),
480 !!(b[6] & 0x40));
481 if (b[6] & 0x10)
482 printf("MultiP=1 (VS=%d) ", !!(b[6] & 0x20));
483 else
484 printf("MultiP=0 ");
485 printf("[MChngr=%d] [ACKREQQ=%d] Addr16=%d\n [RelAdr=%d] ",
486 !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01),
487 !!(b[7] & 0x80));
488 printf("WBus16=%d Sync=%d [Linked=%d] [TranDis=%d] ",
489 !!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x08),
490 !!(b[7] & 0x04));
491 printf("CmdQue=%d\n", !!(b[7] & 0x02));
492 if (len < 36)
493 return;
494 printf(" Vendor_identification: %.8s\n", b + 8);
495 printf(" Product_identification: %.16s\n", b + 16);
496 printf(" Product_revision_level: %.4s\n", b + 32);
497 }
498
499 /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
500 * successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
501 * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
502 * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
503 * CODES command instead). Adds the ability to set the command abort timeout
504 * and the ability to report the residual count. If timeout_secs is zero
505 * the default command abort timeout (60 seconds) is used.
506 * If residp is non-NULL then the residual value is written where residp
507 * points. A residual value of 0 implies mx_resp_len bytes have be written
508 * where resp points. If the residual value equals mx_resp_len then no
509 * bytes have been written. */
510 static int
sg_scsi_inquiry(struct sg_pt_base * ptvp,bool evpd,int pg_op,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int vb)511 sg_scsi_inquiry(struct sg_pt_base * ptvp, bool evpd, int pg_op, void * resp,
512 int mx_resp_len, int timeout_secs, int * residp,
513 bool noisy, int vb)
514 {
515 int res, ret, k, sense_cat, resid;
516 uint8_t inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
517 uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
518 uint8_t * up;
519
520 if (evpd)
521 inq_cdb[1] |= 1;
522 inq_cdb[2] = (uint8_t)pg_op;
523 sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
524 if (vb > 1) {
525 pr2serr(" INQUIRY cdb: ");
526 for (k = 0; k < INQUIRY_CMDLEN; ++k)
527 pr2serr("%02x ", inq_cdb[k]);
528 pr2serr("\n");
529 }
530 if (resp && (mx_resp_len > 0)) {
531 up = (uint8_t *)resp;
532 up[0] = 0x7f; /* defensive prefill */
533 if (mx_resp_len > 4)
534 up[4] = 0;
535 }
536 if (timeout_secs == 0)
537 timeout_secs = DEF_TIMEOUT_SECS;
538 set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
539 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
540 set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
541 res = do_scsi_pt(ptvp, -1, timeout_secs, vb);
542 ret = sg_cmds_process_resp(ptvp, "inquiry", res, noisy, vb, &sense_cat);
543 resid = get_scsi_pt_resid(ptvp);
544 if (residp)
545 *residp = resid;
546 if (-1 == ret)
547 ;
548 else if (-2 == ret) {
549 switch (sense_cat) {
550 case SG_LIB_CAT_RECOVERED:
551 case SG_LIB_CAT_NO_SENSE:
552 ret = 0;
553 break;
554 default:
555 ret = sense_cat;
556 break;
557 }
558 } else if (ret < 4) {
559 if (vb)
560 pr2serr("%s: got too few bytes (%d)\n", __func__, ret);
561 ret = SG_LIB_CAT_MALFORMED;
562 } else
563 ret = 0;
564
565 if (resid > 0) {
566 if (resid > mx_resp_len) {
567 pr2serr("INQUIRY resid (%d) should never exceed requested "
568 "len=%d\n", resid, mx_resp_len);
569 return ret ? ret : SG_LIB_CAT_MALFORMED;
570 }
571 /* zero unfilled section of response buffer */
572 memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid);
573 }
574 return ret;
575 }
576
577 int
main(int argc,char * argv[])578 main(int argc, char * argv[])
579 {
580 bool do_all = false;
581 bool do_dev_id_vpd = false;
582 bool do_id_ctl = false;
583 bool do_id_ns = false;
584 bool do_self_test = false;
585 bool flagged = false;
586 bool is_nvme = false;
587 int res, c, n, resid, off, len, ln, k, q, num;
588 int curr_dev_name_pos = 0;
589 int do_long = 0;
590 int maxlen = INQUIRY_MAX_RESP_LEN;
591 int self_test = 0;
592 int sg_fd = -1;
593 int ret = 0;
594 int timeout_ms = DEF_TIMEOUT_SECS * 1000;
595 int vb = 0;
596 uint32_t nsid = 0;
597 uint32_t dn_nsid, al_size;
598 uint32_t pg_sz = sg_get_page_size();
599 int64_t ll;
600 uint8_t * al_buff = NULL;
601 uint8_t * free_al_buff = NULL;
602 uint8_t * bp;
603 const char * device_name = NULL;
604 const char * cp;
605 struct sg_pt_base * ptvp = NULL;
606 char cmd_name[32];
607 char b[2048];
608
609 while (1) {
610 int option_index = 0;
611
612 c = getopt_long(argc, argv, "cdhlm:n:s:t:vV", long_options,
613 &option_index);
614 if (c == -1)
615 break;
616
617 switch (c) {
618 case 'c':
619 strcpy(cmd_name, "Identify(ctl)");
620 do_id_ctl = true;
621 break;
622 case 'd':
623 strcpy(cmd_name, "INQUIRY(vpd=0x83)");
624 do_dev_id_vpd = true;
625 break;
626 case 'h':
627 case '?':
628 usage();
629 return 0;
630 case 'l':
631 ++do_long;
632 break;
633 case 'm':
634 maxlen = sg_get_num(optarg);
635 if (maxlen < 0) {
636 pr2serr("bad argument to '--maxlen='\n");
637 return SG_LIB_SYNTAX_ERROR;
638 }
639 break;
640 case 'n':
641 if ((2 == strlen(optarg)) && (0 == memcmp("-1", optarg, 2))) {
642 nsid = NVME_NSID_ALL; /* treat '-1' as (2**32 - 1) */
643 break;
644 }
645 ll = sg_get_llnum(optarg);
646 if ((ll < 0) || (ll > UINT32_MAX)) {
647 pr2serr("bad argument to '--nsid', accept 0 to 0xffffffff\n");
648 return SG_LIB_SYNTAX_ERROR;
649 }
650 strcpy(cmd_name, "Identify(ns)");
651 nsid = (uint32_t)ll;
652 do_id_ns = true;
653 break;
654 case 's':
655 self_test = sg_get_num(optarg);
656 if (self_test < 0) {
657 pr2serr("bad argument to '--self-test=', expect 0 or "
658 "higher\n");
659 return SG_LIB_SYNTAX_ERROR;
660 }
661 strcpy(cmd_name, "Device self-test");
662 do_self_test = true;
663 break;
664 case 't':
665 timeout_ms = sg_get_num(optarg);
666 if (timeout_ms < 0) {
667 pr2serr("bad argument to '--to-ms=', expect 0 or higher\n");
668 return SG_LIB_SYNTAX_ERROR;
669 }
670 break;
671 case 'v':
672 ++vb;
673 break;
674 case 'V':
675 pr2serr(ME "version: %s\n", version_str);
676 return 0;
677 default:
678 pr2serr("unrecognised option code 0x%x ??\n", c);
679 usage();
680 return SG_LIB_SYNTAX_ERROR;
681 }
682 }
683 if (optind < argc) {
684 for (; optind < argc; ++optind) {
685 if (next_dev_name_pos >= MAX_DEV_NAMES) {
686 pr2serr("Only accepts %d DEVICE names\n", MAX_DEV_NAMES);
687 usage();
688 return SG_LIB_SYNTAX_ERROR;
689 }
690 dev_name_arr[next_dev_name_pos++] = argv[optind];
691 }
692 }
693
694 if (next_dev_name_pos < 1) {
695 pr2serr("Need at least one DEVICE, can have up to %d\n\n",
696 MAX_DEV_NAMES);
697 usage();
698 return SG_LIB_SYNTAX_ERROR;
699 }
700
701 if (do_self_test && do_id_ns)
702 do_id_ns = false; /* self-test with DW10 set to nsid */
703 n = (int)do_id_ctl + (int)do_id_ns + (int)do_dev_id_vpd +
704 (int)do_self_test;
705 if (n > 1) {
706 pr2serr("can only have one of --ctl, --dev-id, --nsid= and "
707 "--self-test=\n\n");
708 usage();
709 return SG_LIB_SYNTAX_ERROR;
710 } else if (0 == n) {
711 do_id_ns = true;
712 strcpy(cmd_name, "Identify(ns)");
713 }
714
715 al_size = ((uint32_t)maxlen > pg_sz) ? (uint32_t)maxlen : pg_sz;
716 al_buff = sg_memalign(al_size, pg_sz, &free_al_buff, vb > 3);
717 if (NULL == al_buff) {
718 pr2serr("out of memory allocating page sized buffer (of %u bytes)\n",
719 al_size);
720 return SG_LIB_OS_BASE_ERR + ENOMEM;
721 }
722 device_name = dev_name_arr[curr_dev_name_pos++];
723 sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
724 if (sg_fd < 0) {
725 pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
726 ret = SG_LIB_FILE_ERROR;
727 flagged = true;
728 goto fini;
729 }
730 n = check_pt_file_handle(sg_fd, device_name, vb);
731 if (n < 0) {
732 pr2serr("check_pt_file_handle error: %s: %s\n", device_name,
733 safe_strerror(-n));
734 flagged = true;
735 goto fini;
736 }
737 cp = NULL;
738 switch (n) {
739 case 0:
740 cp = "Unidentified device (SATA disk ?)";
741 break;
742 case 1:
743 cp = "SCSI char device (e.g. in Linux: sg or bsg device)";
744 break;
745 case 2:
746 cp = "SCSI block device (e.g. in FreeBSD: /dev/da0)";
747 break;
748 case 3:
749 cp = "NVMe char device (e.g. in Linux: /dev/nvme0)";
750 break;
751 case 4:
752 cp = "NVMe block device (e.g. in FreeBSD: /dev/nvme0ns1)";
753 break;
754 default:
755 pr2serr("Strange value from check_pt_file_handle() --> %d\n", n);
756 break;
757 }
758 if (cp && (vb || (do_long > 0)))
759 pr2serr("%s\n", cp);
760
761 ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
762 if (NULL == ptvp) {
763 pr2serr("%s: out of memory\n", b);
764 ret = sg_convert_errno(ENOMEM);
765 goto fini;
766 }
767 k = get_scsi_pt_os_err(ptvp);
768 if (k) {
769 pr2serr("OS error from construct_scsi_pt_obj_with_fd(): %s\n",
770 safe_strerror(k));
771 ret = sg_convert_errno(k);
772 goto fini;
773 }
774
775 /* Loop over all given DEVICEs */
776 for (q = 0; q < MAX_DEV_NAMES; ++q) {
777 is_nvme = pt_device_is_nvme(ptvp);
778 if ((curr_dev_name_pos > 1) && vb)
779 pr2serr("Device %d [%s] seems to be %s\n", q + 1, device_name,
780 is_nvme ? "NVMe" : "SCSI or ATA");
781 resid = 0;
782 if (do_dev_id_vpd || (! is_nvme)) {
783 if (do_dev_id_vpd)
784 ret = sg_scsi_inquiry(ptvp, true /* evpd */, VPD_DEVICE_ID,
785 al_buff, maxlen, timeout_ms / 1000,
786 &resid, true, vb);
787 else /* do a standard INQUIRY */
788 ret = sg_scsi_inquiry(ptvp, false /* evpd */, 0, al_buff,
789 maxlen, timeout_ms / 1000, &resid, true,
790 vb);
791 if (ret) {
792 pr2serr("SCSI INQUIRY(%s) failed\n",
793 do_dev_id_vpd ? "dev_id" : "standard");
794 goto fini;
795 }
796 len = maxlen - resid;
797 if (len < 4) {
798 pr2serr("Something wrong with data-in, len=%d (resid=%d)\n",
799 len, resid);
800 goto fini;
801 }
802 if (do_dev_id_vpd) {
803 printf(" Device %d [%s] identification VPD:\n", q + 1,
804 device_name);
805 for (off = -1, bp = al_buff + 4, ln = len - 4;
806 0 == sg_vpd_dev_id_iter(bp, ln, &off, -1, -1, -1); ) {
807 n = sg_get_designation_descriptor_str(" ", bp + off,
808 bp[off + 3] + 4, do_long,
809 do_long > 1, sizeof(b), b);
810 if (n > 0)
811 printf("%s", b);
812 }
813 } else {
814 snprintf(b, sizeof(b), " Device %d [%s] Standard INQUIRY:",
815 q + 1, device_name);
816 std_inq_decode(b, al_buff, len, vb);
817 }
818 clear_scsi_pt_obj(ptvp);
819 } else { /* NVME Identify or Device self-test */
820 bool this_ctl = false;
821 uint16_t sct_sc = 0;
822 uint32_t max_nsid;
823 struct sg_nvme_passthru_cmd n_cmd;
824
825 if ((! do_self_test) && (NVME_NSID_ALL == nsid))
826 do_all = true;
827 num = 1; /* preliminary, may alter */
828 for (k = 0; k < num; ++k) {
829 bp = (uint8_t *)&n_cmd;
830 memset(bp, 0, sizeof(n_cmd));
831 if (do_self_test) {
832 n_cmd.opcode = 0x14; /* Device self-test */
833 n_cmd.nsid = nsid;
834 n_cmd.cdw10 = self_test;
835 if (0 == k) {
836 if (0 == nsid)
837 printf("Starting Device self-test for controller "
838 "only\n");
839 else if (do_all)
840 printf("Starting Device self-test for controller "
841 "and all namespaces\n");
842 else
843 printf("Starting Device self-test for controller "
844 "and namespace %u\n", nsid);
845 }
846 } else { /* one or more variants of Identify */
847 n_cmd.opcode = 0x6; /* Identify */
848 dn_nsid = get_pt_nvme_nsid(ptvp);
849 if ((0 == k) && (do_id_ctl || (0 == nsid) || do_all)) {
850 n_cmd.cdw10 = 0x1; /* Controller */
851 this_ctl = true;
852 } else {
853 n_cmd.cdw10 = 0x0; /* Namespace */
854 if (do_all)
855 n_cmd.nsid = k;
856 else if (nsid > 0)
857 n_cmd.nsid = nsid;
858 else if (dn_nsid > 0)
859 n_cmd.nsid = dn_nsid;
860 else
861 break;
862 this_ctl = false;
863 }
864 sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)al_buff,
865 bp + SG_NVME_PT_ADDR);
866 sg_put_unaligned_le32(pg_sz, bp + SG_NVME_PT_DATA_LEN);
867 }
868 ret = nvme_din_admin_cmd(ptvp, (const uint8_t *)&n_cmd,
869 sizeof(n_cmd), cmd_name, al_buff,
870 pg_sz, timeout_ms, &sct_sc, vb);
871 if (sct_sc || (SG_LIB_NVME_STATUS == ret)) {
872 sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b);
873 pr2serr("%s: %s\n", cmd_name, b);
874 flagged = true;
875 goto fini;
876 }
877 if (ret)
878 goto fini;
879 if (0x6 == n_cmd.opcode) {
880 if (this_ctl) {
881 show_nvme_id_ctl(al_buff, device_name, do_long,
882 &max_nsid);
883 num = max_nsid + 1;
884 } else
885 show_nvme_id_ns(al_buff, n_cmd.nsid, device_name,
886 do_long);
887 }
888
889 clear_scsi_pt_obj(ptvp);
890 if (do_self_test)
891 break;
892 if (do_id_ctl)
893 break;
894 } /* end of for loop */
895 }
896 ret = 0;
897
898 if (sg_fd >= 0) {
899 res = sg_cmds_close_device(sg_fd);
900 if (res < 0) {
901 pr2serr("close error: %s\n", safe_strerror(-res));
902 ret = sg_convert_errno(-res);
903 break;
904 }
905 sg_fd = -1;
906 }
907 if (ret)
908 break;
909 if (curr_dev_name_pos < next_dev_name_pos)
910 device_name = dev_name_arr[curr_dev_name_pos++];
911 else
912 break;
913 if (NULL == device_name) {
914 pr2serr("Unexpected NULL device name at pos=%d\n",
915 curr_dev_name_pos - 1);
916 ret = sg_convert_errno(EINVAL);
917 flagged = true;
918 break;
919 }
920 sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
921 if (sg_fd < 0) {
922 pr2serr(ME "open error: %s: %s\n", device_name,
923 safe_strerror(-sg_fd));
924 ret = sg_convert_errno(-sg_fd);
925 flagged = true;
926 break;
927 }
928 k = set_pt_file_handle(ptvp, sg_fd, vb);
929 if (k) {
930 ret = sg_convert_errno(k);
931 pr2serr("set_pt_file_handle() failed: %s\n", safe_strerror(k));
932 flagged = true;
933 break;
934 }
935 printf("\n");
936 } /* end of "q" outer for loop */
937 fini:
938 if (ptvp) {
939 destruct_scsi_pt_obj(ptvp);
940 ptvp = NULL;
941 }
942 if (free_al_buff)
943 free(free_al_buff);
944 if (sg_fd >= 0) {
945 res = sg_cmds_close_device(sg_fd);
946 if (res < 0) {
947 pr2serr("close error: %s\n", safe_strerror(-res));
948 if (0 == ret)
949 return SG_LIB_FILE_ERROR;
950 }
951 }
952 if (ret && (0 == vb) && (! flagged)) {
953 if (! sg_if_can2stderr("", ret))
954 pr2serr("Some error occurred [%d]\n", ret);
955 }
956 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
957 }
958