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