xref: /aosp_15_r20/external/sg3_utils/lib/sg_pt_solaris.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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