1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 #include <sys/types.h>
28 #include <sys/scsi/generic/commands.h>
29 #include <sys/scsi/impl/spc3_types.h>
30
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <alloca.h>
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <dlfcn.h>
39
40 #include <scsi/libscsi.h>
41 #include <sys/byteorder.h>
42 #include "libscsi_impl.h"
43
44 int
45 libscsi_assert(const char *expr, const char *file, int line)
46 {
47 char *msg;
48 size_t len;
49
50 len = snprintf(NULL, 0,
51 "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
52
53 msg = alloca(len + 1);
54
55 (void) snprintf(msg, len + 1,
56 "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
57
58 (void) write(STDERR_FILENO, msg, strlen(msg));
59
60 abort();
61 _exit(1);
62
63 /*NOTREACHED*/
64 return (0);
65 }
66
67 int
68 libscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err)
69 {
70 hp->lsh_errno = err;
71 hp->lsh_errmsg[0] = '\0';
72
73 return (-1);
74 }
75
76 /*
77 * Internal routine for setting both _ue_errno and _ue_errmsg. We save
78 * and restore the UNIX errno across this routing so the caller can use either
79 * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value
80 * changing.
81 */
82 int
83 libscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt,
84 va_list ap)
85 {
86 size_t n;
87 char *errmsg;
88
89 /*
90 * To allow the existing error message to itself be used in an error
91 * message, we put the new error message into a buffer on the stack,
92 * and then copy it into lsh_errmsg. We also need to set the errno,
93 * but because the call to libscsi_set_errno() is destructive to
94 * lsh_errmsg, we do this after we print into our temporary buffer
95 * (in case _libscsi_errmsg is part of the error message) and before we
96 * copy the temporary buffer on to _libscsi_errmsg (to prevent our new
97 * message from being nuked by the call to libscsi_set_errno()).
98 */
99 errmsg = alloca(sizeof (hp->lsh_errmsg));
100 (void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap);
101 (void) libscsi_set_errno(hp, err);
102
103 n = strlen(errmsg);
104
105 if (n != 0 && errmsg[n - 1] == '\n')
106 errmsg[n - 1] = '\0';
107
108 bcopy(errmsg, hp->lsh_errmsg, n + 1);
109
110 return (-1);
111 }
112
113 /*PRINTFLIKE3*/
114 int
115 libscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...)
116 {
117 va_list ap;
118
119 if (fmt == NULL)
120 return (libscsi_set_errno(hp, err));
121
122 va_start(ap, fmt);
123 err = libscsi_verror(hp, err, fmt, ap);
124 va_end(ap);
125
126 return (err);
127 }
128
129 libscsi_errno_t
130 libscsi_errno(libscsi_hdl_t *hp)
131 {
132 return (hp->lsh_errno);
133 }
134
135 const char *
136 libscsi_errmsg(libscsi_hdl_t *hp)
137 {
138 if (hp->lsh_errmsg[0] == '\0')
139 (void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno),
140 sizeof (hp->lsh_errmsg));
141
142 return (hp->lsh_errmsg);
143 }
144
145 void *
146 libscsi_alloc(libscsi_hdl_t *hp, size_t size)
147 {
148 void *mem;
149
150 if (size == 0) {
151 (void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH);
152 return (NULL);
153 }
154
155 if ((mem = malloc(size)) == NULL)
156 (void) libscsi_set_errno(hp, ESCSI_NOMEM);
157
158 return (mem);
159 }
160
161 void *
162 libscsi_zalloc(libscsi_hdl_t *hp, size_t size)
163 {
164 void *mem;
165
166 if ((mem = libscsi_alloc(hp, size)) == NULL)
167 return (NULL);
168
169 bzero(mem, size);
170
171 return (mem);
172 }
173
174 char *
175 libscsi_strdup(libscsi_hdl_t *hp, const char *str)
176 {
177 size_t len = strlen(str);
178 char *dup = libscsi_alloc(hp, len + 1);
179
180 if (dup == NULL)
181 return (NULL);
182
183 return (strcpy(dup, str));
184 }
185
186 /*ARGSUSED*/
187 void
188 libscsi_free(libscsi_hdl_t *hp, void *ptr)
189 {
190 free(ptr);
191 }
192
193 libscsi_hdl_t *
194 libscsi_init(uint_t version, libscsi_errno_t *errp)
195 {
196 libscsi_hdl_t *hp;
197
198 if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) {
199 if (errp != NULL)
200 *errp = ESCSI_NOMEM;
201 return (NULL);
202 }
203
204 bzero(hp, sizeof (libscsi_hdl_t));
205 hp->lsh_version = version;
206
207 return (hp);
208 }
209
210 void
211 libscsi_fini(libscsi_hdl_t *hp)
212 {
213 libscsi_engine_impl_t *eip, *neip;
214
215 if (hp == NULL)
216 return;
217
218 ASSERT(hp->lsh_targets == 0);
219
220 for (eip = hp->lsh_engines; eip != NULL; eip = neip) {
221 neip = eip->lsei_next;
222 (void) dlclose(eip->lsei_dl_hdl);
223 libscsi_free(hp, eip);
224 }
225
226 free(hp);
227 }
228
229 size_t
230 libscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd)
231 {
232 size_t sz;
233
234 switch (CDB_GROUPID(cmd)) {
235 case CDB_GROUPID_0:
236 sz = CDB_GROUP0;
237 break;
238 case CDB_GROUPID_1:
239 sz = CDB_GROUP1;
240 break;
241 case CDB_GROUPID_2:
242 sz = CDB_GROUP2;
243 break;
244 case CDB_GROUPID_3:
245 sz = CDB_GROUP3;
246 break;
247 case CDB_GROUPID_4:
248 sz = CDB_GROUP4;
249 break;
250 case CDB_GROUPID_5:
251 sz = CDB_GROUP5;
252 break;
253 case CDB_GROUPID_6:
254 sz = CDB_GROUP6;
255 break;
256 case CDB_GROUPID_7:
257 sz = CDB_GROUP7;
258 break;
259 default:
260 sz = 0;
261 }
262
263 if (sz == 0)
264 (void) libscsi_error(hp, ESCSI_BADCMD,
265 "unknown or unsupported command %u", cmd);
266
267 return (sz);
268 }
269
270 static char *
271 libscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len)
272 {
273 char *buf;
274
275 buf = alloca(len + 1);
276 bcopy(raw, buf, len);
277
278 for (; len > 0; len--) {
279 if (buf[len - 1] != ' ')
280 break;
281 }
282
283 buf[len] = '\0';
284
285 return (libscsi_strdup(hp, buf));
286 }
287
288 /*
289 * As part of basic initialization, we always retrieve the INQUIRY information
290 * to have the vendor/product/revision information available for all consumers.
291 */
292 int
293 libscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp)
294 {
295 libscsi_action_t *ap;
296 spc3_inquiry_cdb_t *cp;
297 spc3_inquiry_data_t data;
298 size_t len;
299
300 if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
301 LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
302 offsetof(spc3_inquiry_data_t, id_vs_36[0]))) == NULL)
303 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
304
305 cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
306
307 SCSI_WRITE16(&cp->ic_allocation_length,
308 offsetof(spc3_inquiry_data_t, id_vs_36[0]));
309
310 if (libscsi_exec(ap, tp) != 0 ||
311 libscsi_action_get_status(ap) != 0) {
312 libscsi_action_free(ap);
313 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
314 }
315
316 (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
317 libscsi_action_free(ap);
318
319 if (len < offsetof(spc3_inquiry_data_t, id_vs_36))
320 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
321
322 if ((tp->lst_vendor = libscsi_process_inquiry_string(hp,
323 data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL ||
324 (tp->lst_product = libscsi_process_inquiry_string(hp,
325 data.id_product_id, sizeof (data.id_product_id))) == NULL ||
326 (tp->lst_revision = libscsi_process_inquiry_string(hp,
327 data.id_product_revision,
328 sizeof (data.id_product_revision))) == NULL) {
329 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
330 }
331
332 return (0);
333 }
334
335 /*
336 * A designation descriptor consists of the header followed by data.
337 * When given a pointer to the header to get to next descriptor we need to add
338 * to hdr pointer the number of data bytes plus size of the header itself.
339 */
340 #define NEXT_DESC(hdr, data_len, hdr_type) ((hdr_type *)((((uint8_t *)hdr) + \
341 data_len + sizeof (hdr_type))))
342
343 int
344 libscsi_get_inquiry_dev_id(libscsi_hdl_t *hp, libscsi_target_t *tp)
345 {
346 libscsi_action_t *ap;
347 spc3_inquiry_cdb_t *cp;
348 spc3_dev_id_vpd_page_impl_t data;
349 size_t len;
350 int des_bytes_left;
351 struct vpd_desc *cur_desc;
352 char lid[17];
353
354 if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
355 LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
356 sizeof (spc3_dev_id_vpd_page_impl_t))) == NULL)
357 return (libscsi_set_errno(hp, ESCSI_NOMEM));
358
359 cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
360 cp->ic_evpd = 1; /* return vital product data for bellow page code */
361 cp->ic_page_code = DEV_ID_VPD_PAGE_CODE;
362 SCSI_WRITE16(&cp->ic_allocation_length,
363 sizeof (spc3_dev_id_vpd_page_impl_t));
364
365 if (libscsi_exec(ap, tp) != 0 ||
366 libscsi_action_get_status(ap) != 0) {
367 libscsi_action_free(ap);
368 return (libscsi_set_errno(hp, ESCSI_IO));
369 }
370
371 (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
372 libscsi_action_free(ap);
373
374 /* make sure we at least got the header */
375 if (len < offsetof(spc3_dev_id_vpd_page_impl_t, divpi_descrs[0]))
376 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
377
378 /* make sure we got the page we asked for */
379 if (data.divpi_hdr.page_code != DEV_ID_VPD_PAGE_CODE)
380 return (libscsi_set_errno(hp, ESCSI_IO));
381
382 /* check for page truncation */
383 len = ((data.divpi_hdr.page_len)[0] << 8 |
384 (data.divpi_hdr.page_len)[1]);
385 if (len > sizeof (data.divpi_descrs))
386 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
387
388 /* get the first descriptor */
389 cur_desc = (struct vpd_desc *)(data.divpi_descrs);
390 /* iterate over descriptors looking for the one we need */
391 des_bytes_left = len;
392 for (; des_bytes_left > sizeof (struct vpd_desc);
393 des_bytes_left -= (sizeof (struct vpd_desc) + cur_desc->len),
394 cur_desc = NEXT_DESC(cur_desc, cur_desc->len, struct vpd_desc)) {
395
396 /*
397 * Len for the NAA IEEE designators is 12 (aka 0x08).
398 * Designator type (id_type) 3 means a NAA formatted
399 * designator.
400 * Code set for the NAA IEEE designators is 1 (binary format).
401 * Association 0 means this designator is for a Logical Unit.
402 * Association 2 means this designator is for a SCSI device
403 * that contains the Logical Unit.
404 * With the ASSOCIATION field set to 0 or 2, device shall
405 * return the same descriptor when it is accessed through any
406 * other I_T nexus. See SPC4 7.8.6.1
407 */
408 if (cur_desc->len == 0x08 && cur_desc->id_type == 0x3 &&
409 cur_desc->code_set == 0x1 &&
410 (cur_desc->association == 0x0 ||
411 cur_desc->association == 0x2)) {
412 /* get to the data - skip the descriptor header */
413 cur_desc = (struct vpd_desc *)(((uint8_t *)cur_desc) +
414 sizeof (struct vpd_desc));
415
416 /*
417 * Bits 7-4 of the NAA formatted designator hold
418 * the designator type. We're only interested
419 * in designator type 0x5 - a 64bit value
420 * (including this type filed) that represents a
421 * NAA IEEE Registered designator that we use as
422 * the LID.
423 * See SPC4 "NAA designator format" section.
424 */
425 if (((*((uint8_t *)cur_desc)) & 0x50) != 0x50) {
426 /*
427 * This is not an IEEE Registered NAA
428 * designator, point cur_desc back to the
429 * header and skip this designator.
430 */
431 cur_desc = (struct vpd_desc * )
432 (((uint8_t *)cur_desc) -
433 sizeof (struct vpd_desc));
434 continue;
435 }
436
437 /* byte swap to have LID match what libses displays */
438 if (snprintf(lid, sizeof (lid), "%llx",
439 BE_IN64(cur_desc)) < 0)
440 return (libscsi_set_errno(hp, ESCSI_UNKNOWN));
441
442 if ((tp->lst_lid = libscsi_process_inquiry_string(hp,
443 lid, sizeof (lid))) == NULL)
444 return (libscsi_set_errno(hp, ESCSI_NOMEM));
445
446 return (0);
447 }
448 }
449
450 return (libscsi_set_errno(hp, ESCSI_NOTSUP));
451 }
452
453 /*
454 * Execute inquiry for VPD page 0x80 (unit serial #) and extract the USN
455 */
456 int
457 libscsi_get_inquiry_usn(libscsi_hdl_t *hp, libscsi_target_t *tp)
458 {
459 libscsi_action_t *ap;
460 spc3_inquiry_cdb_t *cp;
461 spc3_usn_vpd_page_impl_t data;
462 size_t len;
463
464 if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
465 LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
466 sizeof (spc3_usn_vpd_page_impl_t))) == NULL)
467 return (libscsi_set_errno(hp, ESCSI_NOMEM));
468
469 cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
470 cp->ic_evpd = 1; /* return vital product data for bellow page code */
471 cp->ic_page_code = USN_VPD_PAGE_CODE;
472 SCSI_WRITE16(&cp->ic_allocation_length,
473 sizeof (spc3_usn_vpd_page_impl_t));
474
475 if (libscsi_exec(ap, tp) != 0 ||
476 libscsi_action_get_status(ap) != 0) {
477 libscsi_action_free(ap);
478 return (libscsi_set_errno(hp, ESCSI_IO));
479 }
480
481 (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
482 libscsi_action_free(ap);
483
484 /* make sure we at least got the header */
485 if (len < offsetof(spc3_usn_vpd_page_impl_t, uvpi_usn[0]))
486 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
487
488 /* make sure we got the page we asked for */
489 if (data.uvpi_hdr.page_code != USN_VPD_PAGE_CODE)
490 return (libscsi_set_errno(hp, ESCSI_IO));
491
492 /* check for USN truncation */
493 len = ((data.uvpi_hdr.page_len)[0] << 8 | (data.uvpi_hdr.page_len)[1]);
494 if (len == 0 || len > sizeof (data.uvpi_usn))
495 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
496
497 /* USN is ASCII encoded */
498 if ((tp->lst_usn = libscsi_process_inquiry_string(hp,
499 (char *)data.uvpi_usn, len)) == NULL)
500 return (libscsi_set_errno(hp, ESCSI_NOMEM));
501
502 return (0);
503 }
504
505 const char *
506 libscsi_vendor(libscsi_target_t *tp)
507 {
508 return (tp->lst_vendor);
509 }
510
511 const char *
512 libscsi_product(libscsi_target_t *tp)
513 {
514 return (tp->lst_product);
515 }
516
517 const char *
518 libscsi_revision(libscsi_target_t *tp)
519 {
520 return (tp->lst_revision);
521 }
522
523 const char *
524 libscsi_lid(libscsi_target_t *tp)
525 {
526 return (tp->lst_lid);
527 }
528
529 const char *
530 libscsi_usn(libscsi_target_t *tp)
531 {
532 return (tp->lst_usn);
533 }