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