• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2022 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 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <stdbool.h>
14 #include <string.h>
15 #include <ctype.h>
16 #define __STDC_FORMAT_MACROS 1
17 #include <inttypes.h>
18 
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "sg_lib.h"
25 #include "sg_pt.h"
26 #include "sg_unaligned.h"
27 #include "sg_pr2serr.h"
28 #include "sg_pr2serr.h"
29 
30 #if (HAVE_NVME && (! IGNORE_NVME))
31 #include "sg_pt_nvme.h"
32 #endif
33 
34 static const char * scsi_pt_version_str = "3.19 20220127";
35 
36 /* List of external functions that need to be defined for each OS are
37  * listed at the top of sg_pt_dummy.c   */
38 
39 const char *
scsi_pt_version()40 scsi_pt_version()
41 {
42     return scsi_pt_version_str;
43 }
44 
45 const char *
sg_pt_version()46 sg_pt_version()
47 {
48     return scsi_pt_version_str;
49 }
50 
51 
52 #if (HAVE_NVME && (! IGNORE_NVME))
53 /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
54 
55 #define SAVING_PARAMS_UNSUP 0x39
56 #define INVALID_FIELD_IN_CDB 0x24
57 #define INVALID_FIELD_IN_PARAM_LIST 0x26
58 #define PARAMETER_LIST_LENGTH_ERR 0x1a
59 
60 static const char * nvme_scsi_vendor_str = "NVMe    ";
61 
62 
63 #define F_SA_LOW                0x80    /* cdb byte 1, bits 4 to 0 */
64 #define F_SA_HIGH               0x100   /* as used by variable length cdbs */
65 #define FF_SA (F_SA_HIGH | F_SA_LOW)
66 #define F_INV_OP                0x200
67 
68 /* Table of SCSI operation code (opcodes) supported by SNTL */
69 static struct sg_opcode_info_t sg_opcode_info_arr[] =
70 {
71     {0x0, 0, 0, {6,              /* TEST UNIT READY */
72       0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
73     {0x3, 0, 0, {6,             /* REQUEST SENSE */
74       0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
75     {0x12, 0, 0, {6,            /* INQUIRY */
76       0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
77     {0x1b, 0, 0, {6,            /* START STOP UNIT */
78       0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
79     {0x1c, 0, 0, {6,            /* RECEIVE DIAGNOSTIC RESULTS */
80       0x1, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
81     {0x1d, 0, 0, {6,            /* SEND DIAGNOSTIC */
82       0xf7, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
83     {0x25, 0, 0, {10,            /* READ CAPACITY(10) */
84       0x1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0, 0, 0} },
85     {0x28, 0, 0, {10,            /* READ(10) */
86       0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
87       0, 0} },
88     {0x2a, 0, 0, {10,            /* WRITE(10) */
89       0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
90       0, 0} },
91     {0x2f, 0, 0, {10,            /* VERIFY(10) */
92       0xf6, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
93       0, 0} },
94     {0x35, 0, 0, {10,            /* SYNCHRONIZE CACHE(10) */
95       0x7, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
96       0, 0} },
97     {0x41, 0, 0, {10,            /* WRITE SAME(10) */
98       0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
99       0, 0} },
100     {0x55, 0, 0, {10,           /* MODE SELECT(10) */
101       0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
102     {0x5a, 0, 0, {10,           /* MODE SENSE(10) */
103       0x18, 0xff, 0xff, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
104     {0x88, 0, 0, {16,            /* READ(16) */
105       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
106       0xff, 0xff, 0xff, 0xc7} },
107     {0x8a, 0, 0, {16,            /* WRITE(16) */
108       0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
109       0xff, 0xff, 0xff, 0xc7} },
110     {0x8f, 0, 0, {16,            /* VERIFY(16) */
111       0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
112       0xff, 0xff, 0x3f, 0xc7} },
113     {0x91, 0, 0, {16,            /* SYNCHRONIZE CACHE(16) */
114       0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
115       0xff, 0xff, 0x3f, 0xc7} },
116     {0x93, 0, 0, {16,            /* WRITE SAME(16) */
117       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
118       0xff, 0xff, 0x3f, 0xc7} },
119     {0x9e, 0x10, F_SA_LOW, {16,  /* READ CAPACITY(16) [service action in] */
120       0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
121       0xff, 0xff, 0x1, 0xc7} },
122     {0xa0, 0, 0, {12,           /* REPORT LUNS */
123       0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} },
124     {0xa3, 0xc, F_SA_LOW, {12,  /* REPORT SUPPORTED OPERATION CODES */
125       0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0,
126       0} },
127     {0xa3, 0xd, F_SA_LOW, {12,  /* REPORT SUPPORTED TASK MAN. FUNCTIONS */
128       0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} },
129 
130     {0xff, 0xffff, 0xffff, {0,  /* Sentinel, keep as last element */
131       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
132 };
133 
134 /* Returns pointer to array of struct sg_opcode_info_t of SCSI commands
135  * translated to NVMe. */
136 const struct sg_opcode_info_t *
sg_get_opcode_translation(void)137 sg_get_opcode_translation(void)
138 {
139     return sg_opcode_info_arr;
140 }
141 
142 /* Given the NVMe Identify controller response and optionally the NVMe
143  * Identify namespace response (NULL otherwise), generate the SCSI VPD
144  * page 0x83 (device identification) descriptor(s) in dop. Return the
145  * number of bytes written which will not exceed max_do_len. Probably use
146  * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
147  * protocol (tproto) should be -1 if not known, else SCSI value.
148  * N.B. Does not write total VPD page length into dop[2:3] . */
149 int
sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,const uint8_t * nvme_id_ns_p,int pdt,int tproto,uint8_t * dop,int max_do_len)150 sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
151                            const uint8_t * nvme_id_ns_p, int pdt,
152                            int tproto, uint8_t * dop, int max_do_len)
153 {
154     bool have_nguid, have_eui64;
155     int k, n;
156     char b[4];
157 
158     if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56))
159         return 0;
160 
161     memset(dop, 0, max_do_len);
162     dop[0] = 0x1f & pdt;  /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */
163     dop[1] = 0x83;      /* Device Identification VPD page number */
164     /* Build a T10 Vendor ID based designator (desig_id=1) for controller */
165     if (tproto >= 0) {
166         dop[4] = ((0xf & tproto) << 4) | 0x2;
167         dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */
168     } else {
169         dop[4] = 0x2;  /* Prococol id=0, code_set=2 (ASCII) */
170         dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */
171     }
172     memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe    " */
173     memcpy(dop + 16, nvme_id_ctl_p + 24, 40);  /* MN */
174     for (k = 40; k > 0; --k) {
175         if (' ' == dop[15 + k])
176             dop[15 + k] = '_'; /* convert trailing spaces */
177         else
178             break;
179     }
180     if (40 == k)
181         --k;
182     n = 16 + 1 + k;
183     if (max_do_len < (n + 20))
184         return 0;
185     memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */
186     for (k = 20; k > 0; --k) {  /* trim trailing spaces */
187         if (' ' == dop[n + k - 1])
188             dop[n + k - 1] = '\0';
189         else
190             break;
191     }
192     n += k;
193     if (0 != (n % 4))
194         n = ((n / 4) + 1) * 4;  /* round up to next modulo 4 */
195     dop[7] = n - 8;
196     if (NULL == nvme_id_ns_p)
197         return n;
198 
199     /* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in
200      * NVME Identify for namespace. If found form a EUI and a SCSI string
201      * descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */
202     have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16);
203     have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8);
204     if ((! have_nguid) && (! have_eui64))
205         return n;
206     if (have_nguid) {
207         if (max_do_len < (n + 20))
208             return n;
209         dop[n + 0] = 0x1;  /* Prococol id=0, code_set=1 (binary) */
210         dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
211         dop[n + 3] = 16;
212         memcpy(dop + n + 4, nvme_id_ns_p + 104, 16);
213         n += 20;
214         if (max_do_len < (n + 40))
215             return n;
216         dop[n + 0] = 0x3;  /* Prococol id=0, code_set=3 (utf8) */
217         dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
218         dop[n + 3] = 36;
219         memcpy(dop + n + 4, "eui.", 4);
220         for (k = 0; k < 16; ++k) {
221             snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]);
222             memcpy(dop + n + 8 + (2 * k), b, 2);
223         }
224         return n + 40;
225     } else {    /* have_eui64 is true, 8 byte identifier */
226         if (max_do_len < (n + 12))
227             return n;
228         dop[n + 0] = 0x1;  /* Prococol id=0, code_set=1 (binary) */
229         dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
230         dop[n + 3] = 8;
231         memcpy(dop + n + 4, nvme_id_ns_p + 120, 8);
232         n += 12;
233         if (max_do_len < (n + 24))
234             return n;
235         dop[n + 0] = 0x3;  /* Prococol id=0, code_set=3 (utf8) */
236         dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
237         dop[n + 3] = 20;
238         memcpy(dop + n + 4, "eui.", 4);
239         for (k = 0; k < 8; ++k) {
240             snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]);
241             memcpy(dop + n + 8 + (2 * k), b, 2);
242         }
243         return n + 24;
244     }
245 }
246 
247 /* Disconnect-Reconnect page for mode_sense */
248 static int
resp_disconnect_pg(uint8_t * p,int pcontrol)249 resp_disconnect_pg(uint8_t * p, int pcontrol)
250 {
251     uint8_t disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
252                                0, 0, 0, 0, 0, 0, 0, 0};
253 
254     memcpy(p, disconnect_pg, sizeof(disconnect_pg));
255     if (1 == pcontrol)
256         memset(p + 2, 0, sizeof(disconnect_pg) - 2);
257     return sizeof(disconnect_pg);
258 }
259 
260 static uint8_t caching_m_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
261                                  0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,
262                                  0, 0, 0, 0};
263 
264 /* Control mode page (SBC) for mode_sense */
265 static int
resp_caching_m_pg(unsigned char * p,int pcontrol,bool wce)266 resp_caching_m_pg(unsigned char *p, int pcontrol, bool wce)
267 {       /* Caching page for mode_sense */
268         uint8_t ch_caching_m_pg[] = {/* 0x8, 18, */ 0x4, 0, 0, 0, 0, 0,
269                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
270         uint8_t d_caching_m_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
271                 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
272 
273         // if (SDEBUG_OPT_N_WCE & sdebug_opts)
274                 caching_m_pg[2] &= ~0x4;  /* set WCE=0 (default WCE=1) */
275         if ((0 == pcontrol) || (3 == pcontrol)) {
276             if (wce)
277                 caching_m_pg[2] |= 0x4;
278             else
279                 caching_m_pg[2] &= ~0x4;
280         }
281         memcpy(p, caching_m_pg, sizeof(caching_m_pg));
282         if (1 == pcontrol) {
283             if (wce)
284                 ch_caching_m_pg[2] |= 0x4;
285             else
286                 ch_caching_m_pg[2] &= ~0x4;
287             memcpy(p + 2, ch_caching_m_pg, sizeof(ch_caching_m_pg));
288         }
289         else if (2 == pcontrol) {
290             if (wce)
291                 d_caching_m_pg[2] |= 0x4;
292             else
293                 d_caching_m_pg[2] &= ~0x4;
294             memcpy(p, d_caching_m_pg, sizeof(d_caching_m_pg));
295         }
296         return sizeof(caching_m_pg);
297 }
298 
299 static uint8_t ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
300                               0, 0, 0x2, 0x4b};
301 
302 /* Control mode page for mode_sense */
303 static int
resp_ctrl_m_pg(uint8_t * p,int pcontrol)304 resp_ctrl_m_pg(uint8_t *p, int pcontrol)
305 {
306     uint8_t ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
307                               0, 0, 0, 0};
308     uint8_t d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
309                              0, 0, 0x2, 0x4b};
310 
311     memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
312     if (1 == pcontrol)
313         memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
314     else if (2 == pcontrol)
315         memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
316     return sizeof(ctrl_m_pg);
317 }
318 
319 static uint8_t ctrl_ext_m_pg[] = {0x4a, 0x1, 0, 0x1c,  0, 0, 0x40, 0,
320                                   0, 0, 0, 0,  0, 0, 0, 0,
321                                   0, 0, 0, 0,  0, 0, 0, 0,
322                                   0, 0, 0, 0,  0, 0, 0, 0, };
323 
324 /* Control Extension mode page [0xa,0x1] for mode_sense */
325 static int
resp_ctrl_ext_m_pg(uint8_t * p,int pcontrol)326 resp_ctrl_ext_m_pg(uint8_t *p, int pcontrol)
327 {
328     uint8_t ch_ctrl_ext_m_pg[] = {/* 0x4a, 0x1, 0, 0x1c, */ 0, 0, 0, 0,
329                          0, 0, 0, 0,  0, 0, 0, 0,
330                          0, 0, 0, 0,  0, 0, 0, 0,
331                          0, 0, 0, 0,  0, 0, 0, 0, };
332     uint8_t d_ctrl_ext_m_pg[] = {0x4a, 0x1, 0, 0x1c,  0, 0, 0x40, 0,
333                          0, 0, 0, 0,  0, 0, 0, 0,
334                          0, 0, 0, 0,  0, 0, 0, 0,
335                          0, 0, 0, 0,  0, 0, 0, 0, };
336 
337     memcpy(p, ctrl_ext_m_pg, sizeof(ctrl_ext_m_pg));
338     if (1 == pcontrol)
339         memcpy(p + 4, ch_ctrl_ext_m_pg, sizeof(ch_ctrl_ext_m_pg));
340     else if (2 == pcontrol)
341         memcpy(p, d_ctrl_ext_m_pg, sizeof(d_ctrl_ext_m_pg));
342     return sizeof(ctrl_ext_m_pg);
343 }
344 
345 static uint8_t iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0};
346 
347 /* Informational Exceptions control mode page for mode_sense */
348 static int
resp_iec_m_pg(uint8_t * p,int pcontrol)349 resp_iec_m_pg(uint8_t *p, int pcontrol)
350 {
351     uint8_t ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0, 0, 0,
352                              0x0, 0x0};
353     uint8_t d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0};
354 
355     memcpy(p, iec_m_pg, sizeof(iec_m_pg));
356     if (1 == pcontrol)
357         memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
358     else if (2 == pcontrol)
359         memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
360     return sizeof(iec_m_pg);
361 }
362 
363 static uint8_t vs_ua_m_pg[] = {0x0, 0xe, 0, 0, 0, 0, 0, 0,
364                                0, 0, 0, 0, 0, 0, 0};
365 
366 /* Vendor specific Unit Attention mode page for mode_sense */
367 static int
resp_vs_ua_m_pg(uint8_t * p,int pcontrol)368 resp_vs_ua_m_pg(uint8_t *p, int pcontrol)
369 {
370     uint8_t ch_vs_ua_m_pg[] = {/* 0x0, 0xe, */ 0xff, 0, 0, 0, 0, 0,
371                                0, 0, 0, 0, 0, 0, 0, 0};
372     uint8_t d_vs_ua_m_pg[] = {0x0, 0xe, 0, 0, 0, 0, 0, 0,
373                               0, 0, 0, 0, 0, 0, 0, 0};
374 
375     memcpy(p, vs_ua_m_pg, sizeof(vs_ua_m_pg));
376     if (1 == pcontrol)
377         memcpy(p + 2, ch_vs_ua_m_pg, sizeof(ch_vs_ua_m_pg));
378     else if (2 == pcontrol)
379         memcpy(p, d_vs_ua_m_pg, sizeof(d_vs_ua_m_pg));
380     return sizeof(vs_ua_m_pg);
381 }
382 
383 void
sntl_init_dev_stat(struct sg_sntl_dev_state_t * dsp)384 sntl_init_dev_stat(struct sg_sntl_dev_state_t * dsp)
385 {
386     if (dsp) {
387         dsp->scsi_dsense = !! (0x4 & ctrl_m_pg[2]);
388         dsp->enclosure_override = vs_ua_m_pg[2];
389     }
390 }
391 
392 
393 #define SDEBUG_MAX_MSENSE_SZ 256
394 
395 /* Only support MODE SENSE(10). Returns the number of bytes written to dip,
396  * or -1 if error info placed in resp. */
397 int
sntl_resp_mode_sense10(const struct sg_sntl_dev_state_t * dsp,const uint8_t * cdbp,uint8_t * dip,int mx_di_len,struct sg_sntl_result_t * resp)398 sntl_resp_mode_sense10(const struct sg_sntl_dev_state_t * dsp,
399                        const uint8_t * cdbp, uint8_t * dip, int mx_di_len,
400                        struct sg_sntl_result_t * resp)
401 {
402     bool dbd, llbaa, is_disk, bad_pcode;
403     int pcontrol, pcode, subpcode, bd_len, alloc_len, offset, len;
404     const uint32_t num_blocks = 0x100000;       /* made up */
405     const uint32_t lb_size = 512;               /* guess */
406     uint8_t dev_spec;
407     uint8_t * ap;
408     uint8_t arr[SDEBUG_MAX_MSENSE_SZ];
409 
410     memset(resp, 0, sizeof(*resp));
411     dbd = !! (cdbp[1] & 0x8);         /* disable block descriptors */
412     pcontrol = (cdbp[2] & 0xc0) >> 6;
413     pcode = cdbp[2] & 0x3f;
414     subpcode = cdbp[3];
415     llbaa = !!(cdbp[1] & 0x10);
416     is_disk = sg_pdt_s_eq(sg_lib_pdt_decay(dsp->pdt), PDT_DISK_ZBC);
417     if (is_disk && !dbd)
418         bd_len = llbaa ? 16 : 8;
419     else
420         bd_len = 0;
421     alloc_len = sg_get_unaligned_be16(cdbp + 7);
422     memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
423     if (0x3 == pcontrol) {  /* Saving values not supported */
424         resp->asc = SAVING_PARAMS_UNSUP;
425         goto err_out;
426     }
427     /* for disks set DPOFUA bit and clear write protect (WP) bit */
428     if (is_disk)
429         dev_spec = 0x10;        /* =0x90 if WP=1 implies read-only */
430     else
431         dev_spec = 0x0;
432     arr[3] = dev_spec;
433     if (16 == bd_len)
434         arr[4] = 0x1;   /* set LONGLBA bit */
435     arr[7] = bd_len;        /* assume 255 or less */
436     offset = 8;
437     ap = arr + offset;
438 
439     if (8 == bd_len) {
440         sg_put_unaligned_be32(num_blocks, ap + 0);
441         sg_put_unaligned_be16((uint16_t)lb_size, ap + 6);
442         offset += bd_len;
443         ap = arr + offset;
444     } else if (16 == bd_len) {
445         sg_put_unaligned_be64(num_blocks, ap + 0);
446         sg_put_unaligned_be32(lb_size, ap + 12);
447         offset += bd_len;
448         ap = arr + offset;
449     }
450     bad_pcode = false;
451 
452     switch (pcode) {
453     case 0x2:       /* Disconnect-Reconnect page, all devices */
454         if (0x0 == subpcode)
455             len = resp_disconnect_pg(ap, pcontrol);
456         else {
457             len = 0;
458             bad_pcode = true;
459         }
460         offset += len;
461         break;
462     case 0x8:       /* Caching Mode page, disk (like) devices */
463         if (! is_disk) {
464             len = 0;
465             bad_pcode = true;
466         } else if (0x0 == subpcode)
467             len = resp_caching_m_pg(ap, pcontrol, dsp->wce);
468         else {
469             len = 0;
470             bad_pcode = true;
471         }
472         offset += len;
473         break;
474     case 0xa:       /* Control Mode page, all devices */
475         if (0x0 == subpcode)
476             len = resp_ctrl_m_pg(ap, pcontrol);
477         else if (0x1 == subpcode)
478             len = resp_ctrl_ext_m_pg(ap, pcontrol);
479         else {
480             len = 0;
481             bad_pcode = true;
482         }
483         offset += len;
484         break;
485     case 0x1c:      /* Informational Exceptions Mode page, all devices */
486         if (0x0 == subpcode)
487             len = resp_iec_m_pg(ap, pcontrol);
488         else {
489             len = 0;
490             bad_pcode = true;
491         }
492         offset += len;
493         break;
494     case 0x3f:      /* Read all Mode pages */
495         if ((0 == subpcode) || (0xff == subpcode)) {
496             len = 0;
497             len = resp_disconnect_pg(ap + len, pcontrol);
498             if (is_disk)
499                 len += resp_caching_m_pg(ap + len, pcontrol, dsp->wce);
500             len += resp_ctrl_m_pg(ap + len, pcontrol);
501             if (0xff == subpcode)
502                 len += resp_ctrl_ext_m_pg(ap + len, pcontrol);
503             len += resp_iec_m_pg(ap + len, pcontrol);
504             len += resp_vs_ua_m_pg(ap + len, pcontrol);
505             offset += len;
506         } else {
507             resp->asc = INVALID_FIELD_IN_CDB;
508             resp->in_byte = 3;
509             resp->in_bit = 255;
510             goto err_out;
511         }
512         break;
513     case 0x0:       /* Vendor specific "Unit Attention" mode page */
514         /* all sub-page codes ?? */
515         len = resp_vs_ua_m_pg(ap, pcontrol);
516         offset += len;
517         break;      /* vendor is "NVMe    " (from INQUIRY field) */
518     default:
519         bad_pcode = true;
520         break;
521     }
522     if (bad_pcode) {
523         resp->asc = INVALID_FIELD_IN_CDB;
524         resp->in_byte = 2;
525         resp->in_bit = 5;
526         goto err_out;
527     }
528     sg_put_unaligned_be16(offset - 2, arr + 0);
529     len = (alloc_len < offset) ? alloc_len : offset;
530     len = (len < mx_di_len) ? len : mx_di_len;
531     memcpy(dip, arr, len);
532     return len;
533 
534 err_out:
535     resp->sstatus = SAM_STAT_CHECK_CONDITION;
536     resp->sk = SPC_SK_ILLEGAL_REQUEST;
537     return -1;
538 }
539 
540 #define SDEBUG_MAX_MSELECT_SZ 512
541 
542 /* Only support MODE SELECT(10). Returns number of bytes used from dop,
543  * else -1 on error with sense code placed in resp. */
544 int
sntl_resp_mode_select10(struct sg_sntl_dev_state_t * dsp,const uint8_t * cdbp,const uint8_t * dop,int do_len,struct sg_sntl_result_t * resp)545 sntl_resp_mode_select10(struct sg_sntl_dev_state_t * dsp,
546                         const uint8_t * cdbp, const uint8_t * dop, int do_len,
547                         struct sg_sntl_result_t * resp)
548 {
549     int pf, sp, ps, md_len, bd_len, off, spf, pg_len, rlen, param_len, mpage;
550     int sub_mpage;
551     uint8_t arr[SDEBUG_MAX_MSELECT_SZ];
552 
553     memset(resp, 0, sizeof(*resp));
554     memset(arr, 0, sizeof(arr));
555     pf = cdbp[1] & 0x10;
556     sp = cdbp[1] & 0x1;
557     param_len = sg_get_unaligned_be16(cdbp + 7);
558     if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
559         resp->asc = INVALID_FIELD_IN_CDB;
560         resp->in_byte = 1;
561         if (sp)
562             resp->in_bit = 0;
563         else if (0 == pf)
564             resp->in_bit = 4;
565         else {
566             resp->in_byte = 7;
567             resp->in_bit = 255;
568         }
569         goto err_out;
570     }
571     rlen = (do_len < param_len) ? do_len : param_len;
572     memcpy(arr, dop, rlen);
573     md_len = sg_get_unaligned_be16(arr + 0) + 2;
574     bd_len = sg_get_unaligned_be16(arr + 6);
575     if (md_len > 2) {
576         resp->asc = INVALID_FIELD_IN_PARAM_LIST;
577         resp->in_byte = 0;
578         resp->in_bit = 255;
579         goto err_out;
580     }
581     off = bd_len + 8;
582     mpage = arr[off] & 0x3f;
583     ps = !!(arr[off] & 0x80);
584     if (ps) {
585         resp->asc = INVALID_FIELD_IN_PARAM_LIST;
586         resp->in_byte = off;
587         resp->in_bit = 7;
588         goto err_out;
589     }
590     spf = !!(arr[off] & 0x40);
591     pg_len = spf ? (sg_get_unaligned_be16(arr + off + 2) + 4) :
592                    (arr[off + 1] + 2);
593     sub_mpage = spf ? arr[off + 1] : 0;
594     if ((pg_len + off) > param_len) {
595         resp->asc = PARAMETER_LIST_LENGTH_ERR;
596         goto err_out;
597     }
598     switch (mpage) {
599     case 0x8:      /* Caching Mode page */
600         if (0x0 == sub_mpage) {
601             if (caching_m_pg[1] == arr[off + 1]) {
602                 memcpy(caching_m_pg + 2, arr + off + 2,
603                        sizeof(caching_m_pg) - 2);
604                 dsp->wce = !!(caching_m_pg[2] & 0x4);
605                 dsp->wce_changed = true;
606                 break;
607             }
608         }
609         goto def_case;
610     case 0xa:      /* Control Mode page */
611         if (0x0 == sub_mpage) {
612             if (ctrl_m_pg[1] == arr[off + 1]) {
613                 memcpy(ctrl_m_pg + 2, arr + off + 2,
614                        sizeof(ctrl_m_pg) - 2);
615                 dsp->scsi_dsense = !!(ctrl_m_pg[2] & 0x4);
616                 break;
617             }
618         }
619         goto def_case;
620     case 0x1c:      /* Informational Exceptions Mode page (SBC) */
621         if (0x0 == sub_mpage) {
622             if (iec_m_pg[1] == arr[off + 1]) {
623                 memcpy(iec_m_pg + 2, arr + off + 2,
624                        sizeof(iec_m_pg) - 2);
625                 break;
626             }
627         }
628         goto def_case;
629     case 0x0:       /* Vendor specific "Unit Attention" mode page */
630         if (vs_ua_m_pg[1] == arr[off + 1]) {
631             memcpy(vs_ua_m_pg + 2, arr + off + 2,
632                    sizeof(vs_ua_m_pg) - 2);
633             dsp->enclosure_override = vs_ua_m_pg[2];
634         }
635         break;
636     default:
637 def_case:
638         resp->asc = INVALID_FIELD_IN_PARAM_LIST;
639         resp->in_byte = off;
640         resp->in_bit = 5;
641         goto err_out;
642     }
643     return rlen;
644 
645 err_out:
646     resp->sk = SPC_SK_ILLEGAL_REQUEST;
647     resp->sstatus = SAM_STAT_CHECK_CONDITION;
648     return -1;
649 }
650 
651 #endif          /* (HAVE_NVME && (! IGNORE_NVME)) [near line 140] */
652