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