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