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