xref: /illumos-gate/usr/src/cmd/luxadm/fcalupdate.c (revision 91760536453132b0d3369ad5543622a5478007e6)
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 /*
28  * I18N message number ranges
29  *  This file: 4500 - 4999
30  *  Shared common messages: 1 - 1999
31  */
32 
33 #include	<fcntl.h>
34 #include	<limits.h>
35 #include	<setjmp.h>
36 #include	<signal.h>
37 #include	<siginfo.h>
38 #include	<stdio.h>
39 #include	<stdlib.h>
40 #include	<string.h>
41 #include	<strings.h>
42 #include	<unistd.h>
43 #include	<ctype.h>
44 #include	<dirent.h>
45 #include	<sys/exec.h>
46 #include	<sys/exechdr.h>
47 #include	<sys/mman.h>
48 #include	<sys/stat.h>
49 #include	<sys/types.h>
50 #include	<sys/fibre-channel/fcio.h>
51 #include	<sys/socalreg.h>
52 /*
53  * The following define is not to
54  * include sys/fc4/fcal_linkapp.h
55  * file from sys/socalio.h, since it
56  * has same structure defines as in
57  * sys/fibre-channel/fcio.h.
58  */
59 #define	_SYS_FC4_FCAL_LINKAPP_H
60 #include	<sys/socalio.h>
61 #include	<sys/time.h>
62 #include	<nl_types.h>
63 #include	<errno.h>
64 #include	<stgcom.h>
65 #include	<gfc.h>
66 #include	<l_common.h>
67 #include	"luxadm.h"
68 
69 /*	Defines 	*/
70 #define	FEPROM_SIZE		256*1024
71 #define	FEPROM_MAX_PROGRAM	25
72 #define	FEPROM_MAX_ERASE	1000
73 #define	FEPROM_READ_MEMORY	0x00
74 #define	FEPROM_ERASE		0x20
75 #define	FEPROM_ERASE_VERIFY	0xa0
76 #define	FEPROM_PROGRAM		0x40
77 #define	FEPROM_PROGRAM_VERIFY	0xc0
78 #define	FEPROM_RESET		0xff
79 #define	HBA_MAX			128
80 
81 #define	FOUND			0
82 #define	NOT_FOUND		1
83 #define	PROM_SIZ		0x20010
84 
85 #define	MAX_RETRIES		3
86 #define	MAX_WAIT_TIME		30
87 
88 /*
89  * The next define is to work around a problem with sbusmem driver not
90  * able to round up mmap() requests that are not around page boundaries.
91  */
92 #define	PROM_SIZ_ROUNDED	0x22000
93 #define	SAMPLE_SIZ		0x100
94 
95 #define	REG_OFFSET		0x20000
96 
97 #define	FEPROM_WWN_OFFSET	0x3fe00
98 
99 #define	FEPROM_SUN_WWN		0x50200200
100 
101 /*
102  * We'll leave this on leadville, as the onboard
103  * isn't allowed to be zapped by luxadm
104  */
105 
106 #define	ONBOARD_SOCAL		"SUNW,socal@d"
107 
108 #define	SOCAL_STR	"SUNW,socal"
109 #define	SOCAL_STR_LEN	10
110 
111 
112 static uchar_t	buffer[FEPROM_SIZE];
113 
114 static char	sbus_list[HBA_MAX][PATH_MAX];
115 static char	sbussoc_list[HBA_MAX][PATH_MAX];
116 static char	bootpath[PATH_MAX];
117 static char	version[MAXNAMELEN];
118 
119 static uint_t	getsbuslist(void);
120 static int	load_file(char *, caddr_t, volatile socal_reg_t *);
121 static void	usec_delay(int);
122 static void	getbootdev(unsigned int);
123 static void	getsocpath(char *, int *);
124 static int	loadsocpath(const char *, int *);
125 static int	warn(void);
126 static int	findversion(int, uchar_t *);
127 static int	write_feprom(uchar_t *, uchar_t *, volatile socal_reg_t *);
128 static int	feprom_erase(volatile uchar_t *, volatile socal_reg_t *);
129 
130 static struct exec exec;
131 
132 int
133 fcal_update(unsigned int verbose, char *file)
134 /*ARGSUSED*/
135 {
136 int		fd, strfound = 0, retval = 0;
137 int		fbuf_idx, fd1, bytes_read;
138 caddr_t		addr;
139 uint_t		i;
140 uint_t		fflag = 0;
141 uint_t		vflag = 0;
142 uint_t		numslots;
143 volatile	socal_reg_t *regs;
144 char		*slotname, socal[MAXNAMELEN];
145 char		fbuf[BUFSIZ];
146 
147 	if (!file)
148 		vflag++;
149 	else {
150 		fflag++;
151 		if ((fd1 = open(file, O_RDONLY)) == -1) {
152 			(void) fprintf(stderr,
153 				MSGSTR(4500,
154 				"Error: open() failed on file "
155 				"%s\n"), file);
156 			return (1);
157 		}
158 	/*
159 	 * We will just make a check to see if it the file
160 	 * has the "SUNW,socal" strings in it
161 	 * We cannot use strstr() here because we are operating on
162 	 * binary data and so is very likely to have embedded NULLs
163 	 */
164 		while (!strfound && ((bytes_read = read(fd1,
165 						fbuf, BUFSIZ)) > 0)) {
166 			for (fbuf_idx = 0; fbuf_idx < bytes_read;
167 							fbuf_idx++) {
168 				/* First check for the SUNW,socal string */
169 				if (strncmp((fbuf + fbuf_idx), SOCAL_STR,
170 						SOCAL_STR_LEN) == 0) {
171 					strfound = 1;
172 					break;
173 				}
174 
175 			}
176 		}
177 		(void) close(fd1);
178 
179 		if (!strfound) {
180 			(void) fprintf(stderr,
181 				MSGSTR(4501,
182 					"Error: %s is not a "
183 					"FC100/S Fcode file\n"), file);
184 			return (1);
185 		}
186 	}
187 
188 	/*
189 	 * Get count of, and names of SBus slots using the SBus memory
190 	 * interface.
191 	 */
192 	(void) getbootdev(verbose);
193 	if (getenv("_LUX_D_DEBUG") != NULL) {
194 		(void) fprintf(stdout, "  Bootpath: %s\n", bootpath);
195 	}
196 
197 	numslots = getsbuslist();
198 	(void) fprintf(stdout,
199 	MSGSTR(4503, "\n  Found Path to %d FC100/S Cards\n"), numslots);
200 
201 	for (i = 0; i < numslots; i++) {
202 
203 		/*
204 		 * Open SBus memory for this slot.
205 		 */
206 		slotname = &sbus_list[i][0];
207 		if (fflag && (strcmp(slotname, bootpath) == 0)) {
208 			(void) fprintf(stderr,
209 			MSGSTR(4504, " Ignoring %s (bootpath)\n"), slotname);
210 			continue;
211 		}
212 
213 		(void) sprintf(socal, "%s:0", &sbussoc_list[i][0]);
214 
215 		if ((fd = open(socal, O_RDWR)) < 0) {
216 			(void) sprintf(socal, "%s:1", &sbussoc_list[i][0]);
217 			if ((fd = open(socal, O_RDWR)) < 0) {
218 				(void) fprintf(stderr,
219 					MSGSTR(4505, "Could not open %s\n"),
220 					&sbussoc_list[i][0]);
221 				(void) fprintf(stderr,
222 					MSGSTR(4506, "Ignoring %s\n"),
223 					&sbussoc_list[i][0]);
224 				retval++;
225 				continue;
226 			}
227 		}
228 
229 		(void) close(fd);
230 
231 		if (verbose) {
232 			(void) fprintf(stdout, "\n  ");
233 			(void) fprintf(stdout,
234 			MSGSTR(85, "Opening %s\n"), slotname);
235 		}
236 
237 		fd = open(slotname, O_RDWR);
238 
239 		if (fd < 0) {
240 			perror(MSGSTR(4507, "open of slotname"));
241 			retval++;
242 			continue;
243 		}
244 
245 		/*
246 		 * Mmap that SBus memory into my memory space.
247 		 */
248 		addr = mmap((caddr_t)0, PROM_SIZ_ROUNDED, PROT_READ|PROT_WRITE,
249 			MAP_SHARED, fd, 0);
250 
251 		if (addr == MAP_FAILED) {
252 			perror(MSGSTR(46, "mmap"));
253 			(void) close(fd);
254 			retval++;
255 			continue;
256 		}
257 
258 		if ((int)addr == -1) {
259 			perror(MSGSTR(46, "mmap"));
260 			(void) close(fd);
261 			retval++;
262 			continue;
263 		}
264 
265 		regs = (socal_reg_t *)((int)addr + REG_OFFSET);
266 
267 		(void) fprintf(stdout,
268 			MSGSTR(4508, "\n  Device: %s\n"),
269 			&sbussoc_list[i][0]);
270 		/*
271 		 * Load the New FCode
272 		 */
273 		if (fflag) {
274 			if (!warn())
275 				retval += load_file(file, addr, regs);
276 		} else if (vflag) {
277 			if (findversion(i, (uchar_t *)&version[0]) == FOUND) {
278 				(void) fprintf(stdout,
279 				MSGSTR(4509,
280 				"  Detected FC100/S Version: %s\n"), version);
281 			}
282 		}
283 
284 		if (munmap(addr, PROM_SIZ) == -1) {
285 			perror(MSGSTR(4510, "munmap"));
286 			retval++;
287 		}
288 
289 		(void) close(fd);
290 
291 	}
292 	(void) fprintf(stdout, "  ");
293 	(void) fprintf(stdout, MSGSTR(125, "Complete\n"));
294 	return (retval);
295 }
296 
297 static int
298 findversion(int index, uchar_t *version)
299 /*ARGSUSED*/
300 {
301 int fd, ntries;
302 struct socal_fm_version	*buffer;
303 char	socal[MAXNAMELEN];
304 char	fp[MAXNAMELEN];
305 char	prom_ver[100];
306 char	mcode_ver[100];
307 uint_t	dev_type;
308 fcio_t	fcio;
309 char	fw_rev[FC_FW_REV_SIZE + 1];
310 
311 
312 	if ((dev_type = g_get_path_type(&sbussoc_list[index][0])) == 0) {
313 		return (L_INVALID_PATH);
314 	}
315 
316 
317 	if (dev_type &  FC4_FCA_MASK) {
318 		P_DPRINTF("findversion: found an FC4 path\n");
319 		(void) sprintf(socal, "%s:0", &sbussoc_list[index][0]);
320 		if ((fd = open(socal, O_RDWR)) < 0) {
321 			(void) sprintf(socal, "%s:1", &sbussoc_list[index][0]);
322 			if ((fd = open(socal, O_RDWR)) < 0) {
323 				(void) fprintf(stderr,
324 					MSGSTR(4511, "Could not open %s\n"),
325 					&sbussoc_list[index][0]);
326 				(void) close(fd);
327 				return (NOT_FOUND);
328 			}
329 		}
330 		if ((buffer = (struct socal_fm_version *)malloc(
331 				sizeof (struct socal_fm_version))) == NULL) {
332 			(void) fprintf(stderr, MSGSTR(10,
333 				" Error: Unable to allocate memory."));
334 			(void) fprintf(stderr, "\n");
335 			(void) close(fd);
336 			return (NOT_FOUND);
337 		}
338 
339 		buffer->fcode_ver = (char *)version;
340 		buffer->mcode_ver = mcode_ver;
341 		buffer->prom_ver = prom_ver;
342 		buffer->fcode_ver_len = MAXNAMELEN - 1;
343 		buffer->mcode_ver_len = 100;
344 		buffer->prom_ver_len = 100;
345 
346 		if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, buffer) < 0) {
347 			(void) fprintf(stderr, MSGSTR(4512,
348 				"fcal_s_download: could not get"
349 				" fcode version.\n"));
350 			(void) close(fd);
351 			(void) free(buffer);
352 			return (NOT_FOUND);
353 		}
354 		version[buffer->fcode_ver_len] = '\0';
355 		free(buffer);
356 
357 	} else if (dev_type & FC_FCA_MASK) {
358 		/*
359 		 * Get the fcode and prom's fw version
360 		 * using new ioctls. Currently, we pass
361 		 * only the fcode version to the calling function
362 		 * and ignore the FW version (using the existing
363 		 * implementation). The function definition
364 		 * might be changed in future to pass both the
365 		 * fcode and FW revisions to the calling function, if
366 		 * needed by the calling function.
367 		 */
368 		P_DPRINTF("findversion: found an FC path\n");
369 		(void) sprintf(fp, "%s/fp@0,0:devctl",
370 			&sbussoc_list[index][0]);
371 		if ((fd = open(fp, O_RDWR)) < 0) {
372 			(void) sprintf(fp, "%s/fp@1,0:devctl",
373 				&sbussoc_list[index][0]);
374 			if ((fd = open(fp, O_RDWR)) < 0) {
375 				(void) fprintf(stderr,
376 					MSGSTR(4511, "Could not open %s\n"),
377 					&sbussoc_list[index][0]);
378 				(void) close(fd);
379 				return (NOT_FOUND);
380 			}
381 		}
382 		/* Get the fcode version */
383 		bzero(version, sizeof (version));
384 		fcio.fcio_cmd = FCIO_GET_FCODE_REV;
385 		/* Information read operation */
386 		fcio.fcio_xfer = FCIO_XFER_READ;
387 		fcio.fcio_obuf = (caddr_t)version;
388 		fcio.fcio_olen = MAXNAMELEN;
389 
390 		for (ntries = 0; ntries < MAX_RETRIES; ntries++) {
391 			if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
392 				if ((errno == EAGAIN) &&
393 					(ntries+1 < MAX_RETRIES)) {
394 					/* wait 30 secs */
395 					(void) sleep(MAX_WAIT_TIME);
396 					continue;
397 				}
398 				I_DPRINTF("ioctl FCIO_GET_FCODE_REV failed.\n"
399 					"Error: %s\n", strerror(errno));
400 				(void) close(fd);
401 				return (L_FCIO_GET_FCODE_REV_FAIL);
402 			}
403 			break;
404 		}
405 		version[MAXNAMELEN-1] = '\0';
406 
407 		/* Get the FW revision */
408 		bzero(fw_rev, sizeof (fw_rev));
409 		fcio.fcio_cmd = FCIO_GET_FW_REV;
410 		/* Information read operation */
411 		fcio.fcio_xfer = FCIO_XFER_READ;
412 		fcio.fcio_obuf = (caddr_t)fw_rev;
413 		fcio.fcio_olen = FC_FW_REV_SIZE;
414 		for (ntries = 0; ntries < MAX_RETRIES; ntries++) {
415 			if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
416 				if ((errno == EAGAIN) &&
417 					(ntries+1 < MAX_RETRIES)) {
418 					/* wait 30 secs */
419 					(void) sleep(MAX_WAIT_TIME);
420 					continue;
421 				}
422 				I_DPRINTF(" FCIO_GET_FW_REV ioctl failed.\n"
423 					" Error: %s\n", strerror(errno));
424 				(void) close(fd);
425 				return (L_FCIO_GET_FW_REV_FAIL);
426 			}
427 			break;
428 		}
429 	}
430 
431 	(void) close(fd);
432 	return (FOUND);
433 }
434 /*
435  * program an FEprom with data from 'source_address'.
436  *	program the FEprom with zeroes,
437  *	erase it,
438  *	program it with the real data.
439  */
440 static int
441 feprom_program(uchar_t *source_address, uchar_t *dest_address,
442 	volatile socal_reg_t *regs)
443 {
444 	int i;
445 
446 	(void) fprintf(stdout, MSGSTR(4513, "Filling with zeroes...\n"));
447 	if (!write_feprom((uchar_t *)0, dest_address, regs)) {
448 		(void) fprintf(stderr,
449 			MSGSTR(4514, "FEprom at 0x%x: zero fill failed\n"),
450 			(int)dest_address);
451 		return (0);
452 	}
453 
454 	(void) fprintf(stdout, MSGSTR(4515, "Erasing...\n"));
455 	for (i = 0; i < FEPROM_MAX_ERASE; i++) {
456 		if (feprom_erase(dest_address, regs))
457 			break;
458 	}
459 
460 	if (i >= FEPROM_MAX_ERASE) {
461 		(void) fprintf(stderr,
462 			MSGSTR(4516, "FEprom at 0x%x: failed to erase\n"),
463 			(int)dest_address);
464 		return (0);
465 	} else if (i > 0) {
466 		if (i == 1) {
467 			(void) fprintf(stderr, MSGSTR(4517,
468 				"FEprom erased after %d attempt\n"), i);
469 		} else {
470 			(void) fprintf(stderr, MSGSTR(4518,
471 				"FEprom erased after %d attempts\n"), i);
472 		}
473 	}
474 
475 	(void) fprintf(stdout, MSGSTR(4519, "Programming...\n"));
476 	if (!(write_feprom(source_address, dest_address, regs))) {
477 		(void) fprintf(stderr,
478 			MSGSTR(4520, "FEprom at 0x%x: write failed\n"),
479 			(int)dest_address);
480 		return (0);
481 	}
482 
483 	/* select the zeroth bank at end so we can read it */
484 	regs->socal_cr.w &= ~(0x30000);
485 	(void) fprintf(stdout, MSGSTR(4521, "Programming done\n"));
486 	return (1);
487 }
488 
489 /*
490  * program an FEprom one byte at a time using hot electron injection.
491  */
492 static int
493 write_feprom(uchar_t *source_address, uchar_t *dest_address,
494 	volatile socal_reg_t *regs)
495 {
496 	int pulse, i;
497 	uchar_t *s = source_address;
498 	volatile uchar_t *d;
499 
500 	for (i = 0; i < FEPROM_SIZE; i++, s++) {
501 
502 		if ((i & 0xffff) == 0) {
503 			(void) fprintf(stdout,
504 			MSGSTR(4522, "selecting bank %d\n"), i>>16);
505 			regs->socal_cr.w &= ~(0x30000);
506 			regs->socal_cr.w |= i & 0x30000;
507 		}
508 
509 		d = dest_address + (i & 0xffff);
510 
511 		for (pulse = 0; pulse < FEPROM_MAX_PROGRAM; pulse++) {
512 			*d = FEPROM_PROGRAM;
513 			*d = source_address ? *s : 0;
514 			usec_delay(50);
515 			*d = FEPROM_PROGRAM_VERIFY;
516 			usec_delay(30);
517 			if (*d == (source_address ? *s : 0))
518 					break;
519 		}
520 
521 		if (pulse >= FEPROM_MAX_PROGRAM) {
522 			*dest_address = FEPROM_RESET;
523 			return (0);
524 		}
525 	}
526 
527 	*dest_address = FEPROM_RESET;
528 	return (1);
529 }
530 
531 /*
532  * erase an FEprom using Fowler-Nordheim tunneling.
533  */
534 static int
535 feprom_erase(volatile uchar_t *dest_address, volatile socal_reg_t *regs)
536 {
537 	int i;
538 	volatile uchar_t *d = dest_address;
539 
540 	*d = FEPROM_ERASE;
541 	usec_delay(50);
542 	*d = FEPROM_ERASE;
543 
544 	usec_delay(10000); /* wait 10ms while FEprom erases */
545 
546 	for (i = 0; i < FEPROM_SIZE; i++) {
547 
548 		if ((i & 0xffff) == 0) {
549 			regs->socal_cr.w &= ~(0x30000);
550 			regs->socal_cr.w |= i & 0x30000;
551 		}
552 
553 		d = dest_address + (i & 0xffff);
554 
555 		*d = FEPROM_ERASE_VERIFY;
556 		usec_delay(50);
557 		if (*d != 0xff) {
558 			*dest_address = FEPROM_RESET;
559 			return (0);
560 		}
561 	}
562 	*dest_address = FEPROM_RESET;
563 	return (1);
564 }
565 
566 static void
567 usec_delay(int s)
568 {
569 	hrtime_t now, then;
570 
571 	now = gethrtime();
572 	then = now + s*1000;
573 	do {
574 		now = gethrtime();
575 	} while (now < then);
576 }
577 
578 static uint_t
579 getsbuslist(void)
580 {
581 	int devcnt = 0;
582 	char devpath[PATH_MAX];
583 
584 	/* We're searching the /devices directory, so... */
585 	(void) strcpy(devpath, "/devices");
586 
587 	/* get the directory entries under /devices for socal sbusmem */
588 	(void) getsocpath(devpath, (int *)&devcnt);
589 
590 	return (devcnt);
591 }
592 
593 static void
594 getbootdev(unsigned int verbose)
595 {
596 	char *df = "df /";
597 	FILE *ptr;
598 	char *p, *p1;
599 	char bootdev[PATH_MAX];
600 	char buf[BUFSIZ];
601 	int foundroot = 0;
602 
603 
604 	if ((ptr = popen(df, "r")) != NULL) {
605 		while (fgets(buf, BUFSIZ, ptr) != NULL) {
606 			if (p = strstr(buf, "/dev/dsk/")) {
607 				(void) memset((char *)&bootdev[0], 0,
608 					PATH_MAX);
609 				p1 = p;
610 				while (*p1 != '\0') {
611 					if (!isalnum(*p1) && (*p1 != '/'))
612 						*p1 = ' ';
613 					p1++;
614 				}
615 				(void) sscanf(p, "%s", bootdev);
616 				foundroot = 1;
617 			}
618 		}
619 		if (!foundroot) {
620 			if (verbose)
621 				(void) fprintf(stderr, MSGSTR(44,
622 					"root is not on a local disk!\n"));
623 			(void) memset((char *)&bootpath[0], 0, PATH_MAX);
624 			return;
625 		}
626 		(void) pclose(ptr);
627 		if (bootdev[0]) {
628 			char *ls;
629 			char *p1;
630 			char *p2 = NULL;
631 			char *sbusmem = "/sbusmem@";
632 			char *slot = ",0:slot";
633 
634 			ls = (char *)malloc(PATH_MAX);
635 			(void) memset((char *)ls, NULL, PATH_MAX);
636 			(void) strcpy(ls, "ls -l ");
637 			(void) strcat(ls, bootdev);
638 			if ((ptr = popen(ls, "r")) != NULL) {
639 				while (fgets(buf, BUFSIZ, ptr) != NULL) {
640 					if (p = strstr(buf, "/devices")) {
641 					    if (p1 = strstr(buf, "sbus")) {
642 						while (*p1 != '/')
643 							p1++;
644 						p2 = strstr(p1, "@");
645 						++p2;
646 						*p1 = '\0';
647 					    } else {
648 						if (p1 = strstr(buf,
649 								SOCAL_STR)) {
650 							p2 = strstr(p1, "@");
651 							++p2;
652 							--p1;
653 							*p1 = '\0';
654 						}
655 					    }
656 					}
657 				}
658 				(void) pclose(ptr);
659 			}
660 			(void) memset((char *)&bootdev[0], 0, PATH_MAX);
661 			(void) sscanf(p, "%s", bootdev);
662 			(void) memset((char *)&bootpath[0], 0, PATH_MAX);
663 			(void) strcat(bootpath, bootdev);
664 			(void) strcat(bootpath, sbusmem);
665 			if (p2) {
666 				(void) strncat(bootpath, p2, 1);
667 				(void) strcat(bootpath, slot);
668 				(void) strncat(bootpath, p2, 1);
669 			}
670 		}
671 	}
672 }
673 
674 /*
675  * This function reads "size" bytes from the FC100/S PROM.
676  * source_address: PROM address
677  * dest_address:   local memeory
678  * offset:         Location in PROM to start reading from.
679  */
680 static void
681 feprom_read(uchar_t *source_address, uchar_t *dest_address,
682 		int offset, int size, volatile socal_reg_t *regs)
683 {
684 uchar_t  *s = source_address;
685 uchar_t  *d = dest_address;
686 int	i = offset;
687 
688 	if (getenv("_LUX_D_DEBUG") != NULL) {
689 		(void) fprintf(stdout,
690 			"  feprom_read: selecting bank %d\n",
691 			(i&0xf0000)>>16);
692 		if (size <= 8) {
693 			(void) fprintf(stdout, "  Data read: ");
694 		}
695 	}
696 	regs->socal_cr.w = i & 0xf0000;
697 	s = source_address + (i & 0xffff);
698 	*s = FEPROM_READ_MEMORY;
699 	usec_delay(6);
700 	for (; s < source_address + (i & 0xffff) + size; d++, s++) {
701 		*d = *s;
702 		if ((getenv("_LUX_D_DEBUG") != NULL) &&
703 			(size <= 8)) {
704 			(void) fprintf(stdout, "0x%x ", *d);
705 		}
706 	}
707 	if ((getenv("_LUX_D_DEBUG") != NULL) &&
708 		(size <= 8)) {
709 		(void) fprintf(stdout, "\n  From offset: 0x%x\n",
710 			offset);
711 	}
712 }
713 
714 
715 static int
716 load_file(char *file, caddr_t prom, volatile socal_reg_t *regs)
717 {
718 uint_t	wwn_d8, wwn_lo;
719 uint_t	wwn_hi;
720 int ffd = open(file, 0);
721 
722 	wwn_hi = FEPROM_SUN_WWN;
723 
724 	if (ffd < 0) {
725 		perror(MSGSTR(4524, "open of file"));
726 		exit(1);
727 	}
728 	(void) fprintf(stdout, MSGSTR(4525, "Loading FCode: %s\n"), file);
729 
730 	if (read(ffd, &exec, sizeof (exec)) != sizeof (exec)) {
731 		perror(MSGSTR(4526, "read exec"));
732 		exit(1);
733 	}
734 
735 	if (exec.a_trsize || exec.a_drsize) {
736 		(void) fprintf(stderr,
737 			MSGSTR(4527, "%s: is relocatable\n"), file);
738 		exit(1);
739 	}
740 
741 	if (exec.a_data || exec.a_bss) {
742 		(void) fprintf(stderr,
743 			MSGSTR(4528, "%s: has data or bss\n"), file);
744 		exit(1);
745 	}
746 
747 	if (exec.a_machtype != M_SPARC) {
748 		(void) fprintf(stderr,
749 			MSGSTR(4529, "%s: not for SPARC\n"), file);
750 		exit(1);
751 	}
752 
753 	(void) fprintf(stdout, MSGSTR(4530,
754 		"Loading 0x%x bytes from %s at offset 0x%x\n"),
755 		(int)exec.a_text, file, 0);
756 
757 	if (read(ffd, &buffer, exec.a_text) != exec.a_text) {
758 		perror(MSGSTR(4531, "read"));
759 		exit(1);
760 	}
761 
762 	(void) close(ffd);
763 
764 	feprom_read((uchar_t *)prom, (uchar_t *)&wwn_d8,
765 		FEPROM_WWN_OFFSET, 4, regs);
766 	feprom_read((uchar_t *)prom, (uchar_t *)&wwn_lo,
767 		FEPROM_WWN_OFFSET + 4, 4, regs);
768 	wwn_hi |= wwn_d8 & 0x0f; /* only last digit is interesting */
769 	if (getenv("_LUX_D_DEBUG") != NULL) {
770 		(void) fprintf(stdout,
771 			"  load_file: Writing WWN hi:0x%x lo:0x%x "
772 			"to the FC100/S PROM\n", wwn_hi, wwn_lo);
773 	}
774 	/* put wwn into buffer location */
775 	bcopy((const void *)&wwn_hi,
776 		(void *)&buffer[FEPROM_WWN_OFFSET],
777 		sizeof (wwn_hi));
778 	bcopy((const void *)&wwn_lo,
779 		(void *)&buffer[FEPROM_WWN_OFFSET + 4],
780 		sizeof (wwn_lo));
781 	bcopy((const void *)&wwn_hi,
782 		(void *)&buffer[FEPROM_WWN_OFFSET + 8],
783 		sizeof (wwn_hi));
784 	bcopy((const void *)&wwn_lo,
785 		(void *)&buffer[FEPROM_WWN_OFFSET + 0xc],
786 		sizeof (wwn_lo));
787 
788 	if (feprom_program((uchar_t *)buffer, (uchar_t *)prom, regs) == 0) {
789 		/* here 0 means failure */
790 		return (1);
791 	}
792 
793 	return (0);
794 }
795 
796 static int
797 warn(void)
798 {
799 	char input[1024];
800 
801 	input[0] = '\0';
802 
803 	(void) fprintf(stderr, MSGSTR(4532,
804 "\nWARNING!! This program will update the FCode in this FC100/S Sbus Card.\n"));
805 	(void) fprintf(stderr,  MSGSTR(4533,
806 "This may take a few (5) minutes. Please be patient.\n"));
807 
808 loop1:
809 	(void) fprintf(stderr, MSGSTR(4534,
810 		"Do you wish to continue ? (y/n) "));
811 
812 	(void) gets(input);
813 
814 	if ((strcmp(input, MSGSTR(4535, "y")) == 0) ||
815 			(strcmp(input, MSGSTR(40, "yes")) == 0)) {
816 		return (FOUND);
817 	} else if ((strcmp(input, MSGSTR(4536, "n")) == 0) ||
818 			(strcmp(input, MSGSTR(45, "no")) == 0)) {
819 		(void) fprintf(stderr, MSGSTR(4537, "Not Downloading FCode\n"));
820 		return (NOT_FOUND);
821 	} else {
822 		(void) fprintf(stderr, MSGSTR(4538, "Invalid input\n"));
823 		goto loop1;
824 	}
825 }
826 
827 
828 /*
829  * getsocpath():
830  * 	Searches the /devices directory recursively returning all soc_name
831  *	entries in sbussoc_list (global). This excludes port entries and
832  * 	onboard socal (which leaves only directory entries with
833  *	soc_name included). devcnt is updated to reflect number of soc_name
834  *	devices found.
835  */
836 
837 static void
838 getsocpath(char *devpath, int *devcnt)
839 {
840 	struct stat	statbuf;
841 	struct dirent	*dirp;
842 	DIR		*dp;
843 	char		*ptr;
844 
845 	if (lstat(devpath, &statbuf) < 0) {
846 		(void) fprintf(stderr,
847 		MSGSTR(4539, "Error: %s lstat() error\n"), devpath);
848 		exit(1);
849 	}
850 
851 	if (S_ISDIR(statbuf.st_mode) == 0)
852 		/*
853 		 * not a directory so
854 		 * we don't care about it - return
855 		 */
856 		return;
857 
858 	else {
859 		if (strstr(devpath, ONBOARD_SOCAL))
860 			return;
861 
862 		if (strstr(devpath, SOCAL_STR)) {
863 			/* It's a keeper - call the load function */
864 			if ((loadsocpath(devpath, devcnt)) < 0) {
865 				(void) fprintf(stderr,
866 				MSGSTR(4540, "Error: Cannot set device list\n"),
867 					devpath);
868 				exit(1);
869 			}
870 			/*
871 			 * if socal directory - return,
872 			 * nothing else to see here
873 			 */
874 			return;
875 		}
876 	}
877 
878 	/*
879 	 * It's a directory. Call ourself to
880 	 * traverse the path(s)
881 	 */
882 
883 	ptr = devpath + strlen(devpath);
884 	*ptr++ = '/';
885 	*ptr = 0;
886 
887 	/* Forget the /devices/pseudo/ directory */
888 	if (strcmp(devpath, "/devices/pseudo/") == 0)
889 		return;
890 
891 	if ((dp = opendir(devpath)) == NULL) {
892 		(void) fprintf(stderr,
893 		MSGSTR(4541, "Error: %s Can't read directory\n"), devpath);
894 		exit(1);
895 	}
896 
897 	while ((dirp = readdir(dp)) != NULL) {
898 
899 		if (strcmp(dirp->d_name, ".") == 0 ||
900 			strcmp(dirp->d_name, "..") == 0)
901 			continue;
902 
903 		(void) strcpy(ptr, dirp->d_name); /* append name */
904 		getsocpath(devpath, devcnt);
905 	}
906 
907 	if (closedir(dp) < 0) {
908 		(void) fprintf(stderr,
909 		MSGSTR(4542, "Error: %s Can't close directory\n"), devpath);
910 		exit(1);
911 	}
912 }
913 
914 static int
915 loadsocpath(const char *pathname, int *devcnt)
916 {
917 	int ret = 0;
918 	int len;
919 	int len_tmp;
920 	char *sp;
921 	char *sp_tmp;
922 	char buffer[PATH_MAX];
923 
924 
925 	/*
926 	 * Okay we found a device, now let's load it in to sbussoc_list
927 	 * and load the sbusmem file into sbus_list
928 	 */
929 
930 	if (pathname != NULL && *devcnt < HBA_MAX) {
931 		(void) strcpy(sbussoc_list[*devcnt], pathname);
932 		if (sp_tmp = strstr(sbussoc_list[*devcnt], SOCAL_STR)) {
933 			sp = sp_tmp;
934 			/* len_tmp will be len of "SUNW,socal@" */
935 			len_tmp = SOCAL_STR_LEN + 1;
936 		}
937 		len = strlen(sbussoc_list[*devcnt]) - strlen(sp);
938 		(void) strncpy(buffer, sbussoc_list[*devcnt], len);
939 		buffer[len] = '\0';
940 		sp += len_tmp;
941 		(void) sprintf(sbus_list[*devcnt], "%ssbusmem@%c,0:slot%c",
942 			buffer, sp[0], sp[0]);
943 		*devcnt += 1;
944 	}
945 	else
946 		ret = -1;
947 	return (ret);
948 }
949