xref: /illumos-gate/usr/src/cmd/luxadm/qlgcupdate.c (revision a7e661a2)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * I18N message number ranges
28  *  This file: 21000 - 21499
29  *  Shared common messages: 1 - 1999
30  */
31 
32 
33 
34 /*
35  * Functions to support the download of FCode to PCI HBAs
36  * Qlogic ISP21XX/22XX boards: FC100/P single port, ISP2200 dual port
37  * and Emulex cards
38  */
39 #include <errno.h>
40 #include <ctype.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <strings.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <sys/stat.h>
48 #include <limits.h>
49 #include <signal.h>
50 #include <dirent.h>
51 #include <nl_types.h>
52 #include <utmpx.h>
53 #include <sys/mnttab.h>
54 #include <sys/file.h>
55 #include <sys/mtio.h>
56 #include <sys/scsi/impl/uscsi.h>
57 #include <sys/fibre-channel/fcio.h>
58 #include <stgcom.h>
59 #include <sys/scsi/adapters/ifpio.h>
60 #include <libdevinfo.h>
61 #include "luxadm.h"
62 
63 /* Error codes - used by the fcode_load_file routine */
64 #define	FCODE_SUCCESS	    0	/* successful completion */
65 #define	FCODE_LOAD_FAILURE  1	/* general failure */
66 #define	FCODE_IOCTL_FAILURE 2	/* FCODE ioctl download failure */
67 
68 #define	HBA_MAX	128
69 #define	FCODE_HDR 200
70 #define	MAX_RETRIES	3
71 #define	MAX_WAIT_TIME	30
72 
73 /*
74  * EMULEX Fcode attributes
75  */
76 #define	EMULEX_FCODE_VERSION_LENGTH	16
77 #define	EMULEX_READ_BUFFER_SIZE		128
78 
79 /* Emulex specific error codes */
80 #define	EMLX_ERRNO_START	0x100
81 
82 /* Diagnostic error codes */
83 #define	EMLX_TEST_FAILED	(EMLX_ERRNO_START + 0)
84 
85 /* Download image contains bad data */
86 #define	EMLX_IMAGE_BAD		(EMLX_ERRNO_START + 1)
87 /* Download image not compatible with current hardware */
88 #define	EMLX_IMAGE_INCOMPATIBLE	(EMLX_ERRNO_START + 2)
89 /* Unable to take adapter offline */
90 #define	EMLX_IMAGE_FAILED	(EMLX_ERRNO_START + 3)
91 /* Image download failed */
92 #define	EMLX_OFFLINE_FAILED	(EMLX_ERRNO_START + 4)
93 
94 
95 
96 
97 /*
98  * This is just a random value chosen to identify Sbus Fcodes. Sbus FCode
99  * for Ivory is based on a 2200 chip but this value does not reflect that.
100  */
101 #define	SBUS_CHIP_ID	0x1969
102 #define	IVORY_BUS	"/sbus@"
103 #define	IVORY_DRVR	"/SUNW,qlc@"
104 
105 /*	Global variables	*/
106 static char	fc_trans[] = "SUNW,ifp";	/* fibre channel transport */
107 static char	fp_trans[] = "SUNW,qlc";	/* fca layer driver	   */
108 static char	fp_trans_id[] = "fp@";		/* transport layer id	   */
109 static char	qlgc2100[] = "FC100/P";		/* product name for 2100   */
110 static char	qlgc2200[] = "ISP2200";		/* product name for 2200   */
111 static char	qlgc2300[] = "ISP2300";		/* product name for 2300   */
112 static char	qlgc2312[] = "ISP2312";		/* product name for 2312   */
113 /*
114  * The variable qlgc2200Sbus represents the string which is always the
115  * starting string of the version information in an ISP2200 Sbus Fcode.
116  */
117 static char	qlgc2200Sbus[] = "ISP2200 Sbus FC-AL Host Adapter Driver";
118 static char	pcibus_list[HBA_MAX][PATH_MAX];
119 /*	Internal functions	*/
120 static int	q_load_file(int, char *);
121 static int	q_getbootdev(uchar_t *);
122 static int	q_getdevctlpath(char *, int *);
123 static int	q_warn(int);
124 static int	q_findversion(int, int, uchar_t *, uint16_t *);
125 static int 	q_findfileversion(char *, uchar_t *, uint16_t *, int, int *);
126 static int 	q_findSbusfile(int, int *);
127 static int	memstrstr(char *, char *, int, int);
128 static int	fcode_load_file(int, char *, int *);
129 
130 /*
131  * Functions to support Fcode download for Emulex HBAs
132  */
133 static int	emulex_fcodeversion(di_node_t, uchar_t *);
134 static void	handle_emulex_error(int, char *);
135 
136 /*
137  * Searches for and updates the cards.  This is the "main" function
138  * and will give the output to the user by calling the subfunctions.
139  * args: FCode file; if NULL only the current FCode version is printed
140  */
141 int
142 q_qlgc_update(unsigned int verbose, char *file)
143 /*ARGSUSED*/
144 {
145 	int fd, fcode_fd = -1, errnum = 0, devcnt = 0, retval = 0, isSbus = 0;
146 	int sbus_off;
147 	uint_t i, fflag = 0;
148 	uint16_t chip_id = 0, file_id = 0;
149 	uchar_t fcode_buf[FCODE_HDR];
150 	static uchar_t	bootpath[PATH_MAX];
151 	static uchar_t	version[MAXNAMELEN], version_file[MAXNAMELEN];
152 	char devpath[PATH_MAX], tmppath[PATH_MAX];
153 	void	(*sigint)(); /* to store default SIGTERM setting */
154 	static struct	utmpx *utmpp = NULL; /* pointer for getutxent() */
155 	char *ptr1, *ptr2;
156 	char phys_path[PATH_MAX];
157 	/*
158 	 * The variables port1 and port2 are used to store the bus id
159 	 * e.g. the bus id for this path:
160 	 * /devices/sbus@12,0/SUNW,qlc@2,30000/fp@0,0:devctl
161 	 * is "sbus@12". They are initialized to a random value and are
162 	 * set such that they are not equal initially.
163 	 */
164 	static char port1[MAXNAMELEN] = {NULL};
165 	static char port2[MAXNAMELEN] = {NULL};
166 
167 	if (file) {
168 		fflag++;
169 
170 		/* check for a valid file */
171 		if ((fcode_fd = open(file, O_RDONLY)) < 0) {
172 			(void) fprintf(stderr,
173 			    MSGSTR(21000, "Error: Could not open %s\n"), file);
174 			return (1);
175 		}
176 		if (read(fcode_fd, fcode_buf, FCODE_HDR) != FCODE_HDR) {
177 			perror(MSGSTR(21001, "read"));
178 			(void) close(fcode_fd);
179 			return (1);
180 		}
181 
182 		/*
183 		 * Check if it's SBUS FCode by calling q_findSbusfile
184 		 * if it is then isSbus will be 1, if not it will be 0
185 		 * in case of an error, it will be -1
186 		 */
187 		isSbus = q_findSbusfile(fcode_fd, &sbus_off);
188 		if (isSbus == -1) {
189 			(void) close(fcode_fd);
190 			return (1);
191 		}
192 
193 		/*
194 		 * FCode header check - make sure it's PCI FCode
195 		 * Structure of FCode header (byte# refers to byte numbering
196 		 * in FCode spec, not the byte# of our fcode_buf buffer):
197 		 * header	byte 00    0x55  prom signature byte one
198 		 *		byte 01    0xaa  prom signature byte two
199 		 * data		byte 00-03 P C I R
200 		 * OR
201 		 * header	byte 32    0x55
202 		 *		byte 33    0xaa
203 		 * data		byte 60-63 P C I R
204 		 * The second format with an offset of 32 is used for ifp prom
205 		 */
206 		if (!(((fcode_buf[0x00] == 0x55) &&
207 		    (fcode_buf[0x01] == 0xaa) &&
208 		    (fcode_buf[0x1c] == 'P') &&
209 		    (fcode_buf[0x1d] == 'C') &&
210 		    (fcode_buf[0x1e] == 'I') &&
211 		    (fcode_buf[0x1f] == 'R')) ||
212 
213 		    ((fcode_buf[0x20] == 0x55) &&
214 		    (fcode_buf[0x21] == 0xaa) &&
215 		    (fcode_buf[0x3c] == 'P') &&
216 		    (fcode_buf[0x3d] == 'C') &&
217 		    (fcode_buf[0x3e] == 'I') &&
218 		    (fcode_buf[0x3f] == 'R')) ||
219 
220 		    (isSbus))) {
221 			(void) fprintf(stderr, MSGSTR(21002,
222 			    "Error: %s is not a valid FC100/P, "
223 			    "ISP2200, ISP23xx FCode file.\n"),
224 			    file);
225 			(void) close(fcode_fd);
226 			return (1);
227 		}
228 
229 		/* check for single user mode */
230 		while ((utmpp = getutxent()) != NULL) {
231 			if (strstr(utmpp->ut_line, "run-level") &&
232 			    (strcmp(utmpp->ut_line, "run-level S") &&
233 				strcmp(utmpp->ut_line, "run-level 1"))) {
234 				if (q_warn(1)) {
235 					(void) endutxent();
236 					(void) close(fcode_fd);
237 					return (1);
238 				}
239 				break;
240 			}
241 		}
242 		(void) endutxent();
243 
244 		/* get bootpath */
245 		if (!q_getbootdev((uchar_t *)&bootpath[0]) &&
246 		    getenv("_LUX_D_DEBUG") != NULL) {
247 			(void) fprintf(stdout, "  Bootpath: %s\n", bootpath);
248 		}
249 	}
250 	/*
251 	 * Get count of, and names of PCI slots with ifp device control
252 	 * (devctl) nodes.  Search /devices.
253 	 */
254 	(void) strcpy(devpath, "/devices");
255 	if (q_getdevctlpath(devpath, (int *)&devcnt) == 0) {
256 		(void) fprintf(stdout, MSGSTR(21003,
257 		"\n  Found Path to %d FC100/P, ISP2200, ISP23xx Devices\n"),
258 			devcnt);
259 	} else {
260 		(void) fprintf(stderr, MSGSTR(21004,
261 		"Error: Could not get /devices path to FC100/P,"
262 		"ISP2200, ISP23xx Cards.\n"));
263 		retval++;
264 	}
265 
266 	for (i = 0; i < devcnt; i++) {
267 
268 		(void) strncpy((char *)phys_path, &pcibus_list[i][0],
269 				strlen(&pcibus_list[i][0]));
270 		if (fflag && (strstr((char *)bootpath,
271 		    strtok((char *)phys_path, ":")) != NULL)) {
272 			(void) fprintf(stderr,
273 			    MSGSTR(21005, "Ignoring %s (bootpath)\n"),
274 			    &pcibus_list[i][0]);
275 			continue;
276 		}
277 
278 		(void) fprintf(stdout,
279 		MSGSTR(21006, "\n  Opening Device: %s\n"), &pcibus_list[i][0]);
280 		/* Check if the device is valid */
281 		if ((fd = open(&pcibus_list[i][0], O_RDWR)) < 0) {
282 			(void) fprintf(stderr,
283 			    MSGSTR(21000, "Error: Could not open %s\n"),
284 			    &pcibus_list[i][0]);
285 			retval++;
286 			continue;
287 		}
288 		(void) close(fd);
289 		/*
290 		 * Check FCode version present on the adapter (at last boot)
291 		 */
292 		if (q_findversion(verbose, i, (uchar_t *)&version[0],
293 		    &chip_id) == 0) {
294 			if (strlen((char *)version) == 0) {
295 				(void) fprintf(stdout, MSGSTR(21007,
296 	"  Detected FCode Version:\tNo version available for this FCode\n"));
297 			} else {
298 			(void) fprintf(stdout, MSGSTR(21008,
299 			    "  Detected FCode Version:\t%s\n"), version);
300 			}
301 		} else {
302 			chip_id = 0x0;
303 		}
304 
305 		if (fflag) {
306 			/*
307 			 * For ISP2200, Sbus HBA, do just 1 download
308 			 * for both the ports (dual port HBA)
309 			 * Here it is assumed that readdir() always
310 			 * returns the paths in pcibus_list[] in the
311 			 * sorted order.
312 			 */
313 			(void) strcpy(tmppath, pcibus_list[i]);
314 			if (ptr1 = strstr(tmppath, IVORY_BUS)) {
315 				if (ptr2 = strstr(ptr1, IVORY_DRVR)) {
316 					ptr2 = strchr(ptr2, ',');
317 					if (ptr2 = strchr(++ptr2, ',')) {
318 						*ptr2 = '\0';
319 					}
320 				}
321 				(void) strcpy(port2, ptr1);
322 				if (strcmp(port1, port2) == 0) {
323 				    (void) fprintf(stdout, MSGSTR(21037,
324 				    "/n New FCode has already been downloaded "
325 				    "to this ISP2200 SBus HBA Card.\n"
326 				    "It is sufficient to download to one "
327 				    "port of the ISP2200 SBus HBA Card. "
328 				    "Moving on...\n"));
329 					continue;
330 				}
331 			}
332 			/*
333 			 * Check version of the supplied FCode file (once)
334 			 */
335 			if ((file_id != 0 && version_file != NULL) ||
336 			    (q_findfileversion((char *)
337 			    &fcode_buf[0], (uchar_t *)&version_file[0],
338 			    &file_id, isSbus, &sbus_off) == 0)) {
339 				(void) fprintf(stdout, MSGSTR(21009,
340 				    "  New FCode Version:\t\t%s\n"),
341 				    version_file);
342 			} else {
343 				(void) close(fcode_fd);
344 				return (1);
345 			}
346 
347 			/*
348 			 * Load the New FCode
349 			 * Give warning if file doesn't appear to be correct
350 			 *
351 			 */
352 			if (chip_id == 0) {
353 				errnum = 2; /* can't get chip_id */
354 				retval++;
355 			} else if (chip_id - file_id != 0) {
356 				errnum = 3; /* file/card mismatch */
357 				retval++;
358 			} else {
359 				errnum = 0; /* everything is ok */
360 			}
361 
362 			if (!q_warn(errnum)) {
363 				/* Disable user-interrupt Control-C */
364 				sigint =
365 				    (void (*)(int)) signal(SIGINT, SIG_IGN);
366 
367 				/* Load FCode */
368 				(void) fprintf(stdout, MSGSTR(21010,
369 				    "  Loading FCode: %s\n"), file);
370 
371 				if (q_load_file(fcode_fd,
372 				    &pcibus_list[i][0]) == 0) {
373 					(void) fprintf(stdout, MSGSTR(21011,
374 					"  Successful FCode download: %s\n"),
375 					    &pcibus_list[i][0]);
376 					(void) strcpy(port1, port2);
377 				} else {
378 					(void) fprintf(stderr, MSGSTR(21012,
379 					"Error: FCode download failed: %s\n"),
380 							&pcibus_list[i][0]);
381 					retval++;
382 				}
383 				/* Restore SIGINT (user interrupt) setting */
384 				(void) signal(SIGINT, sigint);
385 			}
386 		}
387 	}
388 	(void) fprintf(stdout, "  ");
389 	(void) fprintf(stdout, MSGSTR(125, "Complete\n"));
390 	if (fcode_fd != -1)
391 		(void) close(fcode_fd);
392 	return (retval);
393 }
394 
395 
396 /*
397  * Retrieve the version banner from the card
398  *    uses ioctl: FCIO_FCODE_MCODE_VERSION  	FCode revision
399  */
400 static int
401 q_findversion(int verbose, int index, uchar_t *version, uint16_t *chip_id)
402 /*ARGSUSED*/
403 {
404 	int fd, ntries;
405 	struct 	ifp_fm_version *version_buffer = NULL;
406 	char	prom_ver[100] = {NULL};
407 	char	mcode_ver[100] = {NULL};
408 	fcio_t	fcio;
409 
410 	if (strstr(&pcibus_list[index][0], fc_trans)) {
411 
412 	if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) {
413 		(void) fprintf(stderr,
414 		    MSGSTR(21000, "Error: Could not open %s\n"),
415 		    &pcibus_list[index][0]);
416 		return (1);
417 	}
418 
419 	if ((version_buffer = (struct ifp_fm_version *)malloc(
420 		sizeof (struct ifp_fm_version))) == NULL) {
421 		(void) fprintf(stderr,
422 		    MSGSTR(21013, "Error: Memory allocation failed\n"));
423 		(void) close(fd);
424 		return (1);
425 	}
426 
427 	version_buffer->fcode_ver = (char *)version;
428 	version_buffer->mcode_ver = mcode_ver;
429 	version_buffer->prom_ver = prom_ver;
430 	version_buffer->fcode_ver_len = MAXNAMELEN - 1;
431 	version_buffer->mcode_ver_len = 100;
432 	version_buffer->prom_ver_len = 100;
433 
434 	if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, version_buffer) < 0) {
435 		(void) fprintf(stderr, MSGSTR(21014,
436 		"Error: Driver interface FCIO_FCODE_MCODE_VERSION failed\n"));
437 		free(version_buffer);
438 		(void) close(fd);
439 		return (1);
440 	}
441 	version[version_buffer->fcode_ver_len] = '\0';
442 
443 	/* Need a way to get card MCODE (firmware) to track certain HW bugs */
444 	if (getenv("_LUX_D_DEBUG") != NULL) {
445 		(void) fprintf(stdout, "  Device %i: QLGC chip_id %x\n",
446 		    index+1, *chip_id);
447 		(void) fprintf(stdout, "  FCode:%s\n  MCODE:%s\n  PROM:%s\n",
448 		    (char *)version, mcode_ver, prom_ver);
449 	}
450 	free(version_buffer);
451 
452 	} else if (strstr(&pcibus_list[index][0], fp_trans)) {
453 		/*
454 		 * Get the fcode and prom's fw version
455 		 * using the fp ioctls. Currently, we pass
456 		 * only the fcode version to the calling function
457 		 * and ignore the FW version (using the existing
458 		 * implementation).
459 		 */
460 
461 		if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) {
462 			(void) fprintf(stderr,
463 			    MSGSTR(4511, "Could not open %s\n"),
464 			    &pcibus_list[index][0]);
465 			(void) close(fd);
466 			return (1);
467 		}
468 		/* Get the fcode version */
469 		bzero(version, sizeof (version));
470 		fcio.fcio_cmd = FCIO_GET_FCODE_REV;
471 		/* Information read operation */
472 		fcio.fcio_xfer = FCIO_XFER_READ;
473 		fcio.fcio_obuf = (caddr_t)version;
474 		fcio.fcio_olen = MAXNAMELEN;
475 
476 		for (ntries = 0; ntries < MAX_RETRIES; ntries++) {
477 			if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
478 				if ((errno == EAGAIN) &&
479 				    (ntries+1 < MAX_RETRIES)) {
480 					/* wait 30 secs */
481 					(void) sleep(MAX_WAIT_TIME);
482 					continue;
483 				}
484 				(void) close(fd);
485 				return (L_FCIO_GET_FCODE_REV_FAIL);
486 			}
487 			break;
488 		}
489 		version[MAXNAMELEN-1] = '\0';
490 	}
491 
492 	/* Get type of card from product name in FCode version banner */
493 	if (strstr((char *)version, qlgc2100)) {
494 		*chip_id = 0x2100;
495 	} else if (strstr((char *)version, qlgc2200)) {
496 		*chip_id = 0x2200;
497 		if (strstr((char *)version, "Sbus")) {
498 			*chip_id = SBUS_CHIP_ID;
499 		}
500 	} else if (strstr((char *)version, qlgc2300)) {
501 		*chip_id = 0x2300;
502 	} else if (strstr((char *)version, qlgc2312)) {
503 		*chip_id = 0x2312;
504 	} else {
505 		*chip_id = 0x0;
506 	}
507 
508 	(void) close(fd);
509 	return (0);
510 }
511 
512 /*
513  * Retrieve the version banner and file type (2100 or 2200) from the file
514  */
515 static int
516 q_findfileversion(char *dl_fcode, uchar_t *version_file, uint16_t *file_id,
517 		    int isSbus, int *sbus_offset)
518 {
519 	int mark;
520 	int qlc_offset = 0;
521 	char temp[4] = {NULL};
522 
523 
524 	/*
525 	 * Get file version from FCode for 2100 or 2202
526 	 */
527 	if (isSbus) {
528 		*file_id = SBUS_CHIP_ID;
529 	} else {
530 		if ((dl_fcode[0x23] == 0x22) ||
531 		    (dl_fcode[0x23] == 0x23)) {
532 			*file_id = dl_fcode[0x22] & 0xff;
533 			*file_id |= (dl_fcode[0x23] << 8) & 0xff00;
534 		} else {
535 			*file_id = dl_fcode[0x42] & 0xff;
536 			*file_id |= (dl_fcode[0x43] << 8) & 0xff00;
537 		}
538 	}
539 
540 	/*
541 	 * Ok, we're just checking for 2200 here. If it is we need
542 	 * to offset to find the banner.
543 	 */
544 	if ((*file_id == 0x2200) ||
545 	    (*file_id == 0x2300) ||
546 	    (*file_id == 0x2312)) {
547 		qlc_offset = -32;
548 	}
549 
550 	/*
551 	 * If this is an ISP2200 Sbus Fcode file, then search for the string
552 	 * "ISP2200 FC-AL Host Adapter Driver" in the whole fcode file
553 	 */
554 	if (isSbus) {
555 		*file_id = SBUS_CHIP_ID;
556 		qlc_offset = *sbus_offset;
557 		/* Subtract 111 from the offset we add below for PCI Fcodes */
558 		qlc_offset -= 111;
559 	}
560 
561 	/* Banner length varies; grab banner to end of date marker yr/mo/da */
562 	version_file[0] = '\0';
563 	for (mark = (111 + qlc_offset); mark < (191 + qlc_offset); mark++) {
564 		(void) strncpy(temp, (char *)&dl_fcode[mark], 4);
565 		if ((strncmp(&temp[0], "/", 1) == 0) &&
566 		    (strncmp(&temp[3], "/", 1) == 0)) {
567 			(void) strncat((char *)version_file,
568 			    (char *)&dl_fcode[mark], 6);
569 			break;
570 		}
571 		(void) strncat((char *)version_file, temp, 1);
572 	}
573 	return (0);
574 }
575 
576 /*
577  * Find if the FCode file is a ISP2200 SBUS Fcode file
578  */
579 static int
580 q_findSbusfile(int fd, int *sbus_offset)
581 {
582 	static int file_size;
583 	char *sbus_info;
584 	struct stat statinfo;
585 
586 	if (lseek(fd, 0, SEEK_SET) == -1) {
587 		perror(MSGSTR(21022, "seek"));
588 		return (-1);
589 	}
590 	if (fstat(fd, &statinfo)) {
591 		perror(MSGSTR(21023, "fstat"));
592 		return (-1);
593 	}
594 	file_size = statinfo.st_size;
595 
596 	if ((sbus_info = (char *)malloc(file_size)) == NULL) {
597 		(void) fprintf(stderr,
598 		    MSGSTR(21013, "Error: Memory allocation failed\n"));
599 		return (-1);
600 	}
601 
602 	if (read(fd, sbus_info, file_size) < 0) {
603 		perror(MSGSTR(21001, "read"));
604 		free(sbus_info);
605 		return (-1);
606 	}
607 
608 	/*
609 	 * Search for the version string in the whole file
610 	 */
611 	if ((*sbus_offset = memstrstr((char *)sbus_info, qlgc2200Sbus,
612 			    file_size, strlen(qlgc2200Sbus))) != -1) {
613 		free(sbus_info);
614 		return (1);
615 	} else {
616 		free(sbus_info);
617 		return (0);
618 	}
619 }
620 
621 
622 /*
623  * Build a list of all the devctl entries for all the 2100/2200 based adapters
624  */
625 static int
626 q_getdevctlpath(char *devpath, int *devcnt)
627 {
628 	struct stat	statbuf;
629 	struct dirent	*dirp = NULL;
630 	DIR		*dp = NULL;
631 	char		*ptr = NULL;
632 	int		err = 0;
633 	int		testopen;
634 
635 	if (lstat(devpath, &statbuf) < 0) {
636 		(void) fprintf(stderr,
637 		    MSGSTR(21016, "Error: %s lstat() error\n"), devpath);
638 		return (1);
639 	}
640 
641 	if ((strstr(devpath, fc_trans) ||
642 	    (strstr(devpath, fp_trans_id) && strstr(devpath, fp_trans))) &&
643 	    strstr(devpath, "devctl")) {
644 		/* Verify the path is valid */
645 		if ((testopen = open(devpath, O_RDONLY)) >= 0) {
646 			(void) close(testopen);
647 			(void) strcpy(pcibus_list[*devcnt], devpath);
648 			*devcnt += 1;
649 			return (0);
650 		}
651 	}
652 
653 	if (S_ISDIR(statbuf.st_mode) == 0) {
654 		/*
655 		 * not a directory so
656 		 * we don't care about it - return
657 		 */
658 		return (0);
659 	}
660 
661 	/*
662 	 * It's a directory. Call ourself to
663 	 * traverse the path(s)
664 	 */
665 	ptr = devpath + strlen(devpath);
666 	*ptr++ = '/';
667 	*ptr = 0;
668 
669 	/* Forget the /devices/pseudo/ directory */
670 	if (strcmp(devpath, "/devices/pseudo/") == 0) {
671 		return (0);
672 	}
673 
674 	if ((dp = opendir(devpath)) == NULL) {
675 		(void) fprintf(stderr,
676 		    MSGSTR(21017, "Error: %s Can't read directory\n"), devpath);
677 		return (1);
678 	}
679 
680 	while ((dirp = readdir(dp)) != NULL) {
681 
682 		if (strcmp(dirp->d_name, ".") == 0 ||
683 		    strcmp(dirp->d_name, "..") == 0) {
684 			continue;
685 		}
686 		(void) strcpy(ptr, dirp->d_name); /* append name */
687 		err = q_getdevctlpath(devpath, devcnt);
688 	}
689 
690 	if (closedir(dp) < 0) {
691 		(void) fprintf(stderr,
692 		MSGSTR(21018, "Error: Can't close directory %s\n"), devpath);
693 		return (1);
694 	}
695 	return (err);
696 }
697 
698 /*
699  * Get the boot device.  Cannot load FCode to current boot device.
700  * Boot devices under volume management will prompt a warning.
701  */
702 static int
703 q_getbootdev(uchar_t *bootpath)
704 {
705 	struct mnttab mp;
706 	struct mnttab mpref;
707 	FILE *fp = NULL;
708 	static char buf[BUFSIZ];
709 	char *p = NULL, *p1 = NULL;  /* p = full device, p1 = chunk to rm */
710 	char *slot = ":devctl";
711 	char *root = "/";
712 
713 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
714 		(void) fprintf(stderr,
715 		    MSGSTR(21000, "Error: Could not open %s\n"), MNTTAB);
716 		return (1);
717 	}
718 
719 	mntnull(&mpref);
720 	mpref.mnt_mountp = (char *)root;
721 
722 	if (getmntany(fp, &mp, &mpref) != 0 ||
723 	    mpref.mnt_mountp == NULL) {
724 		(void) fprintf(stderr, MSGSTR(21019,
725 		    "Error: Cannot get boot device, check %s.\n"), MNTTAB);
726 		(void) fclose(fp);
727 		return (1);
728 	}
729 	(void) fclose(fp);
730 
731 	/*
732 	 * If we can't get a link, we may be dealing with a volume mgr
733 	 * so give a warning.  If a colon is present, we likely have a
734 	 * non-local disk or cd-rom, so no warning is necessary.
735 	 * e.g. /devices/pci@1f,4000/scsi@3/sd@6,0:b (cdrom, no link) or
736 	 * 	storage-e4:/blah/blah remote boot server
737 	 */
738 	if (readlink(mp.mnt_special, buf, BUFSIZ) < 0) {
739 		if (strstr(mp.mnt_special, ":") == NULL) {
740 			(void) fprintf(stderr, MSGSTR(21020,
741 	"\nWarning: Cannot read boot device link, check %s.\n"), MNTTAB);
742 			(void) fprintf(stderr, MSGSTR(21021,
743 	"Do not upgrade FCode on adapters controlling the boot device.\n"));
744 		}
745 		return (1);
746 	}
747 	/*
748 	 * Copy boot device path to bootpath.  First remove leading
749 	 * path junk (../../..) then if it's an ifp device, chop off
750 	 * the disk and add the devctl to the end of the path.
751 	 */
752 	if (p = strstr(buf, "/devices")) {
753 		if (strstr(buf, fc_trans) != NULL) {
754 			p1 = strrchr(p, '/');
755 			*p1 = '\0';
756 		}
757 	}
758 	(void) strcpy((char *)bootpath, (char *)p);
759 	if (p1) {
760 		(void) strcat((char *)bootpath, slot);
761 	}
762 	return (0);
763 }
764 
765 /*
766  * Load FCode to card.
767  *    uses ioctl: IFPIO_FCODE_DOWNLOAD
768  */
769 static int
770 q_load_file(int fcode_fd, char *device)
771 {
772 	static int	dev_fd, fcode_size;
773 	struct stat	stat;
774 	ifp_download_t	*download_p = NULL;
775 	fcio_t		fcio;
776 	uint16_t	file_id = 0;
777 	uchar_t		*bin;
778 
779 	if (lseek(fcode_fd, 0, SEEK_SET) == -1) {
780 		perror(MSGSTR(21022, "seek"));
781 		(void) close(fcode_fd);
782 		return (1);
783 	}
784 	if (fstat(fcode_fd, &stat) == -1) {
785 		perror(MSGSTR(21023, "fstat"));
786 		(void) close(fcode_fd);
787 		return (1);
788 	}
789 
790 	fcode_size = stat.st_size;
791 
792 	if (strstr(device, fc_trans)) {
793 		if ((download_p = (ifp_download_t *)malloc(
794 			sizeof (ifp_download_t) + fcode_size)) == NULL) {
795 			(void) fprintf(stderr,
796 			    MSGSTR(21013, "Error: Memory allocation failed\n"));
797 			(void) close(fcode_fd);
798 			return (1);
799 		}
800 	} else {
801 		if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) {
802 			(void) fprintf(stderr,
803 			    MSGSTR(21013, "Error: Memory allocation failed\n"));
804 			(void) close(fcode_fd);
805 			return (1);
806 		}
807 	}
808 
809 	if (strstr(device, fc_trans)) {
810 		if (read(fcode_fd, download_p->dl_fcode, fcode_size)
811 		    != fcode_size) {
812 			perror(MSGSTR(21001, "read"));
813 			free(download_p);
814 			(void) close(fcode_fd);
815 			return (1);
816 		}
817 	} else {
818 		if (read(fcode_fd, bin, fcode_size)
819 		    != fcode_size) {
820 			perror(MSGSTR(21001, "read"));
821 			free(bin);
822 			(void) close(fcode_fd);
823 			return (1);
824 		}
825 	}
826 
827 
828 	if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) {
829 		(void) fprintf(stderr,
830 		    MSGSTR(21000, "Error: Could not open %s\n"), device);
831 		free(download_p);
832 		return (1);
833 	}
834 	if (strstr(device, fc_trans)) {
835 		download_p->dl_fcode_len = fcode_size;
836 		file_id = download_p->dl_fcode[0x42] & 0xff;
837 		file_id |= (download_p->dl_fcode[0x43] << 8) & 0xff00;
838 		download_p->dl_chip_id = file_id;
839 		if (ioctl(dev_fd, IFPIO_FCODE_DOWNLOAD, download_p) < 0) {
840 			(void) fprintf(stderr, MSGSTR(21024,
841 		    "Error: Driver interface IFPIO_FCODE_DOWNLOAD failed\n"));
842 			free(download_p);
843 			(void) close(dev_fd);
844 			return (1);
845 		}
846 		free(download_p);
847 	} else if (strstr(device, fp_trans)) {
848 		fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE;
849 		/* Information read operation */
850 		fcio.fcio_xfer = FCIO_XFER_WRITE;
851 		fcio.fcio_ibuf = (caddr_t)bin;
852 		fcio.fcio_ilen = fcode_size;
853 
854 		if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) {
855 			(void) fprintf(stderr, MSGSTR(21036,
856 		    "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n"));
857 			free(download_p);
858 			(void) close(dev_fd);
859 			return (1);
860 		}
861 		free(bin);
862 	}
863 	(void) close(dev_fd);
864 	return (0);
865 }
866 
867 /*
868  * Issue warning strings and loop for Yes/No user interaction
869  *    err# 0 -- we're ok, warn for pending FCode load
870  *         1 -- not in single user mode
871  *         2 -- can't get chip_id
872  *         3 -- card and file do not have same type (2100/2200)
873  */
874 static int
875 q_warn(int errnum)
876 {
877 	char input[1024];
878 	input[0] = '\0';
879 
880 	if (errnum == 1) {
881 		(void) fprintf(stderr, MSGSTR(21025,
882 		    "\nWarning: System is not in single-user mode.\n"));
883 		(void) fprintf(stderr, MSGSTR(21026,
884 	"Loading FCode will reset the adapter and terminate I/O activity\n"));
885 	} else {
886 		if (errnum == 2) {
887 			(void) fprintf(stderr, MSGSTR(21027,
888 			"  Warning: FCode is missing or existing FCode has"
889 			" unrecognized version.\n"));
890 			return (1);
891 		} else if (errnum == 3) {
892 			(void) fprintf(stderr, MSGSTR(21028,
893 			"  Warning: New FCode file version does not match this"
894 			" board type. Skipping...\n"));
895 			return (1);
896 		}
897 		(void) fprintf(stderr, MSGSTR(21029,
898 		"\nWARNING!! This program will update the FCode in this"
899 		" FC100/PCI, ISP2200/PCI, ISP23xx/PCI "
900 		" and Emulex devices.\n"));
901 		(void) fprintf(stderr, MSGSTR(21030,
902 		"This may take a few (5) minutes. Please be patient.\n"));
903 	}
904 
905 loop1:
906 	(void) fprintf(stderr, MSGSTR(21031,
907 		"Do you wish to continue ? (y/n) "));
908 
909 	(void) gets(input);
910 
911 	if ((strcmp(input, MSGSTR(21032, "y")) == 0) ||
912 			(strcmp(input, MSGSTR(40, "yes")) == 0)) {
913 		return (0);
914 	} else if ((strcmp(input, MSGSTR(21033, "n")) == 0) ||
915 			(strcmp(input, MSGSTR(45, "no")) == 0)) {
916 		(void) fprintf(stderr,
917 		    MSGSTR(21034, "Not Downloading FCode\n"));
918 		return (1);
919 	} else {
920 		(void) fprintf(stderr, MSGSTR(21035, "Invalid input\n"));
921 		goto loop1;
922 	}
923 }
924 
925 /*
926  * Name    : memstrstr
927  * Input   : pointer to buf1, pointer to buf2, size of buf1, size of buf2
928  * Returns :
929  *      Offset of the start of contents-of-buf2 in buf1 if it is found
930  *      -1 if buf1 does not contain contents of buf2
931  * Synopsis:
932  * This function works similar to strstr(). The difference is that null
933  * characters in the buffer are treated like any other character. So, buf1
934  * and buf2 can have embedded null characters in them.
935  */
936 static int
937 memstrstr(char *s1, char *s2, int size1, int size2)
938 {
939 	int count1, count2;
940 	char *s1_ptr, *s2_ptr;
941 
942 	count1 = size1; count2 = size2;
943 	s1_ptr = s1; s2_ptr = s2;
944 
945 	if ((size2 == 0)||(size1 == 0))
946 		return (-1);
947 
948 	for (count1 = 0; count1 < (size1 - size2 + 1); count1++) {
949 		if (*s1_ptr++ == *s2_ptr++) {
950 			if (--count2 == 0) {
951 				return (count1 - size2 + 1);
952 			}
953 			continue;
954 		}
955 		count2 = size2;
956 		s2_ptr = s2;
957 	}
958 
959 	return (-1);
960 }
961 
962 /*
963  * generic fcode load file routine.  given a file descriptor to a fcode file
964  * this routine will issue the FCIO_DOWNLOAD_FCODE ioctl to the given
965  * device.  Any ioctl errors will be returned in fcio_errno
966  *
967  * Arguments:
968  *	fcode_fd    file descriptor to a fcode file
969  *	device	    path to the device we will be downloading the fcode onto
970  *	fcio_errno  pointer to an int that will be used to return any errors
971  *			back to the caller
972  * Retrurn Values:
973  *	0	    successful download
974  *	>0	    otherwise
975  */
976 static int
977 fcode_load_file(int fcode_fd, char *device, int *fcio_errno)
978 {
979 
980 	fcio_t		fcio;
981 	static int	dev_fd, fcode_size;
982 	uchar_t		*bin;
983 	struct stat	stat;
984 
985 	if (device == NULL || fcio_errno == NULL) {
986 		return (FCODE_LOAD_FAILURE);
987 	}
988 
989 	*fcio_errno = 0;
990 	if (lseek(fcode_fd, 0, SEEK_SET) == -1) {
991 		perror(MSGSTR(21022, "seek"));
992 		return (FCODE_LOAD_FAILURE);
993 	}
994 
995 	if (fstat(fcode_fd, &stat) == -1) {
996 		perror(MSGSTR(21023, "fstat"));
997 		return (FCODE_LOAD_FAILURE);
998 	}
999 
1000 	fcode_size = stat.st_size;
1001 
1002 	if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) {
1003 		(void) fprintf(stderr,
1004 		    MSGSTR(21013, "Error: Memory allocation failed\n"));
1005 		return (FCODE_LOAD_FAILURE);
1006 	}
1007 
1008 	if (read(fcode_fd, bin, fcode_size)
1009 	    != fcode_size) {
1010 		perror(MSGSTR(21001, "read"));
1011 		free(bin);
1012 		return (FCODE_LOAD_FAILURE);
1013 	}
1014 
1015 	if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) {
1016 		(void) fprintf(stderr,
1017 		    MSGSTR(21122, "Error: Could not open %s, failed "
1018 			    "with errno %d\n"), device, errno);
1019 		free(bin);
1020 		return (FCODE_LOAD_FAILURE);
1021 	}
1022 
1023 	fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE;
1024 	fcio.fcio_xfer = FCIO_XFER_WRITE;
1025 	fcio.fcio_ibuf = (caddr_t)bin;
1026 	fcio.fcio_ilen = fcode_size;
1027 
1028 	if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) {
1029 		(void) close(dev_fd);
1030 		*fcio_errno = fcio.fcio_errno;
1031 		free(bin);
1032 		return (FCODE_IOCTL_FAILURE);
1033 	}
1034 
1035 	free(bin);
1036 	(void) close(dev_fd);
1037 	return (FCODE_SUCCESS);
1038 }
1039 
1040 /*
1041  * Searches for and updates the fcode for Emulex HBA cards
1042  * args: FCode file; if NULL only the current FCode
1043  * version is printed
1044  */
1045 
1046 int
1047 emulex_update(char *file)
1048 {
1049 
1050 	int 		fd, retval = 0;
1051 	int 		devcnt = 0;
1052 	uint_t 		state = 0, fflag = 0;
1053 	static uchar_t  bootpath[PATH_MAX];
1054 	int 		fcode_fd = -1;
1055 	static struct	utmpx *utmpp = NULL;
1056 	di_node_t 	root;
1057 	di_node_t 	node, sib_node, count_node;
1058 	di_minor_t 	minor_node;
1059 	char 		phys_path[PATH_MAX], *path;
1060 	int 		errnum = 0, fcio_errno = 0;
1061 	static uchar_t	prom_ver_data[MAXNAMELEN];
1062 	static char	ver_file[EMULEX_FCODE_VERSION_LENGTH];
1063 	void		(*sigint)();
1064 	int		prop_entries = -1;
1065 	int 		*port_data = NULL;
1066 
1067 	if (file) {
1068 		/* set the fcode download flag */
1069 		fflag++;
1070 
1071 		/* check for a valid file */
1072 		if ((fcode_fd = open(file, O_RDONLY)) < 0) {
1073 			(void) fprintf(stderr,
1074 			    MSGSTR(21118, "Error: Could not open %s, failed "
1075 				    "with errno %d\n"), file, errno);
1076 			return (1);
1077 		}
1078 
1079 		/* check for single user mode */
1080 		while ((utmpp = getutxent()) != NULL) {
1081 			if (strstr(utmpp->ut_line, "run-level") &&
1082 				(strcmp(utmpp->ut_line, "run-level S") &&
1083 				strcmp(utmpp->ut_line, "run-level 1"))) {
1084 				if (q_warn(1)) {
1085 					(void) endutxent();
1086 					(void) close(fcode_fd);
1087 					return (1);
1088 				}
1089 				break;
1090 			}
1091 		}
1092 		(void) endutxent();
1093 
1094 		/* get bootpath */
1095 		if (!q_getbootdev((uchar_t *)&bootpath[0]) &&
1096 		    getenv("_LUX_D_DEBUG") != NULL) {
1097 			(void) fprintf(stdout, "  Bootpath: %s\n", bootpath);
1098 		}
1099 	}
1100 
1101 	/*
1102 	 * Download the Fcode to all the emulex cards found
1103 	 */
1104 
1105 	/* Create a snapshot of the kernel device tree */
1106 	if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
1107 		(void) fprintf(stderr, MSGSTR(21114,
1108 		"Error: Could not get /devices path to "
1109 		"Emulex Devices.\n"));
1110 		retval++;
1111 	}
1112 
1113 	/* point to first node which matches emulex driver */
1114 	node = di_drv_first_node("emlxs", root);
1115 
1116 	if (node == DI_NODE_NIL) {
1117 		/*
1118 		 * Could not find any emulex cards
1119 		 */
1120 		(void) di_fini(root);
1121 		(void) fprintf(stderr, MSGSTR(21115,
1122 		"\n  Found Path to %d Emulex Devices.\n"), devcnt);
1123 		retval++;
1124 	} else {
1125 
1126 		count_node = node;
1127 		while (count_node != DI_NODE_NIL) {
1128 			state = di_state(count_node);
1129 			if ((state & DI_DRIVER_DETACHED)
1130 			    != DI_DRIVER_DETACHED) {
1131 				devcnt++;
1132 			}
1133 			count_node = di_drv_next_node(count_node);
1134 		}
1135 		(void) fprintf(stdout, MSGSTR(21116,
1136 		"\n  Found Path to %d Emulex Devices.\n"), devcnt);
1137 	}
1138 
1139 
1140 	/*
1141 	 * Traverse device tree to find all emulex cards
1142 	 */
1143 	while (node != DI_NODE_NIL) {
1144 
1145 		state = di_state(node);
1146 		if ((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) {
1147 			node = di_drv_next_node(node);
1148 			continue;
1149 		}
1150 
1151 		sib_node = di_child_node(node);
1152 		while (sib_node != DI_NODE_NIL) {
1153 
1154 			state = di_state(sib_node);
1155 			if ((state & DI_DRIVER_DETACHED) !=
1156 			    DI_DRIVER_DETACHED) {
1157 
1158 				/* Found an attached node */
1159 				prop_entries = di_prop_lookup_ints(
1160 				    DDI_DEV_T_ANY, sib_node,
1161 				    "port", &port_data);
1162 				if (prop_entries != -1) {
1163 
1164 					/* Found a node with "port" property */
1165 					minor_node = di_minor_next(sib_node,
1166 					    DI_MINOR_NIL);
1167 					break;
1168 				}
1169 			}
1170 			sib_node = di_sibling_node(sib_node);
1171 		}
1172 
1173 		if (sib_node == DI_NODE_NIL) {
1174 			return (1);
1175 		}
1176 		path = di_devfs_path(sib_node);
1177 		(void) strcpy(phys_path, "/devices");
1178 		(void) strncat(phys_path, path, strlen(path));
1179 		di_devfs_path_free(path);
1180 
1181 		if (fflag && (strstr((char *)bootpath,
1182 		    (char *)phys_path) != NULL)) {
1183 			(void) fprintf(stderr,
1184 			    MSGSTR(21117, "Ignoring %s (bootpath)\n"),
1185 			    phys_path);
1186 			node = di_drv_next_node(node);
1187 			continue;
1188 		}
1189 
1190 		if (minor_node) {
1191 			(void) strncat(phys_path, ":", 1);
1192 			(void) strncat(phys_path,
1193 				di_minor_name(minor_node),
1194 				strlen(di_minor_name(minor_node)));
1195 		}
1196 
1197 		(void) fprintf(stdout,
1198 				MSGSTR(21107, "\n  Opening Device: %s\n"),
1199 				phys_path);
1200 
1201 		/* Check if the device is valid */
1202 		if ((fd = open(phys_path, O_RDWR)) < 0) {
1203 			(void) fprintf(stderr,
1204 			    MSGSTR(21121, "Error: Could not open %s, failed "
1205 				    "with errno %d\n"), phys_path, errno);
1206 			retval++;
1207 			node = di_drv_next_node(node);
1208 			continue;
1209 		}
1210 
1211 		(void) close(fd);
1212 
1213 		/*
1214 		 * Check FCode version present on the adapter
1215 		 * (at last boot)
1216 		 */
1217 		memset(prom_ver_data, 0, sizeof (prom_ver_data));
1218 		if (emulex_fcodeversion(node, (uchar_t *)&prom_ver_data[0])
1219 		    == 0) {
1220 			errnum = 0;
1221 			if (strlen((char *)prom_ver_data) == 0) {
1222 				(void) fprintf(stdout, MSGSTR(21108,
1223 	"  Detected FCode Version:\tNo version available for this FCode\n"));
1224 			} else {
1225 				(void) fprintf(stdout, MSGSTR(21109,
1226 				    "  Detected FCode Version:\t%s\n"),
1227 				    prom_ver_data);
1228 			}
1229 		} else {
1230 			errnum = 2; /* can't get prom properties */
1231 			retval++;
1232 		}
1233 
1234 		if (fflag) {
1235 
1236 			memset(ver_file, 0, sizeof (ver_file));
1237 			if (emulex_fcode_reader(fcode_fd, "fcode-version",
1238 				    ver_file, sizeof (ver_file)) == 0) {
1239 				(void) fprintf(stdout, MSGSTR(21110,
1240 					    "  New FCode Version:\t\t%s\n"),
1241 					    ver_file);
1242 			} else {
1243 				di_fini(root);
1244 				(void) close(fcode_fd);
1245 				return (1);
1246 			}
1247 
1248 			/*
1249 			 * Load the New FCode
1250 			 * Give warning if file doesn't appear to be correct
1251 			 */
1252 			if (!q_warn(errnum)) {
1253 				/* Disable user-interrupt Control-C */
1254 				sigint =
1255 				    (void (*)(int)) signal(SIGINT, SIG_IGN);
1256 				/* Load FCode */
1257 				(void) fprintf(stdout, MSGSTR(21111,
1258 					"  Loading FCode: %s\n"), file);
1259 				if (fcode_load_file(fcode_fd, phys_path,
1260 					    &fcio_errno) == FCODE_SUCCESS) {
1261 					(void) fprintf(stdout, MSGSTR(21112,
1262 					"  Successful FCode download: %s\n"),
1263 					phys_path);
1264 				} else {
1265 					handle_emulex_error(fcio_errno,
1266 					    phys_path);
1267 					retval++;
1268 				}
1269 
1270 				/* Restore SIGINT (user interrupt) setting */
1271 				(void) signal(SIGINT, sigint);
1272 			}
1273 		}
1274 
1275 		node = di_drv_next_node(node);
1276 	}
1277 
1278 	di_fini(root);
1279 	(void) fprintf(stdout, "  ");
1280 	(void) fprintf(stdout, MSGSTR(125, "Complete\n"));
1281 	if (fcode_fd != -1)
1282 		(void) close(fcode_fd);
1283 	return (retval);
1284 
1285 }
1286 
1287 /*
1288  * Retrieve the version from the card.
1289  *    uses PROM properties
1290  */
1291 static int
1292 emulex_fcodeversion(di_node_t node, uchar_t *ver) {
1293 	di_prom_prop_t	    promprop;
1294 	di_prom_handle_t    ph;
1295 	char		    *promname;
1296 	uchar_t		    *ver_data = NULL;
1297 	int		    size, found = 0;
1298 
1299 	/* check to make sure ver is not NULL */
1300 	if (ver == NULL) {
1301 		return (1);
1302 	}
1303 
1304 	if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) {
1305 		return (1);
1306 	}
1307 
1308 	for (promprop = di_prom_prop_next(ph, node,
1309 		DI_PROM_PROP_NIL);
1310 		promprop != DI_PROM_PROP_NIL;
1311 		promprop = di_prom_prop_next(ph, node, promprop)) {
1312 		if (((promname = di_prom_prop_name(
1313 			promprop)) != NULL) &&
1314 			(strcmp(promname, "fcode-version") == 0)) {
1315 			size = di_prom_prop_data(promprop, &ver_data);
1316 			(void) memset(ver, NULL, size);
1317 			(void) memcpy(ver, ver_data, size);
1318 			found = 1;
1319 		}
1320 	}
1321 
1322 	if (found) {
1323 		return (0);
1324 	} else {
1325 		return (1);
1326 	}
1327 }
1328 
1329 /*
1330  * Retrieves information from the Emulex fcode
1331  *
1332  * Given a pattern, this routine will look for this pattern in the fcode
1333  * file and if found will return the pattern value
1334  *
1335  * possible patterns are manufacturer and fcode-version
1336  */
1337 int
1338 emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value,
1339     uint32_t pattern_value_size) {
1340 	int32_t i = 0;
1341 	uint32_t n = 0;
1342 	uint32_t b = 0;
1343 	char byte1;
1344 	char byte2;
1345 	char byte3;
1346 	char byte4;
1347 	char buffer1[EMULEX_READ_BUFFER_SIZE];
1348 	char buffer2[EMULEX_READ_BUFFER_SIZE];
1349 	uint32_t plen, image_size;
1350 	struct stat	stat;
1351 	uchar_t		*image;
1352 
1353 	/* Check the arguments */
1354 	if (!fcode_fd || !pattern_value || pattern_value_size < 8) {
1355 		return (1);
1356 	}
1357 
1358 	if (fstat(fcode_fd, &stat) == -1) {
1359 		perror(MSGSTR(21023, "fstat"));
1360 		return (1);
1361 	}
1362 	image_size = stat.st_size;
1363 	if (image_size < 2) {
1364 		return (1);
1365 	}
1366 	if ((image = (uchar_t *)calloc(image_size, 1)) == NULL) {
1367 		(void) fprintf(stderr,
1368 		    MSGSTR(21013, "Error: Memory allocation failed\n"));
1369 		return (1);
1370 	}
1371 
1372 	/* Read the fcode image file */
1373 	lseek(fcode_fd, 0, SEEK_SET);
1374 	read(fcode_fd, image, image_size);
1375 
1376 	/* Initialize */
1377 	bzero(buffer1, sizeof (buffer1));
1378 	bzero(buffer2, sizeof (buffer2));
1379 	/* Default pattern_value string */
1380 	strcpy((char *)pattern_value, "<unknown>");
1381 	plen = strlen(pattern);
1382 	n = 0;
1383 	b = 0;
1384 	i = 0;
1385 
1386 	/* Search entire image for pattern string */
1387 	while (i <= (image_size - 2)) {
1388 		/* Read next two bytes */
1389 		byte1 = image[i++];
1390 		byte2 = image[i++];
1391 
1392 		/* Check second byte first due to endianness */
1393 
1394 		/* Save byte in circular buffer */
1395 		buffer1[b++] = byte2;
1396 		if (b == sizeof (buffer1)) {
1397 			b = 0;
1398 		}
1399 
1400 		/* Check byte for pattern match */
1401 		if (pattern[n++] != byte2) {
1402 			/* If no match, then reset pattern */
1403 			n = 0;
1404 		} else {
1405 			/*
1406 			 * If complete pattern has been matched then
1407 			 * exit loop
1408 			 */
1409 			if (n == plen) {
1410 				goto found;
1411 			}
1412 		}
1413 
1414 
1415 		/* Check first byte second due to endianness */
1416 		/* Save byte in circular buffer */
1417 		buffer1[b++] = byte1;
1418 		if (b == sizeof (buffer1)) {
1419 			b = 0;
1420 		}
1421 		/* Check byte for pattern match */
1422 		if (pattern[n++] != byte1) {
1423 			/* If no match, then reset pattern */
1424 			n = 0;
1425 		} else {
1426 			/*
1427 			 * If complete pattern has been matched
1428 			 * then exit loop
1429 			 */
1430 			if (n == plen) {
1431 				goto found;
1432 			}
1433 		}
1434 	}
1435 
1436 	/* Not found.  Try again with different endianess */
1437 
1438 	/* Initialize */
1439 	bzero(buffer1, sizeof (buffer1));
1440 	bzero(buffer2, sizeof (buffer2));
1441 	n = 0;
1442 	b = 0;
1443 	i = 0;
1444 
1445 	/* Search entire 32bit endian image for pattern string */
1446 	while (i <= (image_size - 4)) {
1447 		/* Read next four bytes */
1448 		byte1 = image[i++];
1449 		byte2 = image[i++];
1450 		byte3 = image[i++];
1451 		byte4 = image[i++];
1452 
1453 		/* Save byte in circular buffer */
1454 		buffer1[b++] = byte4;
1455 		if (b == sizeof (buffer1)) {
1456 			b = 0;
1457 		}
1458 
1459 		/* Check byte for pattern match */
1460 		if (pattern[n++] != byte4) {
1461 			/* If no match, then reset pattern */
1462 			n = 0;
1463 		} else {
1464 			/*
1465 			 * If complete pattern has been matched then exit loop
1466 			 */
1467 			if (n == plen) {
1468 				goto found;
1469 			}
1470 		}
1471 
1472 		/* Save byte in circular buffer */
1473 		buffer1[b++] = byte3;
1474 		if (b == sizeof (buffer1)) {
1475 			b = 0;
1476 		}
1477 
1478 		/* Check byte for pattern match */
1479 		if (pattern[n++] != byte3) {
1480 			/* If no match, then reset pattern */
1481 			n = 0;
1482 		} else {
1483 			/*
1484 			 * If complete pattern has been matched then exit loop
1485 			 */
1486 			if (n == plen) {
1487 				goto found;
1488 			}
1489 		}
1490 
1491 		/* Save byte in circular buffer */
1492 		buffer1[b++] = byte2;
1493 		if (b == sizeof (buffer1)) {
1494 			b = 0;
1495 		}
1496 
1497 		/* Check byte for pattern match */
1498 		if (pattern[n++] != byte2) {
1499 			/* If no match, then reset pattern */
1500 			n = 0;
1501 		} else {
1502 			/*
1503 			 * If complete pattern has been matched then exit loop
1504 			 */
1505 			if (n == plen) {
1506 				goto found;
1507 			}
1508 		}
1509 
1510 		/* Save byte in circular buffer */
1511 		buffer1[b++] = byte1;
1512 		if (b == sizeof (buffer1)) {
1513 			b = 0;
1514 		}
1515 
1516 		/* Check byte for pattern match */
1517 		if (pattern[n++] != byte1) {
1518 			/* If no match, then reset pattern */
1519 			n = 0;
1520 		} else {
1521 			/*
1522 			 * If complete pattern has been matched then exit loop
1523 			 */
1524 			if (n == plen) {
1525 				goto found;
1526 			}
1527 		}
1528 	}
1529 
1530 	free(image);
1531 	return (1);
1532 
1533 found:
1534 	free(image);
1535 
1536 	/* Align buffer and eliminate non-printable characters */
1537 	for (i = 0; i < (sizeof (buffer1)-plen); i++) {
1538 		byte1 = buffer1[b++];
1539 		if (b == sizeof (buffer1)) {
1540 			b = 0;
1541 		}
1542 		/* Zero any non-printable characters */
1543 		if (byte1 >= 33 && byte1 <= 126) {
1544 			buffer2[i] = byte1;
1545 		} else {
1546 			buffer2[i] = 0;
1547 		}
1548 	}
1549 
1550 	/*
1551 	 *  Scan backwards for first non-zero string. This will be the
1552 	 *  version string
1553 	 */
1554 	for (i = sizeof (buffer1)-plen-1; i >= 0; i--) {
1555 		if (buffer2[i] != 0) {
1556 			for (; i >= 0; i--) {
1557 				if (buffer2[i] == 0) {
1558 					i++;
1559 					strncpy((char *)pattern_value,
1560 					    &buffer2[i], pattern_value_size);
1561 					break;
1562 				}
1563 			}
1564 			break;
1565 		}
1566 	}
1567 	return (0);
1568 }
1569 
1570 /*
1571  * error handling routine to handle emulex error conditions
1572  */
1573 static void
1574 handle_emulex_error(int fcio_errno, char *phys_path) {
1575 	if (fcio_errno == EMLX_IMAGE_BAD) {
1576 		fprintf(stderr, MSGSTR(21119,
1577 			    "Error: Fcode download failed.  "
1578 			    "Bad fcode image.\n"));
1579 	} else if (fcio_errno == EMLX_IMAGE_INCOMPATIBLE) {
1580 		fprintf(stderr, MSGSTR(21120,
1581 			    "Error: Fcode download failed.  Fcode is not "
1582 			    "compatible with card.\n"));
1583 	} else {
1584 		(void) fprintf(stderr, MSGSTR(21036,
1585 		    "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n"));
1586 		(void) fprintf(stderr,
1587 			MSGSTR(21113,
1588 				"Error: FCode download failed: %s\n"),
1589 				phys_path);
1590 	}
1591 }
1592