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