1 /*
2 * Copyright (c) 2007-2021 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 /* sg_pt_solaris version 1.15 20210617 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/param.h>
22
23 /* Solaris headers */
24 #include <sys/scsi/generic/commands.h>
25 #include <sys/scsi/generic/status.h>
26 #include <sys/scsi/impl/types.h>
27 #include <sys/scsi/impl/uscsi.h>
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "sg_pt.h"
34 #include "sg_lib.h"
35
36
37 #define DEF_TIMEOUT 60 /* 60 seconds */
38
39 struct sg_pt_solaris_scsi {
40 struct uscsi_cmd uscsi;
41 int max_sense_len;
42 int in_err;
43 int os_err;
44 bool is_nvme;
45 int dev_fd;
46 };
47
48 struct sg_pt_base {
49 struct sg_pt_solaris_scsi impl;
50 };
51
52
53 /* Returns >= 0 if successful. If error in Unix returns negated errno. */
54 int
scsi_pt_open_device(const char * device_name,bool read_only,int verbose)55 scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
56 {
57 int oflags = 0 /* O_NONBLOCK*/ ;
58
59 oflags |= (read_only ? O_RDONLY : O_RDWR);
60 return scsi_pt_open_flags(device_name, oflags, verbose);
61 }
62
63 /* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
64 * together. The 'flags' argument is ignored in Solaris.
65 * Returns >= 0 if successful, otherwise returns negated errno. */
66 int
scsi_pt_open_flags(const char * device_name,int flags_arg,int verbose)67 scsi_pt_open_flags(const char * device_name, int flags_arg, int verbose)
68 {
69 int oflags = O_NONBLOCK | O_RDWR;
70 int fd;
71
72 flags_arg = flags_arg; /* ignore flags argument, suppress warning */
73 if (verbose > 1) {
74 fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
75 "open %s with flags=0x%x\n", device_name, oflags);
76 }
77 fd = open(device_name, oflags);
78 if (fd < 0)
79 fd = -errno;
80 return fd;
81 }
82
83 /* Returns 0 if successful. If error in Unix returns negated errno. */
84 int
scsi_pt_close_device(int device_fd)85 scsi_pt_close_device(int device_fd)
86 {
87 int res;
88
89 res = close(device_fd);
90 if (res < 0)
91 res = -errno;
92 return res;
93 }
94
95 struct sg_pt_base *
construct_scsi_pt_obj_with_fd(int dev_fd,int verbose)96 construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
97 {
98 struct sg_pt_solaris_scsi * ptp;
99
100 ptp = (struct sg_pt_solaris_scsi *)
101 calloc(1, sizeof(struct sg_pt_solaris_scsi));
102 if (ptp) {
103 ptp->dev_fd = (dev_fd < 0) ? -1 : dev_fd;
104 ptp->is_nvme = false;
105 ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
106 /* Comment in Illumos suggest USCSI_ISOLATE and USCSI_DIAGNOSE (both)
107 * seem to mean "don't retry" which is what we want. */
108 ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE |
109 USCSI_RQENABLE;
110 } else if (verbose)
111 fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
112 "%s: calloc() out of memory\n", __func__);
113 return (struct sg_pt_base *)ptp;
114 }
115
116 struct sg_pt_base *
construct_scsi_pt_obj()117 construct_scsi_pt_obj()
118 {
119 return construct_scsi_pt_obj_with_fd(-1, 0);
120 }
121
122 void
destruct_scsi_pt_obj(struct sg_pt_base * vp)123 destruct_scsi_pt_obj(struct sg_pt_base * vp)
124 {
125 struct sg_pt_solaris_scsi * ptp = &vp->impl;
126
127 if (ptp)
128 free(ptp);
129 }
130
131 void
clear_scsi_pt_obj(struct sg_pt_base * vp)132 clear_scsi_pt_obj(struct sg_pt_base * vp)
133 {
134 bool is_nvme;
135 int dev_fd;
136 struct sg_pt_solaris_scsi * ptp = &vp->impl;
137
138 if (ptp) {
139 is_nvme = ptp->is_nvme;
140 dev_fd = ptp->dev_fd;
141 memset(ptp, 0, sizeof(struct sg_pt_solaris_scsi));
142 ptp->dev_fd = dev_fd;
143 ptp->is_nvme = is_nvme;
144 ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
145 ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE |
146 USCSI_RQENABLE;
147 }
148 }
149
150 void
partial_clear_scsi_pt_obj(struct sg_pt_base * vp)151 partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
152 {
153 struct sg_pt_solaris_scsi * ptp = &vp->impl;
154
155 if (ptp) {
156 ptp->in_err = 0;
157 ptp->os_err = 0;
158 ptp->uscsi.uscsi_status = 0;
159 ptp->uscsi.uscsi_bufaddr = NULL;
160 ptp->uscsi.uscsi_buflen = 0;
161 ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE |
162 USCSI_RQENABLE;
163 }
164 }
165
166 void
set_scsi_pt_cdb(struct sg_pt_base * vp,const uint8_t * cdb,int cdb_len)167 set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
168 int cdb_len)
169 {
170 struct sg_pt_solaris_scsi * ptp = &vp->impl;
171
172 ptp->uscsi.uscsi_cdb = (char *)cdb;
173 ptp->uscsi.uscsi_cdblen = cdb_len;
174 }
175
176 int
get_scsi_pt_cdb_len(const struct sg_pt_base * vp)177 get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
178 {
179 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
180
181 return ptp->uscsi.uscsi_cdblen;
182 }
183
184 uint8_t *
get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)185 get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
186 {
187 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
188
189 return (uint8_t *)ptp->uscsi.uscsi_cdb;
190 }
191
192 void
set_scsi_pt_sense(struct sg_pt_base * vp,uint8_t * sense,int max_sense_len)193 set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense,
194 int max_sense_len)
195 {
196 struct sg_pt_solaris_scsi * ptp = &vp->impl;
197
198 if (sense && (max_sense_len > 0))
199 memset(sense, 0, max_sense_len);
200 ptp->uscsi.uscsi_rqbuf = (char *)sense;
201 ptp->uscsi.uscsi_rqlen = max_sense_len;
202 ptp->max_sense_len = max_sense_len;
203 }
204
205 /* from device */
206 void
set_scsi_pt_data_in(struct sg_pt_base * vp,uint8_t * dxferp,int dxfer_len)207 set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp,
208 int dxfer_len)
209 {
210 struct sg_pt_solaris_scsi * ptp = &vp->impl;
211
212 if (ptp->uscsi.uscsi_bufaddr)
213 ++ptp->in_err;
214 if (dxfer_len > 0) {
215 ptp->uscsi.uscsi_bufaddr = (char *)dxferp;
216 ptp->uscsi.uscsi_buflen = dxfer_len;
217 ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE |
218 USCSI_RQENABLE;
219 }
220 }
221
222 /* to device */
223 void
set_scsi_pt_data_out(struct sg_pt_base * vp,const uint8_t * dxferp,int dxfer_len)224 set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp,
225 int dxfer_len)
226 {
227 struct sg_pt_solaris_scsi * ptp = &vp->impl;
228
229 if (ptp->uscsi.uscsi_bufaddr)
230 ++ptp->in_err;
231 if (dxfer_len > 0) {
232 ptp->uscsi.uscsi_bufaddr = (char *)dxferp;
233 ptp->uscsi.uscsi_buflen = dxfer_len;
234 ptp->uscsi.uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | USCSI_DIAGNOSE |
235 USCSI_RQENABLE;
236 }
237 }
238
239 void
set_scsi_pt_packet_id(struct sg_pt_base * vp,int pack_id)240 set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
241 {
242 // struct sg_pt_solaris_scsi * ptp = &vp->impl;
243
244 vp = vp; /* ignore and suppress warning */
245 pack_id = pack_id; /* ignore and suppress warning */
246 }
247
248 void
set_scsi_pt_tag(struct sg_pt_base * vp,uint64_t tag)249 set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
250 {
251 // struct sg_pt_solaris_scsi * ptp = &vp->impl;
252
253 vp = vp; /* ignore and suppress warning */
254 tag = tag; /* ignore and suppress warning */
255 }
256
257 /* Note that task management function codes are transport specific */
258 void
set_scsi_pt_task_management(struct sg_pt_base * vp,int tmf_code)259 set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
260 {
261 struct sg_pt_solaris_scsi * ptp = &vp->impl;
262
263 ++ptp->in_err;
264 tmf_code = tmf_code; /* dummy to silence compiler */
265 }
266
267 void
set_scsi_pt_task_attr(struct sg_pt_base * vp,int attribute,int priority)268 set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
269 {
270 struct sg_pt_solaris_scsi * ptp = &vp->impl;
271
272 ++ptp->in_err;
273 attribute = attribute; /* dummy to silence compiler */
274 priority = priority; /* dummy to silence compiler */
275 }
276
277 void
set_scsi_pt_flags(struct sg_pt_base * objp,int flags)278 set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
279 {
280 /* do nothing, suppress warnings */
281 objp = objp;
282 flags = flags;
283 }
284
285 /* Executes SCSI command (or at least forwards it to lower layers).
286 * Clears os_err field prior to active call (whose result may set it
287 * again). */
288 int
do_scsi_pt(struct sg_pt_base * vp,int fd,int time_secs,int verbose)289 do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
290 {
291 struct sg_pt_solaris_scsi * ptp = &vp->impl;
292 FILE * ferr = sg_warnings_strm ? sg_warnings_strm : stderr;
293
294 ptp->os_err = 0;
295 if (ptp->in_err) {
296 if (verbose)
297 fprintf(ferr, "Replicated or unused set_scsi_pt... functions\n");
298 return SCSI_PT_DO_BAD_PARAMS;
299 }
300 if (fd < 0) {
301 if (ptp->dev_fd < 0) {
302 if (verbose)
303 fprintf(ferr, "%s: No device file descriptor given\n",
304 __func__);
305 return SCSI_PT_DO_BAD_PARAMS;
306 }
307 } else {
308 if (ptp->dev_fd >= 0) {
309 if (fd != ptp->dev_fd) {
310 if (verbose)
311 fprintf(ferr, "%s: file descriptor given to create and "
312 "this differ\n", __func__);
313 return SCSI_PT_DO_BAD_PARAMS;
314 }
315 } else
316 ptp->dev_fd = fd;
317 }
318 if (NULL == ptp->uscsi.uscsi_cdb) {
319 if (verbose)
320 fprintf(ferr, "%s: No SCSI command (cdb) given\n", __func__);
321 return SCSI_PT_DO_BAD_PARAMS;
322 }
323 if (time_secs > 0)
324 ptp->uscsi.uscsi_timeout = time_secs;
325
326 if (ioctl(ptp->dev_fd, USCSICMD, &ptp->uscsi)) {
327 ptp->os_err = errno;
328 if ((EIO == ptp->os_err) && ptp->uscsi.uscsi_status) {
329 ptp->os_err = 0;
330 return 0;
331 }
332 if (verbose)
333 fprintf(ferr, "%s: ioctl(USCSICMD) failed with os_err (errno) "
334 "= %d\n", __func__, ptp->os_err);
335 return -ptp->os_err;
336 }
337 return 0;
338 }
339
340 int
get_scsi_pt_result_category(const struct sg_pt_base * vp)341 get_scsi_pt_result_category(const struct sg_pt_base * vp)
342 {
343 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
344 int scsi_st = ptp->uscsi.uscsi_status;
345
346 if (ptp->os_err)
347 return SCSI_PT_RESULT_OS_ERR;
348 else if ((SAM_STAT_CHECK_CONDITION == scsi_st) ||
349 (SAM_STAT_COMMAND_TERMINATED == scsi_st))
350 return SCSI_PT_RESULT_SENSE;
351 else if (scsi_st)
352 return SCSI_PT_RESULT_STATUS;
353 else
354 return SCSI_PT_RESULT_GOOD;
355 }
356
357 uint32_t
get_pt_result(const struct sg_pt_base * vp)358 get_pt_result(const struct sg_pt_base * vp)
359 {
360 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
361
362 return (uint32_t)ptp->uscsi.uscsi_status;
363 }
364
365 int
get_scsi_pt_resid(const struct sg_pt_base * vp)366 get_scsi_pt_resid(const struct sg_pt_base * vp)
367 {
368 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
369
370 return ptp->uscsi.uscsi_resid;
371 }
372
373 void
get_pt_req_lengths(const struct sg_pt_base * vp,int * req_dinp,int * req_doutp)374 get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp,
375 int * req_doutp)
376 {
377 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
378 int dxfer_len = ptp->uscsi.uscsi_buflen;
379 int flags = ptp->uscsi.uscsi_flags;
380
381 if (req_dinp) {
382 if ((dxfer_len > 0) && (USCSI_READ & flags))
383 *req_dinp = dxfer_len;
384 else
385 *req_dinp = 0;
386 }
387 if (req_doutp) {
388 if ((dxfer_len > 0) && (USCSI_WRITE & flags))
389 *req_doutp = dxfer_len;
390 else
391 *req_doutp = 0;
392 }
393 }
394
395 void
get_pt_actual_lengths(const struct sg_pt_base * vp,int * act_dinp,int * act_doutp)396 get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp,
397 int * act_doutp)
398 {
399 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
400 int dxfer_len = ptp->uscsi.uscsi_buflen;
401 int flags = ptp->uscsi.uscsi_flags;
402
403 if (act_dinp) {
404 if ((dxfer_len > 0) && (USCSI_READ & flags))
405 *act_dinp = dxfer_len - ptp->uscsi.uscsi_resid;
406 else
407 *act_dinp = 0;
408 }
409 if (act_doutp) {
410 if ((dxfer_len > 0) && (USCSI_WRITE & flags))
411 *act_doutp = dxfer_len - ptp->uscsi.uscsi_resid;
412 else
413 *act_doutp = 0;
414 }
415 }
416
417 int
get_scsi_pt_status_response(const struct sg_pt_base * vp)418 get_scsi_pt_status_response(const struct sg_pt_base * vp)
419 {
420 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
421
422 return ptp->uscsi.uscsi_status;
423 }
424
425 int
get_scsi_pt_sense_len(const struct sg_pt_base * vp)426 get_scsi_pt_sense_len(const struct sg_pt_base * vp)
427 {
428 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
429 int res;
430
431 if (ptp->max_sense_len > 0) {
432 res = ptp->max_sense_len - ptp->uscsi.uscsi_rqresid;
433 return (res > 0) ? res : 0;
434 }
435 return 0;
436 }
437
438 uint8_t *
get_scsi_pt_sense_buf(const struct sg_pt_base * vp)439 get_scsi_pt_sense_buf(const struct sg_pt_base * vp)
440 {
441 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
442
443 return (uint8_t *)ptp->uscsi.uscsi_rqbuf;
444 }
445
446 int
get_scsi_pt_duration_ms(const struct sg_pt_base * vp)447 get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
448 {
449 // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
450
451 vp = vp; /* ignore and suppress warning */
452 return -1; /* not available */
453 }
454
455 /* If not available return 0 otherwise return number of nanoseconds that the
456 * lower layers (and hardware) took to execute the command just completed. */
457 uint64_t
get_pt_duration_ns(const struct sg_pt_base * vp)458 get_pt_duration_ns(const struct sg_pt_base * vp __attribute__ ((unused)))
459 {
460 return 0;
461 }
462
463 int
get_scsi_pt_transport_err(const struct sg_pt_base * vp)464 get_scsi_pt_transport_err(const struct sg_pt_base * vp)
465 {
466 // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
467
468 if (vp) { ; } /* ignore and suppress warning */
469 return 0;
470 }
471
472 void
set_scsi_pt_transport_err(struct sg_pt_base * vp,int err)473 set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
474 {
475 // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
476
477 if (vp) { ; } /* ignore and suppress warning */
478 if (err) { ; } /* ignore and suppress warning */
479 }
480
481 int
get_scsi_pt_os_err(const struct sg_pt_base * vp)482 get_scsi_pt_os_err(const struct sg_pt_base * vp)
483 {
484 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
485
486 return ptp->os_err;
487 }
488
489 bool
pt_device_is_nvme(const struct sg_pt_base * vp)490 pt_device_is_nvme(const struct sg_pt_base * vp)
491 {
492 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
493
494 return ptp ? ptp->is_nvme : false;
495 }
496
497 char *
get_scsi_pt_transport_err_str(const struct sg_pt_base * vp,int max_b_len,char * b)498 get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
499 char * b)
500 {
501 // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
502
503 vp = vp; /* ignore and suppress warning */
504 if (max_b_len > 0)
505 b[0] = '\0';
506
507 return b;
508 }
509
510 char *
get_scsi_pt_os_err_str(const struct sg_pt_base * vp,int max_b_len,char * b)511 get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
512 {
513 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
514 const char * cp;
515
516 cp = safe_strerror(ptp->os_err);
517 strncpy(b, cp, max_b_len);
518 if ((int)strlen(cp) >= max_b_len)
519 b[max_b_len - 1] = '\0';
520 return b;
521 }
522
523 int
do_nvm_pt(struct sg_pt_base * vp,int submq,int timeout_secs,int verbose)524 do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose)
525 {
526 if (vp) { }
527 if (submq) { }
528 if (timeout_secs) { }
529 if (verbose) { }
530 return SCSI_PT_DO_NOT_SUPPORTED;
531 }
532
533 int
check_pt_file_handle(int device_fd,const char * device_name,int vb)534 check_pt_file_handle(int device_fd, const char * device_name, int vb)
535 {
536 if (device_fd) {}
537 if (device_name) {}
538 if (vb) {}
539 return 0;
540 }
541
542 /* Valid file handles (which is the return value) are >= 0 . Returns -1
543 * if there is no valid file handle. */
544 int
get_pt_file_handle(const struct sg_pt_base * vp)545 get_pt_file_handle(const struct sg_pt_base * vp)
546 {
547 const struct sg_pt_solaris_scsi * ptp = &vp->impl;
548
549 return ptp->dev_fd;
550 }
551
552
553 /* If a NVMe block device (which includes the NSID) handle is associated
554 * with 'vp', then its NSID is returned (values range from 0x1 to
555 * 0xffffffe). Otherwise 0 is returned. */
556 uint32_t
get_pt_nvme_nsid(const struct sg_pt_base * vp)557 get_pt_nvme_nsid(const struct sg_pt_base * vp)
558 {
559 if (vp) { }
560 return 0;
561 }
562
563 /* Forget any previous dev_han and install the one given. May attempt to
564 * find file type (e.g. if pass-though) from OS so there could be an error.
565 * Returns 0 for success or the same value as get_scsi_pt_os_err()
566 * will return. dev_han should be >= 0 for a valid file handle or -1 . */
567 int
set_pt_file_handle(struct sg_pt_base * vp,int dev_han,int vb)568 set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
569 {
570 struct sg_pt_solaris_scsi * ptp = &vp->impl;
571
572 if (vb) {}
573 ptp->dev_fd = (dev_han < 0) ? -1 : dev_han;
574 ptp->in_err = 0;
575 ptp->os_err = 0;
576 ptp->is_nvme = false;
577 return 0;
578 }
579