xref: /illumos-gate/usr/src/common/devid/devid_scsi.c (revision 2850d85b7b93f31e578520dc3b3feb24db609c62)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * These functions are used to encode SCSI INQUIRY data into
31  * Solaris devid / guid values.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/stropts.h>
36 #include <sys/debug.h>
37 #include <sys/isa_defs.h>
38 #include <sys/dditypes.h>
39 #include <sys/ddi_impldefs.h>
40 #include <sys/scsi/scsi.h>
41 #include "devid_impl.h"
42 
43 #define	SCSI_INQUIRY_VID_POS			9
44 #define	SCSI_INQUIRY_VID_SUN			"SUN"
45 #define	SCSI_INQUIRY_VID_SUN_LEN		3
46 #define	SCSI_INQUIRY_VID_HITACHI		"HITACHI"
47 #define	SCSI_INQUIRY_VID_HITACHI_LEN		7
48 #define	SCSI_INQUIRY_PID_HITACHI_OPEN		"OPEN-"
49 #define	SCSI_INQUIRY_PID_HITACHI_OPEN_LEN	5
50 #define	SCSI_INQUIRY_VID_EMC			"EMC     "
51 #define	SCSI_INQUIRY_VID_EMC_LEN		8
52 #define	SCSI_INQUIRY_PID_EMC_SYMMETRIX		"SYMMETRIX       "
53 #define	SCSI_INQUIRY_PID_EMC_SYMMETRIX_LEN	16
54 
55 #define	MSG_NOT_STANDARDS_COMPLIANT "!Page83 data not standards compliant "
56 #define	MSG_NOT_STANDARDS_COMPLIANT_SIZE	( \
57 	sizeof (MSG_NOT_STANDARDS_COMPLIANT) + \
58 	sizeof (((struct scsi_inquiry *)NULL)->inq_vid) + \
59 	sizeof (((struct scsi_inquiry *)NULL)->inq_pid) + \
60 	sizeof (((struct scsi_inquiry *)NULL)->inq_revision) + 4)
61 
62 #define	IS_DEVID_GUID_TYPE(type) ((type == DEVID_SCSI3_WWN)	|| \
63 				(IS_DEVID_SCSI3_VPD_TYPE(type)))
64 
65 #define	IS_DEVID_SCSI_TYPE(type) ((IS_DEVID_GUID_TYPE(type)) || \
66 				(type == DEVID_SCSI_SERIAL))
67 
68 /*
69  * The max inquiry page 83 size as expected in the code today
70  * is 0xf0 bytes. Defining a constant to make it easy incase
71  * this needs to be changed at a later time.
72  */
73 
74 #define	SCMD_MAX_INQUIRY_PAGE83_SIZE			0xFF
75 #define	SCMD_MIN_INQUIRY_PAGE83_SIZE			0x08
76 #define	SCMD_INQUIRY_PAGE83_HDR_SIZE			4
77 #define	SCSI_INQUIRY_PAGE83_EMC_SYMMETRIX_ID_LEN	16
78 
79 #define	SCMD_MAX_INQUIRY_PAGE80_SIZE	0xFF
80 #define	SCMD_MIN_INQUIRY_PAGE80_SIZE	0x04
81 
82 #define	SCMD_MIN_STANDARD_INQUIRY_SIZE	0x04
83 
84 #define	SCMD_INQUIRY_PAGE83_IDENT_DESC_HDR_SIZE		4
85 
86 #define	SCMD_INQUIRY_VPD_TYPE_T10	0x01
87 #define	SCMD_INQUIRY_VPD_TYPE_EUI	0x02
88 #define	SCMD_INQUIRY_VPD_TYPE_NAA	0x03
89 #define	SCMD_INQUIRY_VPD_TYPE_RTP	0x04
90 #define	SCMD_INQUIRY_VPD_TYPE_TPG	0x05
91 #define	SCMD_INQUIRY_VPD_TYPE_LUG	0x06
92 #define	SCMD_INQUIRY_VPD_TYPE_MD5	0x07
93 #define	SCMD_INQUIRY_VPD_TYPE_SSN	0x08
94 
95 static int is_page83_data_valid(uchar_t *inq83, size_t inq83_len);
96 static int is_page80_data_valid(uchar_t *inq80, size_t inq80_len);
97 static int is_initialized_id(uchar_t *id, size_t id_len);
98 
99 static void encode_scsi3_page83(int version, uchar_t *inq83,
100     size_t inq83_len, uchar_t **id, size_t *id_len, ushort_t *id_type);
101 static void encode_scsi3_page83_emc(int version, uchar_t *inq83,
102     size_t inq83_len, uchar_t **id, size_t *id_len, ushort_t *id_type);
103 static void encode_serialnum(int version, uchar_t *inq, uchar_t *inq80,
104     size_t inq80_len, uchar_t **id, size_t *id_len, ushort_t *id_type);
105 static void encode_sun_serialnum(int version, uchar_t *inq,
106     size_t inq_len, uchar_t **id, size_t *id_len, ushort_t *id_type);
107 
108 static int devid_scsi_init(char *driver_name,
109     uchar_t *raw_id, size_t raw_id_len, ushort_t raw_id_type,
110     ddi_devid_t *ret_devid);
111 
112 static char ctoi(char c);
113 
114 /*
115  *    Function: ddi_/devid_scsi_encode
116  *
117  * Description: This routine finds and encodes a unique devid
118  *
119  *   Arguments: version - id encode algorithm version
120  *		driver_name - binding driver name (if ! known use NULL)
121  *		inq - standard inquiry buffer
122  *		inq_len - standard inquiry buffer length
123  *		inq80 - serial number inquiry buffer
124  *		inq80_len - serial number inquiry buffer length
125  *		inq83 - vpd inquiry buffer
126  *		inq83_len - vpd inquiry buffer length
127  *		devid - id returned
128  *
129  * Return Code: DEVID_SUCCESS - success
130  *		DEVID_FAILURE - failure
131  *		DEVID_RETRY - LUN is in a transitional state.  A delay should
132  *		occur and then this inquiry data should be re-acquired and
133  *		this function should be called again.
134  */
135 int
136 #ifdef _KERNEL
137 ddi_devid_scsi_encode(
138 #else /* ! _KERNEL */
139 devid_scsi_encode(
140 #endif /* _KERNEL */
141     int version,	/* IN */
142     char *driver_name,	/* IN */
143     uchar_t *inq,	/* IN */
144     size_t inq_len,	/* IN */
145     uchar_t *inq80,	/* IN */
146     size_t inq80_len,	/* IN */
147     uchar_t *inq83,	/* IN */
148     size_t inq83_len,	/* IN */
149     ddi_devid_t *devid)	/* OUT */
150 {
151 	int			rval		= DEVID_FAILURE;
152 	uchar_t			*id		= NULL;
153 	size_t			id_len		= 0;
154 	ushort_t		id_type		= DEVID_NONE;
155 	struct scsi_inquiry	*inq_std	= (struct scsi_inquiry *)inq;
156 #ifdef	_KERNEL
157 	char			*msg		= NULL;
158 #endif	/* _KERNEL */
159 
160 	DEVID_ASSERT(devid != NULL);
161 
162 	/* verify valid version */
163 	if (version > DEVID_SCSI_ENCODE_VERSION_LATEST) {
164 		return (rval);
165 	}
166 
167 	/* make sure minimum inquiry bytes are available */
168 	if (inq_len < SCMD_MIN_STANDARD_INQUIRY_SIZE) {
169 		return (rval);
170 	}
171 
172 	/*
173 	 * If 0x83 is availible, that is the best choice.  Our next choice is
174 	 * 0x80.  If neither are availible, we leave it to the caller to
175 	 * determine possible alternate ID, although discouraged.  In the
176 	 * case of the target drivers they create a fabricated id which is
177 	 * stored in the acyl.  The HBA drivers should avoid using an
178 	 * alternate id.  Although has already created a hack of using the
179 	 * node wwn in some cases.  Which needs to be carried forward for
180 	 * legacy reasons.
181 	 */
182 	if (inq83 != NULL) {
183 		/*
184 		 * Perform page 83 validation tests and report offenders.
185 		 * We cannot enforce the page 83 specification because
186 		 * many Sun partners (ex. HDS) do not conform to the
187 		 * standards yet.
188 		 */
189 		if (is_page83_data_valid(inq83, inq83_len) ==
190 		    DEVID_RET_INVALID) {
191 			/*
192 			 * invalid page 83 data.  bug 4939576 introduced
193 			 * handling for EMC non-standard data.
194 			 */
195 			if ((bcmp(inq_std->inq_vid, SCSI_INQUIRY_VID_EMC,
196 			    SCSI_INQUIRY_VID_EMC_LEN) == 0) &&
197 			    (bcmp(inq_std->inq_pid,
198 			    SCSI_INQUIRY_PID_EMC_SYMMETRIX,
199 			    SCSI_INQUIRY_PID_EMC_SYMMETRIX_LEN) == 0)) {
200 				encode_scsi3_page83_emc(version, inq83,
201 				    inq83_len, &id, &id_len, &id_type);
202 			}
203 #ifdef	_KERNEL
204 			/*
205 			 * invalid page 83 data. Special hack for HDS
206 			 * specific device, to suppress the warning msg.
207 			 */
208 			if ((bcmp(inq_std->inq_vid, SCSI_INQUIRY_VID_HITACHI,
209 			    SCSI_INQUIRY_VID_HITACHI_LEN) != 0) ||
210 			    (bcmp(inq_std->inq_pid,
211 			    SCSI_INQUIRY_PID_HITACHI_OPEN,
212 			    SCSI_INQUIRY_PID_HITACHI_OPEN_LEN) != 0)) {
213 				/*
214 				 * report the page 0x83 standards violation.
215 				 */
216 				msg = kmem_alloc(
217 				    MSG_NOT_STANDARDS_COMPLIANT_SIZE,
218 				    KM_SLEEP);
219 				(void) strcpy(msg, MSG_NOT_STANDARDS_COMPLIANT);
220 				(void) strncat(msg, inq_std->inq_vid,
221 				    sizeof (inq_std->inq_vid));
222 				(void) strcat(msg, " ");
223 				(void) strncat(msg, inq_std->inq_pid,
224 				    sizeof (inq_std->inq_pid));
225 				(void) strcat(msg, " ");
226 				(void) strncat(msg, inq_std->inq_revision,
227 				    sizeof (inq_std->inq_revision));
228 				(void) strcat(msg, "\n");
229 				cmn_err(CE_WARN, msg);
230 				kmem_free(msg,
231 				    MSG_NOT_STANDARDS_COMPLIANT_SIZE);
232 			}
233 #endif	/* _KERNEL */
234 		}
235 
236 		if (id_type == DEVID_NONE) {
237 			encode_scsi3_page83(version, inq83,
238 			    inq83_len, &id, &id_len, &id_type);
239 		}
240 	}
241 
242 	/*
243 	 * If no vpd page is available at this point then we
244 	 * attempt to use a SCSI serial number from page 0x80.
245 	 */
246 	if ((id_type == DEVID_NONE) &&
247 	    (inq != NULL) &&
248 	    (inq80 != NULL)) {
249 		if (is_page80_data_valid(inq80, inq80_len) == DEVID_RET_VALID) {
250 			encode_serialnum(version, inq, inq80,
251 			    inq80_len, &id, &id_len, &id_type);
252 		}
253 	}
254 
255 	/*
256 	 * If no vpd page  or serial is available at this point and
257 	 * it's a SUN disk it conforms to the disk qual. 850 specifications
258 	 * and we can fabricate a serial number id based on the standard
259 	 * inquiry page.
260 	 */
261 	if ((id_type == DEVID_NONE) &&
262 	    (inq != NULL)) {
263 		encode_sun_serialnum(version, inq, inq_len,
264 		    &id, &id_len, &id_type);
265 	}
266 
267 	if (id_type != DEVID_NONE) {
268 		if (is_initialized_id(id, id_len) == DEVID_RET_VALID) {
269 			rval = devid_scsi_init(driver_name,
270 			    id, id_len, id_type, devid);
271 		} else {
272 			rval = DEVID_RETRY;
273 		}
274 		DEVID_FREE(id, id_len);
275 	}
276 
277 	return (rval);
278 }
279 
280 
281 /*
282  *    Function: is_page83_data_valid
283  *
284  * Description: This routine is used to validate the page 0x83 data
285  *		passed in valid based on the standards specification.
286  *
287  *   Arguments: inq83 -
288  *		inq83_len -
289  *
290  * Return Code: DEVID_RET_VALID
291  *              DEVID_RET_INVALID
292  *
293  */
294 static int
295 is_page83_data_valid(uchar_t *inq83, size_t inq83_len)
296 {
297 
298 	int 	covered_desc_len	= 0;
299 	int	dlen			= 0;
300 	uchar_t	*dblk			= NULL;
301 
302 	DEVID_ASSERT(inq83 != NULL);
303 
304 	/* if not large enough fail */
305 	if (inq83_len < SCMD_MIN_INQUIRY_PAGE83_SIZE)
306 		return (DEVID_RET_INVALID);
307 
308 	/*
309 	 * Ensuring that the Peripheral device type(bits 0 - 4) has
310 	 * the valid settings - the value 0x1f indicates no device type.
311 	 * Only this value can be validated since all other fields are
312 	 * either used or reserved.
313 	 */
314 	if ((inq83[0] & DTYPE_MASK) == DTYPE_UNKNOWN) {
315 		/* failed-peripheral devtype */
316 		return (DEVID_RET_INVALID);
317 	}
318 
319 	/*
320 	 * Ensure that the page length field - third and 4th bytes
321 	 * contain a non zero length value. Our implementation
322 	 * does not seem to expect more that 255 bytes of data...
323 	 * what is to be done if the reported size is > 255 bytes?
324 	 * Yes the device will return only 255 bytes as we provide
325 	 * buffer to house only that much data but the standards
326 	 * prevent the targets from reporting the truncated size
327 	 * in this field.
328 	 *
329 	 * Currently reporting sizes more than 255 as failure.
330 	 *
331 	 */
332 
333 	if ((inq83[2] == 0) && (inq83[3] == 0)) {
334 		/* length field is 0! */
335 		return (DEVID_RET_INVALID);
336 	}
337 	if (inq83[3] > (SCMD_MAX_INQUIRY_PAGE83_SIZE - 3)) {
338 		/* length field exceeds expected size of 255 bytes */
339 		return (DEVID_RET_INVALID);
340 	}
341 
342 	/*
343 	 * Validation of individual descriptor blocks are done in the
344 	 * following while loop. It is possible to have multiple
345 	 * descriptor blocks.
346 	 * the 'dblk' pointer will be pointing to the start of
347 	 * each entry of the descriptor block.
348 	 */
349 	covered_desc_len = 0;
350 	dblk = &inq83[4]; /* start of first decriptor blk */
351 	while (covered_desc_len < inq83[3]) {
352 
353 		/*
354 		 * Ensure that the length field is non zero
355 		 * Further length validations will be done
356 		 * along with the 'identifier type' as some of
357 		 * the lengths are dependent on it.
358 		 */
359 		dlen = dblk[3];
360 		if (dlen == 0) {
361 			/* descr length is 0 */
362 			return (DEVID_RET_INVALID);
363 		}
364 
365 		/*
366 		 * ensure that the size of the descriptor block does
367 		 * not claim to be larger than the entire page83
368 		 * data that has been received.
369 		 */
370 		if ((covered_desc_len + dlen) > inq83[3]) {
371 			/* failed-descr length */
372 			return (DEVID_RET_INVALID);
373 		}
374 
375 		/*
376 		 * The spec says that if the PIV field is 0 OR the
377 		 * association field contains value other than 1 and 2,
378 		 * then the protocol identifier field should be ignored.
379 		 * If association field contains a value of 1 or 2
380 		 * and the PIV field is set, then the protocol identifier
381 		 * field has to be validated.
382 		 * The protocol identifier values 0 - f are either assigned
383 		 * or reserved. Nothing to validate here, hence skipping
384 		 * over to the next check.
385 		 */
386 
387 		/*
388 		 * Check for valid code set values.
389 		 * All possible values are reserved or assigned. Nothing
390 		 * to validate - skipping over.
391 		 */
392 
393 		/*
394 		 * Identifier Type validation
395 		 * All SPC3rev22 identified types and the expected lengths
396 		 * are validated.
397 		 */
398 		switch (dblk[1] & 0x0f) {
399 		case SCMD_INQUIRY_VPD_TYPE_T10: /* T10 vendor Id */
400 			/* No specific length validation required */
401 			break;
402 
403 		case SCMD_INQUIRY_VPD_TYPE_EUI: /* EUI 64 ID */
404 			/* EUI-64: size is expected to be 8, 12, or 16 bytes */
405 			if ((dlen != 8) && (dlen != 12) && (dlen != 16)) {
406 				/* page83 validation failed-EIU64 */
407 				return (DEVID_RET_INVALID);
408 			}
409 			break;
410 
411 		case SCMD_INQUIRY_VPD_TYPE_NAA: /* NAA Id type */
412 
413 			/*
414 			 * the size for this varies -
415 			 * IEEE extended/registered is 8 bytes
416 			 * IEEE Registered extended is 16 bytes
417 			 */
418 			switch (dblk[4] & 0xf0) {
419 
420 				case 0x20: /* IEEE Ext */
421 				case 0x50: /* IEEE Reg */
422 					if (dlen != 8) {
423 						/* failed-IEE E/R len */
424 						return (DEVID_RET_INVALID);
425 					}
426 					/*
427 					 * the codeSet for this MUST
428 					 * be set to 1
429 					 */
430 					if ((dblk[0] & 0x0f) != 1) {
431 						/*
432 						 * failed-IEEE E/R
433 						 * codeSet != 1.
434 						 */
435 						return (DEVID_RET_INVALID);
436 					}
437 				break;
438 
439 				case 0x60: /* IEEE EXT REG */
440 					if (dlen != 16) {
441 						/* failed-IEEE ER len */
442 						return (DEVID_RET_INVALID);
443 					}
444 					/*
445 					 * the codeSet for this MUST
446 					 * be set to 1
447 					 */
448 					if ((dblk[0] & 0x0f) != 1) {
449 						/*
450 						 * failed-IEEE ER
451 						 * codeSet != 1.
452 						 */
453 						return (DEVID_RET_INVALID);
454 						}
455 				break;
456 
457 				default:
458 					/* reserved values */
459 					break;
460 			}
461 			break;
462 
463 		case SCMD_INQUIRY_VPD_TYPE_RTP: /* Relative Target port */
464 			if (dlen != 4) {
465 				/* failed-Rel target Port length */
466 				return (DEVID_RET_INVALID);
467 			}
468 			break;
469 
470 		case SCMD_INQUIRY_VPD_TYPE_TPG: /* Target port group */
471 			if (dlen != 4) {
472 				/* failed-target Port group length */
473 				return (DEVID_RET_INVALID);
474 			}
475 			break;
476 
477 		case SCMD_INQUIRY_VPD_TYPE_LUG: /* Logical unit group */
478 			if (dlen != 4) {
479 				/* failed-Logical Unit group length */
480 				return (DEVID_RET_INVALID);
481 			}
482 			break;
483 
484 		case SCMD_INQUIRY_VPD_TYPE_MD5: /* MD5 unit group */
485 			if (dlen != 16) {
486 				/* failed-MD5 Unit grp */
487 				return (DEVID_RET_INVALID);
488 			}
489 			break;
490 
491 		default:
492 			break;
493 		}
494 
495 		/*
496 		 * Now lets advance to the next descriptor block
497 		 * and validate it.
498 		 * the descriptor block size is <descr Header> + <descr Data>
499 		 * <descr Header> is equal to 4 bytes
500 		 * <descr Data> is available in dlen or dblk[3].
501 		 */
502 		dblk = &dblk[4 + dlen];
503 
504 		/*
505 		 * update the covered_desc_len so that we can ensure that
506 		 * the 'while' loop terminates.
507 		 */
508 		covered_desc_len += (dlen + 4);
509 	}
510 	return (DEVID_RET_VALID);
511 }
512 
513 
514 /*
515  *    Function: is_initialized_id
516  *
517  * Description: Routine to ensure that the ID calculated is not a
518  *		space or zero filled ID. Returning a space / zero
519  *		filled ID when the luns on the target are not fully
520  *		initialized is a valid response from the target as
521  *		per the T10 spec. When a space/zero filled ID is
522  *		found its information needs to be polled again
523  *		after sometime time to see if the luns are fully
524  *		initialized to return a valid guid information.
525  *
526  *   Arguments: id - raw id
527  *              id_len - raw id len
528  *
529  * Return Code:	DEVID_VALID - indicates a non space/zero filled id
530  *		DEVID_INVALID - indicates id contains uninitialized data
531  *		and suggests retry of the collection commands.
532  */
533 static int
534 is_initialized_id(uchar_t *id, size_t id_len)
535 {
536 	int idx;
537 
538 	if ((id == NULL) ||
539 	    (id_len == 0)) {
540 		/* got id length as 0 fetch info again */
541 		return (DEVID_RET_INVALID);
542 	}
543 
544 	/* First lets check if the guid is filled with spaces */
545 	for (idx = 0; idx < id_len; idx++) {
546 		if (id[idx] != ' ') {
547 			break;
548 		}
549 	}
550 
551 	/*
552 	 * Lets exit if we find that it contains ALL spaces
553 	 * saying that it has an uninitialized guid
554 	 */
555 	if (idx >= id_len) {
556 		/* guid filled with spaces found */
557 		return (DEVID_RET_INVALID);
558 	}
559 
560 	/*
561 	 * Since we have found that it is not filled with spaces
562 	 * now lets ensure that the guid is not filled with only
563 	 * zeros.
564 	 */
565 	for (idx = 0; idx < id_len; idx ++) {
566 		if (id[idx] != 0) {
567 			return (DEVID_RET_VALID);
568 		}
569 	}
570 
571 	/* guid filled with zeros found */
572 	return (DEVID_RET_INVALID);
573 }
574 
575 
576 /*
577  *    Function: is_page80_data_valid
578  *
579  * Description: This routine is used to validate the page 0x80 data
580  *		passed in valid based on the standards specification.
581  *
582  *   Arguments: inq80 -
583  *		inq80_len -
584  *
585  * Return Code: DEVID_RET_VALID
586  *              DEVID_RET_INVALID
587  *
588  */
589 /* ARGSUSED */
590 static int
591 is_page80_data_valid(uchar_t *inq80, size_t inq80_len)
592 {
593 	DEVID_ASSERT(inq80);
594 
595 	/* if not large enough fail */
596 	if (inq80_len < SCMD_MIN_INQUIRY_PAGE80_SIZE) {
597 		return (DEVID_RET_INVALID);
598 	}
599 
600 	/*
601 	 * (inq80_len - 4) is the size of the buffer space available
602 	 * for the product serial number.  So inq80[3] (ie. product
603 	 * serial number) should be <= (inq80_len -4).
604 	 */
605 	if (inq80[3] > (inq80_len - 4)) {
606 		return (DEVID_RET_INVALID);
607 	}
608 
609 	return (DEVID_RET_VALID);
610 }
611 
612 
613 /*
614  *    Function: encode_devid_page
615  *
616  * Description: This routine finds the unique devid if available and
617  *		fills the devid and length parameters.
618  *
619  *   Arguments: version - encode version
620  *		inq83 - driver soft state (unit) structure
621  *		inq83_len - length of raw inq83 data
622  *		id - raw id
623  *		id_len - len of raw id
624  *		id_type - type of id
625  *
626  *        Note: DEVID_NONE is returned in the id_type field
627  *		if no supported page 83 id is found.
628  */
629 static void
630 encode_scsi3_page83(int version, uchar_t *inq83, size_t inq83_len,
631     uchar_t **id, size_t *id_len, ushort_t *id_type)
632 {
633 	size_t	descriptor_bytes_left   = 0;
634 	size_t	offset			= 0;
635 	int	idx			= 0;
636 	size_t	offset_id_type[4];
637 
638 	DEVID_ASSERT(inq83 != NULL);
639 	/* inq83 length was already validate in is_page83_valid */
640 	DEVID_ASSERT(id != NULL);
641 	DEVID_ASSERT(id_len != NULL);
642 	DEVID_ASSERT(id_type != NULL);
643 
644 	/* preset defaults */
645 	*id = NULL;
646 	*id_len = 0;
647 	*id_type = DEVID_NONE;
648 
649 	/* verify we have enough memory for a ident header */
650 	if (inq83_len < SCMD_INQUIRY_PAGE83_HDR_SIZE) {
651 		return;
652 	}
653 
654 	/*
655 	 * Attempt to validate the page data.  Once validated, we'll walk
656 	 * the descriptors, looking for certain identifier types that will
657 	 * mark this device with a unique id/wwn.  Note the comment below
658 	 * for what we really want to receive.
659 	 */
660 
661 	/*
662 	 * The format of the inq83 data (Device Identification VPD page) is
663 	 * a header (containing the total length of the page, from which
664 	 * descriptor_bytes_left is calculated), followed by a list of
665 	 * identification descriptors. Each identifcation descriptor has a
666 	 * header which includes the length of the individual identification
667 	 * descriptor).
668 	 *
669 	 * Set the offset to the beginning byte of the first identification
670 	 * descriptor.  We'll index everything from there.
671 	 */
672 	offset = SCMD_INQUIRY_PAGE83_HDR_SIZE;
673 	descriptor_bytes_left = (size_t)((inq83[2] << 8) | inq83[3]);
674 
675 	/*
676 	 * If the raw data states that the data is larger
677 	 * than what is actually received abort encode.
678 	 * Otherwise we will run off into unknown memory
679 	 * on the decode.
680 	 */
681 	if ((descriptor_bytes_left + offset) > inq83_len) {
682 		return;
683 	}
684 
685 
686 	/* Zero out our offset array */
687 	bzero(offset_id_type, sizeof (offset_id_type));
688 
689 	/*
690 	 * According to the scsi spec 8.4.3 SPC-2, there could be several
691 	 * descriptors associated with each lun.  Some we care about and some
692 	 * we don't.  This loop is set up to iterate through the descriptors.
693 	 * We want the 0x03 case which represents an FC-PH, FC-PH3 or FC-FS
694 	 * Name_Identifier.  The spec mentions nothing about ordering, so we
695 	 * don't assume any.
696 	 *
697 	 * We need to check if we've finished walking the list of descriptors,
698 	 * we also perform additional checks to be sure the newly calculated
699 	 * offset is within the bounds of the buffer, and the identifier length
700 	 * (as calculated by the length field in the header) is valid. This is
701 	 * done to protect against devices which return bad page83 data.
702 	 */
703 	while ((descriptor_bytes_left > 0) && (offset_id_type[3] == 0) &&
704 	    (offset + SCMD_INQUIRY_PAGE83_IDENT_DESC_HDR_SIZE <= inq83_len) &&
705 	    (offset + SCMD_INQUIRY_PAGE83_IDENT_DESC_HDR_SIZE +
706 	    (size_t)inq83[offset + 3] <= inq83_len)) {
707 		/*
708 		 * Inspect the Identification descriptor list. Store the
709 		 * offsets in the devid page separately for 0x03, 0x01 and
710 		 * 0x02.  Identifiers 0x00 and 0x04 are not useful as they
711 		 * don't represent unique identifiers for a lun.  We also
712 		 * check the association by masking with 0x3f because we want
713 		 * an association of 0x0 - indicating the identifier field is
714 		 * associated with the addressed physical or logical device
715 		 * and not the port.
716 		 */
717 		switch ((inq83[offset + 1] & 0x3f)) {
718 		case SCMD_INQUIRY_VPD_TYPE_T10:
719 			offset_id_type[SCMD_INQUIRY_VPD_TYPE_T10] = offset;
720 			break;
721 		case SCMD_INQUIRY_VPD_TYPE_EUI:
722 			offset_id_type[SCMD_INQUIRY_VPD_TYPE_EUI] = offset;
723 			break;
724 		case SCMD_INQUIRY_VPD_TYPE_NAA:
725 			offset_id_type[SCMD_INQUIRY_VPD_TYPE_NAA] = offset;
726 			break;
727 		default:
728 			/* Devid page undesired id type */
729 			break;
730 		}
731 		/*
732 		 * Calculate the descriptor bytes left and move to
733 		 * the beginning byte of the next id descriptor.
734 		 */
735 		descriptor_bytes_left -= (size_t)(inq83[offset + 3] +
736 		    SCMD_INQUIRY_PAGE83_IDENT_DESC_HDR_SIZE);
737 		offset += (SCMD_INQUIRY_PAGE83_IDENT_DESC_HDR_SIZE +
738 		    (size_t)inq83[offset + 3]);
739 	}
740 
741 	offset = 0;
742 
743 	/*
744 	 * We can't depend on an order from a device by identifier type, but
745 	 * once we have them, we'll walk them in the same order to prevent a
746 	 * firmware upgrade from breaking our algorithm.  Start with the one
747 	 * we want the most: id_offset_type[3].
748 	 */
749 	for (idx = 3; idx > 0; idx--) {
750 		if (offset_id_type[idx] > 0) {
751 			offset = offset_id_type[idx];
752 			break;
753 		}
754 	}
755 
756 	/*
757 	 * We have a valid Device ID page, set the length of the
758 	 * identifier and copy the value into the wwn.
759 	 */
760 	if (offset > 0) {
761 		*id_len = (size_t)inq83[offset + 3];
762 		if ((*id = DEVID_MALLOC(*id_len)) == NULL) {
763 			*id_len = 0;
764 			return;
765 		}
766 		bcopy(&inq83[offset + SCMD_INQUIRY_PAGE83_IDENT_DESC_HDR_SIZE],
767 		    *id, *id_len);
768 
769 		/* set devid type */
770 		switch (version) {
771 		/* In version 1 all page 83 types were grouped */
772 		case DEVID_SCSI_ENCODE_VERSION1:
773 			*id_type = DEVID_SCSI3_WWN;
774 			break;
775 		/* In version 2 we break page 83 apart to be unique */
776 		case DEVID_SCSI_ENCODE_VERSION2:
777 			switch (idx) {
778 			case 3:
779 				*id_type = DEVID_SCSI3_VPD_NAA;
780 				break;
781 			case 2:
782 				*id_type = DEVID_SCSI3_VPD_EUI;
783 				break;
784 			case 1:
785 				*id_type = DEVID_SCSI3_VPD_T10;
786 				break;
787 			default:
788 				DEVID_FREE(*id, *id_len);
789 				*id_len = 0;
790 				break;
791 			}
792 			break;
793 		default:
794 			DEVID_FREE(*id, *id_len);
795 			*id_len = 0;
796 			break;
797 		}
798 	}
799 }
800 
801 
802 /*
803  *    Function: encode_scsi3_page83_emc
804  *
805  * Description: Routine to handle proprietary page 83 of EMC Symmetrix
806  *              device. Called by ssfcp_handle_page83()
807  *
808  *   Arguments: version - encode version
809  *		inq83 - scsi page 83 buffer
810  *		inq83_len - scsi page 83 buffer size
811  *		id - raw emc id
812  *		id_len - len of raw emc id
813  *		id_type - type of emc id
814  */
815 static void
816 encode_scsi3_page83_emc(int version, uchar_t *inq83,
817     size_t inq83_len, uchar_t **id, size_t *id_len, ushort_t *id_type)
818 {
819 	uchar_t	*guidp	= NULL;
820 
821 	DEVID_ASSERT(inq83 != NULL);
822 	DEVID_ASSERT(id != NULL);
823 	DEVID_ASSERT(id_len != NULL);
824 	DEVID_ASSERT(id_type != NULL);
825 
826 	/* preset defaults */
827 	*id = NULL;
828 	*id_len = 0;
829 	*id_type = DEVID_NONE;
830 
831 	/* The initial devid algorithm didn't use EMC page 83 data */
832 	if (version == DEVID_SCSI_ENCODE_VERSION1) {
833 		return;
834 	}
835 
836 	/* EMC page 83 requires atleast 20 bytes */
837 	if (inq83_len < (SCMD_INQUIRY_PAGE83_HDR_SIZE +
838 	    SCSI_INQUIRY_PAGE83_EMC_SYMMETRIX_ID_LEN)) {
839 		return;
840 	}
841 
842 	/*
843 	 * The 4th byte in the page 83 info returned is most likely
844 	 * indicating the length of the id - which 0x10(16 bytes)
845 	 * and the 5th byte is indicating that the id is of
846 	 * IEEE Registered Extended Name format(6). Validate
847 	 * these code prints before proceeding further as the
848 	 * following proprietary approach is tied to the specific
849 	 * device type and incase the EMC firmware changes, we will
850 	 * have to validate for the changed device before we start
851 	 * supporting such a device.
852 	 */
853 	if ((inq83[3] != 0x10) || (inq83[4] != 0x60)) {
854 		/* unsupported emc symtx device type */
855 		return;
856 	} else {
857 		guidp = &inq83[SCMD_INQUIRY_PAGE83_HDR_SIZE];
858 		/*
859 		 * The GUID returned by the EMC device is
860 		 * in the IEEE Registered Extended Name format(6)
861 		 * as a result it is of 16 bytes in length.
862 		 * An IEEE Registered Name format(5) will be of
863 		 * 8 bytes which is NOT what is being returned
864 		 * by the device type for which we are providing
865 		 * the support.
866 		 */
867 		*id_len = SCSI_INQUIRY_PAGE83_EMC_SYMMETRIX_ID_LEN;
868 		if ((*id = DEVID_MALLOC(*id_len)) == NULL) {
869 			*id_len = 0;
870 			return;
871 		}
872 		bcopy(guidp, *id, *id_len);
873 
874 		/* emc id matches type 3 */
875 		*id_type = DEVID_SCSI3_VPD_NAA;
876 	}
877 }
878 
879 
880 /*
881  *    Function: encode_serialnum
882  *
883  * Description: This routine finds the unique devid from the inquiry page
884  *		0x80, serial number page.  If available and fills the wwn
885  *		and length parameters.
886  *
887  *   Arguments: version - encode version
888  *		inq - standard inquiry data
889  *		inq80 - serial inquiry data
890  *		inq80_len - serial inquiry data len
891  *		id - raw id
892  *		id_len - raw id len
893  *		id_type - raw id type
894  */
895 /* ARGSUSED */
896 static void
897 encode_serialnum(int version, uchar_t *inq, uchar_t *inq80,
898     size_t inq80_len, uchar_t **id, size_t *id_len, ushort_t *id_type)
899 {
900 	struct scsi_inquiry	*inq_std	= (struct scsi_inquiry *)inq;
901 	int			idx		= 0;
902 
903 	DEVID_ASSERT(inq != NULL);
904 	DEVID_ASSERT(inq80 != NULL);
905 	DEVID_ASSERT(id != NULL);
906 	DEVID_ASSERT(id_len != NULL);
907 	DEVID_ASSERT(id_type != NULL);
908 
909 	/* preset defaults */
910 	*id = NULL;
911 	*id_len = 0;
912 	*id_type = DEVID_NONE;
913 
914 	/* verify inq80 buffer is large enough for a header */
915 	if (inq80_len < SCMD_MIN_INQUIRY_PAGE80_SIZE) {
916 		return;
917 	}
918 
919 	/*
920 	 * Attempt to validate the page data.  Once validated, we'll check
921 	 * the serial number.
922 	 */
923 	*id_len = (size_t)inq80[3]; /* Store Product Serial Number length */
924 
925 	/* verify buffer is large enough for serial number */
926 	if (inq80_len < (*id_len + SCMD_MIN_INQUIRY_PAGE80_SIZE)) {
927 		return;
928 	}
929 
930 	/*
931 	 * Device returns ASCII space (20h) in all the bytes of successful data
932 	 * transfer, if the product serial number is not available.  So we end
933 	 * up having to check all the bytes for a space until we reach
934 	 * something else.
935 	 */
936 	for (idx = 0; idx < *id_len; idx++) {
937 		if (inq80[4 + idx] == ' ') {
938 			continue;
939 		}
940 		/*
941 		 * The serial number is valid, but since this is only vendor
942 		 * unique, we'll combine the inquiry vid and pid with the
943 		 * serial number.
944 		 */
945 		*id_len += sizeof (inq_std->inq_vid);
946 		*id_len += sizeof (inq_std->inq_pid);
947 
948 		if ((*id = DEVID_MALLOC(*id_len)) == NULL) {
949 			*id_len = 0;
950 			return;
951 		}
952 
953 		bcopy(&inq_std->inq_vid, *id, sizeof (inq_std->inq_vid));
954 		bcopy(&inq_std->inq_pid, &(*id)[sizeof (inq_std->inq_vid)],
955 		    sizeof (inq_std->inq_pid));
956 		bcopy(&inq80[4], &(*id)[sizeof (inq_std->inq_vid) +
957 		    sizeof (inq_std->inq_pid)], inq80[3]);
958 
959 		*id_type = DEVID_SCSI_SERIAL;
960 		break;
961 	}
962 
963 	/*
964 	 * The spec suggests that the command could succeed but return all
965 	 * spaces if the product serial number is not available.  In this case
966 	 * we need to fail this routine. To accomplish this, we compare our
967 	 * length to the serial number length. If they are the same, then we
968 	 * never copied in the vid and updated the length. That being the case,
969 	 * we must not have found a valid serial number.
970 	 */
971 	if (*id_len == (size_t)inq80[3]) {
972 		/* empty unit serial number */
973 		if (*id != NULL) {
974 			DEVID_FREE(*id, *id_len);
975 		}
976 		*id = NULL;
977 		*id_len = 0;
978 	}
979 }
980 
981 
982 /*
983  *    Function: encode_sun_serialnum
984  *
985  * Description: This routine finds the unique devid from the inquiry page
986  *		0x80, serial number page.  If available and fills the wwn
987  *		and length parameters.
988  *
989  *   Arguments: version - encode version
990  *		inq - standard inquiry data
991  *		inq_len - standard inquiry data len
992  *		id - raw id
993  *		id_len - raw id len
994  *		id_type - raw id type
995  *
996  * Return Code: DEVID_SUCCESS
997  *              DEVID_FAILURE
998  */
999 /* ARGSUSED */
1000 static void
1001 encode_sun_serialnum(int version, uchar_t *inq,
1002     size_t inq_len, uchar_t **id, size_t *id_len, ushort_t *id_type)
1003 {
1004 	struct scsi_inquiry *inq_std = (struct scsi_inquiry *)inq;
1005 
1006 	DEVID_ASSERT(inq != NULL);
1007 	DEVID_ASSERT(id != NULL);
1008 	DEVID_ASSERT(id_len != NULL);
1009 	DEVID_ASSERT(id_type != NULL);
1010 
1011 	/* verify enough buffer is available */
1012 	if (inq_len < SCMD_MIN_STANDARD_INQUIRY_SIZE) {
1013 		return;
1014 	}
1015 
1016 	/* sun qual drive */
1017 	if ((inq_std != NULL) &&
1018 	    (bcmp(&inq_std->inq_pid[SCSI_INQUIRY_VID_POS],
1019 	    SCSI_INQUIRY_VID_SUN, SCSI_INQUIRY_VID_SUN_LEN) == 0)) {
1020 		/*
1021 		 * VPD pages 0x83 and 0x80 are unavailable. This
1022 		 * is a Sun qualified disks as indicated by
1023 		 * "SUN" in bytes 25-27 of the inquiry data
1024 		 * (bytes 9-11 of the pid).  Devid's are created
1025 		 * for Sun qualified disks by combining the
1026 		 * vendor id with the product id with the serial
1027 		 * number located in bytes 36-47 of the inquiry data.
1028 		 */
1029 
1030 		/* get data size */
1031 		*id_len = sizeof (inq_std->inq_vid) +
1032 		    sizeof (inq_std->inq_pid) +
1033 		    sizeof (inq_std->inq_serial);
1034 
1035 		if ((*id = DEVID_MALLOC(*id_len)) == NULL) {
1036 			*id_len = 0;
1037 			return;
1038 		}
1039 
1040 		/* copy the vid at the beginning */
1041 		bcopy(&inq_std->inq_vid, *id,
1042 		    sizeof (inq_std->inq_vid));
1043 			/* copy the pid after the vid */
1044 		bcopy(&inq_std->inq_pid,
1045 		    &(*id)[sizeof (inq_std->inq_vid)],
1046 		    sizeof (inq_std->inq_pid));
1047 			/* copy the serial number after the vid and pid */
1048 		bcopy(&inq_std->inq_serial,
1049 		    &(*id)[sizeof (inq_std->inq_vid) +
1050 		    sizeof (inq_std->inq_pid)],
1051 		    sizeof (inq_std->inq_serial));
1052 			/* devid formed from inquiry data */
1053 		*id_type = DEVID_SCSI_SERIAL;
1054 	}
1055 }
1056 
1057 
1058 /*
1059  *    Function: devid_scsi_init
1060  *
1061  * Description: This routine is used to create a devid for a scsi
1062  *		devid type.
1063  *
1064  *   Arguments: hint - driver soft state (unit) structure
1065  *		raw_id - pass by reference variable to hold wwn
1066  *		raw_id_len - wwn length
1067  *		raw_id_type -
1068  *		ret_devid -
1069  *
1070  * Return Code: DEVID_SUCCESS
1071  *              DEVID_FAILURE
1072  *
1073  */
1074 static int
1075 devid_scsi_init(
1076 	char		*driver_name,
1077 	uchar_t		*raw_id,
1078 	size_t		raw_id_len,
1079 	ushort_t	raw_id_type,
1080 	ddi_devid_t	*ret_devid)
1081 {
1082 	impl_devid_t	*i_devid	= NULL;
1083 	int		i_devid_len	= 0;
1084 	int		driver_name_len	= 0;
1085 	ushort_t	u_raw_id_len	= 0;
1086 
1087 	DEVID_ASSERT(raw_id != NULL);
1088 	DEVID_ASSERT(ret_devid != NULL);
1089 
1090 	if (!IS_DEVID_SCSI_TYPE(raw_id_type)) {
1091 		*ret_devid = NULL;
1092 		return (DEVID_FAILURE);
1093 	}
1094 
1095 	i_devid_len = sizeof (*i_devid) + raw_id_len - sizeof (i_devid->did_id);
1096 	if ((i_devid = DEVID_MALLOC(i_devid_len)) == NULL) {
1097 		*ret_devid = NULL;
1098 		return (DEVID_FAILURE);
1099 	}
1100 
1101 	i_devid->did_magic_hi = DEVID_MAGIC_MSB;
1102 	i_devid->did_magic_lo = DEVID_MAGIC_LSB;
1103 	i_devid->did_rev_hi = DEVID_REV_MSB;
1104 	i_devid->did_rev_lo = DEVID_REV_LSB;
1105 	DEVID_FORMTYPE(i_devid, raw_id_type);
1106 	u_raw_id_len = raw_id_len;
1107 	DEVID_FORMLEN(i_devid, u_raw_id_len);
1108 
1109 	/* Fill in driver name hint */
1110 	if (driver_name != NULL) {
1111 		driver_name_len = strlen(driver_name);
1112 		if (driver_name_len > DEVID_HINT_SIZE) {
1113 			/* Pick up last four characters of driver name */
1114 			driver_name += driver_name_len - DEVID_HINT_SIZE;
1115 			driver_name_len = DEVID_HINT_SIZE;
1116 		}
1117 		bcopy(driver_name, i_devid->did_driver, driver_name_len);
1118 	} else {
1119 		bzero(i_devid->did_driver, DEVID_HINT_SIZE);
1120 	}
1121 
1122 	bcopy(raw_id, i_devid->did_id, raw_id_len);
1123 
1124 	/* return device id */
1125 	*ret_devid = (ddi_devid_t)i_devid;
1126 	return (DEVID_SUCCESS);
1127 }
1128 
1129 
1130 /*
1131  *    Function: devid_to_guid
1132  *
1133  * Description: This routine extracts a guid string form a devid.
1134  *		The common use of this guid is for a HBA driver
1135  *		to pass into mdi_pi_alloc().
1136  *
1137  *   Arguments: devid - devid to extract guid from
1138  *
1139  * Return Code: guid string - success
1140  *		NULL - failure
1141  */
1142 char *
1143 #ifdef  _KERNEL
1144 ddi_devid_to_guid(ddi_devid_t devid)
1145 #else   /* !_KERNEL */
1146 devid_to_guid(ddi_devid_t devid)
1147 #endif  /* _KERNEL */
1148 {
1149 	impl_devid_t	*id	= (impl_devid_t *)devid;
1150 	int		len	= 0;
1151 	int		idx	= 0;
1152 	int		num	= 0;
1153 	char		*guid	= NULL;
1154 	char		*ptr	= NULL;
1155 	char		*dp	= NULL;
1156 
1157 	DEVID_ASSERT(devid != NULL);
1158 
1159 	/* NULL devid -> NULL guid */
1160 	if (devid == NULL)
1161 		return (NULL);
1162 
1163 	if (!IS_DEVID_GUID_TYPE(DEVID_GETTYPE(id)))
1164 		return (NULL);
1165 
1166 	/* guid is always converted to ascii, append NULL */
1167 	len = DEVID_GETLEN(id);
1168 
1169 	/* allocate guid string */
1170 	if ((guid = DEVID_MALLOC((len * 2) + 1)) == NULL)
1171 		return (NULL);
1172 
1173 	/* perform encode of id to hex string */
1174 	ptr = guid;
1175 	for (idx = 0, dp = &id->did_id[0]; idx < len; idx++, dp++) {
1176 		num = ((*dp) >> 4) & 0xF;
1177 		*ptr++ = (num < 10) ? (num + '0') : (num + ('a' - 10));
1178 		num = (*dp) & 0xF;
1179 		*ptr++ = (num < 10) ? (num + '0') : (num + ('a' - 10));
1180 	}
1181 	*ptr = 0;
1182 
1183 	return (guid);
1184 }
1185 
1186 /*
1187  *    Function: devid_free_guid
1188  *
1189  * Description: This routine frees a guid allocated by
1190  *		devid_to_guid().
1191  *
1192  *   Arguments: guid - guid to free
1193  */
1194 void
1195 #ifdef  _KERNEL
1196 ddi_devid_free_guid(char *guid)
1197 #else   /* !_KERNEL */
1198 devid_free_guid(char *guid)
1199 #endif  /* _KERNEL */
1200 {
1201 	if (guid != NULL) {
1202 		DEVID_FREE(guid, strlen(guid) + 1);
1203 	}
1204 }
1205 
1206 static char
1207 ctoi(char c)
1208 {
1209 	if ((c >= '0') && (c <= '9'))
1210 		c -= '0';
1211 	else if ((c >= 'A') && (c <= 'F'))
1212 		c = c - 'A' + 10;
1213 	else if ((c >= 'a') && (c <= 'f'))
1214 		c = c - 'a' + 10;
1215 	else
1216 		c = -1;
1217 	return (c);
1218 }
1219 
1220 /*
1221  *    Function: devid_str_to_wwn
1222  *
1223  * Description: This routine translates wwn from string to uint64 type.
1224  *
1225  *   Arguments: string - the string wwn to be transformed
1226  *              wwn - the pointer to 64 bit wwn
1227  */
1228 int
1229 #ifdef  _KERNEL
1230 ddi_devid_str_to_wwn(const char *string, uint64_t *wwn)
1231 #else   /* !_KERNEL */
1232 devid_str_to_wwn(const char *string, uint64_t *wwn)
1233 #endif  /* _KERNEL */
1234 {
1235 	int i;
1236 	char cl, ch;
1237 	uint64_t tmp;
1238 
1239 	if (wwn == NULL || strlen(string) != 16) {
1240 		return (DDI_FAILURE);
1241 	}
1242 
1243 	*wwn = 0;
1244 	for (i = 0; i < 8; i++) {
1245 		ch = ctoi(*string++);
1246 		cl = ctoi(*string++);
1247 		if (cl == -1 || ch == -1) {
1248 			return (DDI_FAILURE);
1249 		}
1250 		tmp = (ch << 4) + cl;
1251 		*wwn = (*wwn << 8) | tmp;
1252 	}
1253 	return (DDI_SUCCESS);
1254 }
1255