diff options
Diffstat (limited to 'usr/src/cmd/luxadm/qlgcupdate.c')
| -rw-r--r-- | usr/src/cmd/luxadm/qlgcupdate.c | 1591 |
1 files changed, 1591 insertions, 0 deletions
diff --git a/usr/src/cmd/luxadm/qlgcupdate.c b/usr/src/cmd/luxadm/qlgcupdate.c new file mode 100644 index 0000000000..653e98e234 --- /dev/null +++ b/usr/src/cmd/luxadm/qlgcupdate.c @@ -0,0 +1,1591 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * I18N message number ranges + * This file: 21000 - 21499 + * Shared common messages: 1 - 1999 + */ + + + +/* + * Functions to support the download of FCode to PCI HBAs + * Qlogic ISP21XX/22XX boards: FC100/P single port, ISP2200 dual port + * and Emulex cards + */ +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <limits.h> +#include <signal.h> +#include <dirent.h> +#include <nl_types.h> +#include <utmpx.h> +#include <sys/mnttab.h> +#include <sys/file.h> +#include <sys/mtio.h> +#include <sys/scsi/impl/uscsi.h> +#include <sys/fibre-channel/fcio.h> +#include <stgcom.h> +#include <sys/scsi/adapters/ifpio.h> +#include <libdevinfo.h> +#include "luxadm.h" + +/* Error codes - used by the fcode_load_file routine */ +#define FCODE_SUCCESS 0 /* successful completion */ +#define FCODE_LOAD_FAILURE 1 /* general failure */ +#define FCODE_IOCTL_FAILURE 2 /* FCODE ioctl download failure */ + +#define HBA_MAX 128 +#define FCODE_HDR 200 +#define MAX_RETRIES 3 +#define MAX_WAIT_TIME 30 + +/* + * EMULEX Fcode attributes + */ +#define EMULEX_FCODE_VERSION_LENGTH 16 +#define EMULEX_READ_BUFFER_SIZE 128 + +/* Emulex specific error codes */ +#define EMLX_ERRNO_START 0x100 + +/* Diagnostic error codes */ +#define EMLX_TEST_FAILED (EMLX_ERRNO_START + 0) + +/* Download image contains bad data */ +#define EMLX_IMAGE_BAD (EMLX_ERRNO_START + 1) +/* Download image not compatible with current hardware */ +#define EMLX_IMAGE_INCOMPATIBLE (EMLX_ERRNO_START + 2) +/* Unable to take adapter offline */ +#define EMLX_IMAGE_FAILED (EMLX_ERRNO_START + 3) +/* Image download failed */ +#define EMLX_OFFLINE_FAILED (EMLX_ERRNO_START + 4) + + + + +/* + * This is just a random value chosen to identify Sbus Fcodes. Sbus FCode + * for Ivory is based on a 2200 chip but this value does not reflect that. + */ +#define SBUS_CHIP_ID 0x1969 +#define IVORY_BUS "/sbus@" +#define IVORY_DRVR "/SUNW,qlc@" + +/* Global variables */ +static char fc_trans[] = "SUNW,ifp"; /* fibre channel transport */ +static char fp_trans[] = "SUNW,qlc"; /* fca layer driver */ +static char fp_trans_id[] = "fp@"; /* transport layer id */ +static char qlgc2100[] = "FC100/P"; /* product name for 2100 */ +static char qlgc2200[] = "ISP2200"; /* product name for 2200 */ +static char qlgc2300[] = "ISP2300"; /* product name for 2300 */ +static char qlgc2312[] = "ISP2312"; /* product name for 2312 */ +/* + * The variable qlgc2200Sbus represents the string which is always the + * starting string of the version information in an ISP2200 Sbus Fcode. + */ +static char qlgc2200Sbus[] = "ISP2200 Sbus FC-AL Host Adapter Driver"; +static char pcibus_list[HBA_MAX][PATH_MAX]; +/* Internal functions */ +static int q_load_file(int, char *); +static int q_getbootdev(uchar_t *); +static int q_getdevctlpath(char *, int *); +static int q_warn(int); +static int q_findversion(int, int, uchar_t *, uint16_t *); +static int q_findfileversion(char *, uchar_t *, uint16_t *, int, int *); +static int q_findSbusfile(int, int *); +static int memstrstr(char *, char *, int, int); +static int fcode_load_file(int, char *, int *); + +/* + * Functions to support Fcode download for Emulex HBAs + */ +static int emulex_fcodeversion(di_node_t, uchar_t *); +static void handle_emulex_error(int, char *); + +/* + * Searches for and updates the cards. This is the "main" function + * and will give the output to the user by calling the subfunctions. + * args: FCode file; if NULL only the current FCode version is printed + */ +int +q_qlgc_update(unsigned int verbose, char *file) +/*ARGSUSED*/ +{ + int fd, fcode_fd = -1, errnum = 0, devcnt = 0, retval = 0, isSbus = 0; + int sbus_off; + uint_t i, fflag = 0; + uint16_t chip_id = 0, file_id = 0; + uchar_t fcode_buf[FCODE_HDR]; + static uchar_t bootpath[PATH_MAX]; + static uchar_t version[MAXNAMELEN], version_file[MAXNAMELEN]; + char devpath[PATH_MAX], tmppath[PATH_MAX]; + void (*sigint)(); /* to store default SIGTERM setting */ + static struct utmpx *utmpp = NULL; /* pointer for getutxent() */ + char *ptr1, *ptr2; + char phys_path[PATH_MAX]; + /* + * The variables port1 and port2 are used to store the bus id + * e.g. the bus id for this path: + * /devices/sbus@12,0/SUNW,qlc@2,30000/fp@0,0:devctl + * is "sbus@12". They are initialized to a random value and are + * set such that they are not equal initially. + */ + static char port1[MAXNAMELEN] = {NULL}; + static char port2[MAXNAMELEN] = {NULL}; + + if (file) { + fflag++; + + /* check for a valid file */ + if ((fcode_fd = open(file, O_RDONLY)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), file); + return (1); + } + if (read(fcode_fd, fcode_buf, FCODE_HDR) != FCODE_HDR) { + perror(MSGSTR(21001, "read")); + (void) close(fcode_fd); + return (1); + } + + /* + * Check if it's SBUS FCode by calling q_findSbusfile + * if it is then isSbus will be 1, if not it will be 0 + * in case of an error, it will be -1 + */ + isSbus = q_findSbusfile(fcode_fd, &sbus_off); + if (isSbus == -1) { + (void) close(fcode_fd); + return (1); + } + + /* + * FCode header check - make sure it's PCI FCode + * Structure of FCode header (byte# refers to byte numbering + * in FCode spec, not the byte# of our fcode_buf buffer): + * header byte 00 0x55 prom signature byte one + * byte 01 0xaa prom signature byte two + * data byte 00-03 P C I R + * OR + * header byte 32 0x55 + * byte 33 0xaa + * data byte 60-63 P C I R + * The second format with an offset of 32 is used for ifp prom + */ + if (!(((fcode_buf[0x00] == 0x55) && + (fcode_buf[0x01] == 0xaa) && + (fcode_buf[0x1c] == 'P') && + (fcode_buf[0x1d] == 'C') && + (fcode_buf[0x1e] == 'I') && + (fcode_buf[0x1f] == 'R')) || + + ((fcode_buf[0x20] == 0x55) && + (fcode_buf[0x21] == 0xaa) && + (fcode_buf[0x3c] == 'P') && + (fcode_buf[0x3d] == 'C') && + (fcode_buf[0x3e] == 'I') && + (fcode_buf[0x3f] == 'R')) || + + (isSbus))) { + (void) fprintf(stderr, MSGSTR(21002, + "Error: %s is not a valid FC100/P, " + "ISP2200, ISP23xx FCode file.\n"), + file); + (void) close(fcode_fd); + return (1); + } + + /* check for single user mode */ + while ((utmpp = getutxent()) != NULL) { + if (strstr(utmpp->ut_line, "run-level") && + (strcmp(utmpp->ut_line, "run-level S") && + strcmp(utmpp->ut_line, "run-level 1"))) { + if (q_warn(1)) { + (void) endutxent(); + (void) close(fcode_fd); + return (1); + } + break; + } + } + (void) endutxent(); + + /* get bootpath */ + if (!q_getbootdev((uchar_t *)&bootpath[0]) && + getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Bootpath: %s\n", bootpath); + } + } + /* + * Get count of, and names of PCI slots with ifp device control + * (devctl) nodes. Search /devices. + */ + (void) strcpy(devpath, "/devices"); + if (q_getdevctlpath(devpath, (int *)&devcnt) == 0) { + (void) fprintf(stdout, MSGSTR(21003, + "\n Found Path to %d FC100/P, ISP2200, ISP23xx Devices\n"), + devcnt); + } else { + (void) fprintf(stderr, MSGSTR(21004, + "Error: Could not get /devices path to FC100/P," + "ISP2200, ISP23xx Cards.\n")); + retval++; + } + + for (i = 0; i < devcnt; i++) { + + (void) strncpy((char *)phys_path, &pcibus_list[i][0], + strlen(&pcibus_list[i][0])); + if (fflag && (strstr((char *)bootpath, + strtok((char *)phys_path, ":")) != NULL)) { + (void) fprintf(stderr, + MSGSTR(21005, "Ignoring %s (bootpath)\n"), + &pcibus_list[i][0]); + continue; + } + + (void) fprintf(stdout, + MSGSTR(21006, "\n Opening Device: %s\n"), &pcibus_list[i][0]); + /* Check if the device is valid */ + if ((fd = open(&pcibus_list[i][0], O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), + &pcibus_list[i][0]); + retval++; + continue; + } + (void) close(fd); + /* + * Check FCode version present on the adapter (at last boot) + */ + if (q_findversion(verbose, i, (uchar_t *)&version[0], + &chip_id) == 0) { + if (strlen((char *)version) == 0) { + (void) fprintf(stdout, MSGSTR(21007, + " Detected FCode Version:\tNo version available for this FCode\n")); + } else { + (void) fprintf(stdout, MSGSTR(21008, + " Detected FCode Version:\t%s\n"), version); + } + } else { + chip_id = 0x0; + } + + if (fflag) { + /* + * For ISP2200, Sbus HBA, do just 1 download + * for both the ports (dual port HBA) + * Here it is assumed that readdir() always + * returns the paths in pcibus_list[] in the + * sorted order. + */ + (void) strcpy(tmppath, pcibus_list[i]); + if (ptr1 = strstr(tmppath, IVORY_BUS)) { + if (ptr2 = strstr(ptr1, IVORY_DRVR)) { + ptr2 = strchr(ptr2, ','); + if (ptr2 = strchr(++ptr2, ',')) { + *ptr2 = '\0'; + } + } + (void) strcpy(port2, ptr1); + if (strcmp(port1, port2) == 0) { + (void) fprintf(stdout, MSGSTR(21037, + "/n New FCode has already been downloaded " + "to this ISP2200 SBus HBA Card.\n" + "It is sufficient to download to one " + "port of the ISP2200 SBus HBA Card. " + "Moving on...\n")); + continue; + } + } + /* + * Check version of the supplied FCode file (once) + */ + if ((file_id != 0 && version_file != NULL) || + (q_findfileversion((char *) + &fcode_buf[0], (uchar_t *)&version_file[0], + &file_id, isSbus, &sbus_off) == 0)) { + (void) fprintf(stdout, MSGSTR(21009, + " New FCode Version:\t\t%s\n"), + version_file); + } else { + (void) close(fcode_fd); + return (1); + } + + /* + * Load the New FCode + * Give warning if file doesn't appear to be correct + * + */ + if (chip_id == 0) { + errnum = 2; /* can't get chip_id */ + retval++; + } else if (chip_id - file_id != 0) { + errnum = 3; /* file/card mismatch */ + retval++; + } else { + errnum = 0; /* everything is ok */ + } + + if (!q_warn(errnum)) { + /* Disable user-interrupt Control-C */ + sigint = + (void (*)(int)) signal(SIGINT, SIG_IGN); + + /* Load FCode */ + (void) fprintf(stdout, MSGSTR(21010, + " Loading FCode: %s\n"), file); + + if (q_load_file(fcode_fd, + &pcibus_list[i][0]) == 0) { + (void) fprintf(stdout, MSGSTR(21011, + " Successful FCode download: %s\n"), + &pcibus_list[i][0]); + (void) strcpy(port1, port2); + } else { + (void) fprintf(stderr, MSGSTR(21012, + "Error: FCode download failed: %s\n"), + &pcibus_list[i][0]); + retval++; + } + /* Restore SIGINT (user interrupt) setting */ + (void) signal(SIGINT, sigint); + } + } + } + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(125, "Complete\n")); + if (fcode_fd != -1) + (void) close(fcode_fd); + return (retval); +} + + +/* + * Retrieve the version banner from the card + * uses ioctl: FCIO_FCODE_MCODE_VERSION FCode revision + */ +static int +q_findversion(int verbose, int index, uchar_t *version, uint16_t *chip_id) +/*ARGSUSED*/ +{ + int fd, ntries; + struct ifp_fm_version *version_buffer = NULL; + char prom_ver[100] = {NULL}; + char mcode_ver[100] = {NULL}; + fcio_t fcio; + + if (strstr(&pcibus_list[index][0], fc_trans)) { + + if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), + &pcibus_list[index][0]); + return (1); + } + + if ((version_buffer = (struct ifp_fm_version *)malloc( + sizeof (struct ifp_fm_version))) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + (void) close(fd); + return (1); + } + + version_buffer->fcode_ver = (char *)version; + version_buffer->mcode_ver = mcode_ver; + version_buffer->prom_ver = prom_ver; + version_buffer->fcode_ver_len = MAXNAMELEN - 1; + version_buffer->mcode_ver_len = 100; + version_buffer->prom_ver_len = 100; + + if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, version_buffer) < 0) { + (void) fprintf(stderr, MSGSTR(21014, + "Error: Driver interface FCIO_FCODE_MCODE_VERSION failed\n")); + free(version_buffer); + (void) close(fd); + return (1); + } + version[version_buffer->fcode_ver_len] = '\0'; + + /* Need a way to get card MCODE (firmware) to track certain HW bugs */ + if (getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Device %i: QLGC chip_id %x\n", + index+1, *chip_id); + (void) fprintf(stdout, " FCode:%s\n MCODE:%s\n PROM:%s\n", + (char *)version, mcode_ver, prom_ver); + } + free(version_buffer); + + } else if (strstr(&pcibus_list[index][0], fp_trans)) { + /* + * Get the fcode and prom's fw version + * using the fp ioctls. Currently, we pass + * only the fcode version to the calling function + * and ignore the FW version (using the existing + * implementation). + */ + + if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(4511, "Could not open %s\n"), + &pcibus_list[index][0]); + (void) close(fd); + return (1); + } + /* Get the fcode version */ + bzero(version, sizeof (version)); + fcio.fcio_cmd = FCIO_GET_FCODE_REV; + /* Information read operation */ + fcio.fcio_xfer = FCIO_XFER_READ; + fcio.fcio_obuf = (caddr_t)version; + fcio.fcio_olen = MAXNAMELEN; + + for (ntries = 0; ntries < MAX_RETRIES; ntries++) { + if (ioctl(fd, FCIO_CMD, &fcio) != 0) { + if ((errno == EAGAIN) && + (ntries+1 < MAX_RETRIES)) { + /* wait 30 secs */ + (void) sleep(MAX_WAIT_TIME); + continue; + } + (void) close(fd); + return (L_FCIO_GET_FCODE_REV_FAIL); + } + break; + } + version[MAXNAMELEN-1] = '\0'; + } + + /* Get type of card from product name in FCode version banner */ + if (strstr((char *)version, qlgc2100)) { + *chip_id = 0x2100; + } else if (strstr((char *)version, qlgc2200)) { + *chip_id = 0x2200; + if (strstr((char *)version, "Sbus")) { + *chip_id = SBUS_CHIP_ID; + } + } else if (strstr((char *)version, qlgc2300)) { + *chip_id = 0x2300; + } else if (strstr((char *)version, qlgc2312)) { + *chip_id = 0x2312; + } else { + *chip_id = 0x0; + } + + (void) close(fd); + return (0); +} + +/* + * Retrieve the version banner and file type (2100 or 2200) from the file + */ +static int +q_findfileversion(char *dl_fcode, uchar_t *version_file, uint16_t *file_id, + int isSbus, int *sbus_offset) +{ + int mark; + int qlc_offset = 0; + char temp[4] = {NULL}; + + + /* + * Get file version from FCode for 2100 or 2202 + */ + if (isSbus) { + *file_id = SBUS_CHIP_ID; + } else { + if ((dl_fcode[0x23] == 0x22) || + (dl_fcode[0x23] == 0x23)) { + *file_id = dl_fcode[0x22] & 0xff; + *file_id |= (dl_fcode[0x23] << 8) & 0xff00; + } else { + *file_id = dl_fcode[0x42] & 0xff; + *file_id |= (dl_fcode[0x43] << 8) & 0xff00; + } + } + + /* + * Ok, we're just checking for 2200 here. If it is we need + * to offset to find the banner. + */ + if ((*file_id == 0x2200) || + (*file_id == 0x2300) || + (*file_id == 0x2312)) { + qlc_offset = -32; + } + + /* + * If this is an ISP2200 Sbus Fcode file, then search for the string + * "ISP2200 FC-AL Host Adapter Driver" in the whole fcode file + */ + if (isSbus) { + *file_id = SBUS_CHIP_ID; + qlc_offset = *sbus_offset; + /* Subtract 111 from the offset we add below for PCI Fcodes */ + qlc_offset -= 111; + } + + /* Banner length varies; grab banner to end of date marker yr/mo/da */ + version_file[0] = '\0'; + for (mark = (111 + qlc_offset); mark < (191 + qlc_offset); mark++) { + (void) strncpy(temp, (char *)&dl_fcode[mark], 4); + if ((strncmp(&temp[0], "/", 1) == 0) && + (strncmp(&temp[3], "/", 1) == 0)) { + (void) strncat((char *)version_file, + (char *)&dl_fcode[mark], 6); + break; + } + (void) strncat((char *)version_file, temp, 1); + } + return (0); +} + +/* + * Find if the FCode file is a ISP2200 SBUS Fcode file + */ +static int +q_findSbusfile(int fd, int *sbus_offset) +{ + static int file_size; + char *sbus_info; + struct stat statinfo; + + if (lseek(fd, 0, SEEK_SET) == -1) { + perror(MSGSTR(21022, "seek")); + return (-1); + } + if (fstat(fd, &statinfo)) { + perror(MSGSTR(21023, "fstat")); + return (-1); + } + file_size = statinfo.st_size; + + if ((sbus_info = (char *)malloc(file_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + return (-1); + } + + if (read(fd, sbus_info, file_size) < 0) { + perror(MSGSTR(21001, "read")); + free(sbus_info); + return (-1); + } + + /* + * Search for the version string in the whole file + */ + if ((*sbus_offset = memstrstr((char *)sbus_info, qlgc2200Sbus, + file_size, strlen(qlgc2200Sbus))) != -1) { + free(sbus_info); + return (1); + } else { + free(sbus_info); + return (0); + } +} + + +/* + * Build a list of all the devctl entries for all the 2100/2200 based adapters + */ +static int +q_getdevctlpath(char *devpath, int *devcnt) +{ + struct stat statbuf; + struct dirent *dirp = NULL; + DIR *dp = NULL; + char *ptr = NULL; + int err; + int testopen; + + if (lstat(devpath, &statbuf) < 0) { + (void) fprintf(stderr, + MSGSTR(21016, "Error: %s lstat() error\n"), devpath); + return (1); + } + + if ((strstr(devpath, fc_trans) || + (strstr(devpath, fp_trans_id) && strstr(devpath, fp_trans))) && + strstr(devpath, "devctl")) { + /* Verify the path is valid */ + if ((testopen = open(devpath, O_RDONLY)) >= 0) { + (void) close(testopen); + (void) strcpy(pcibus_list[*devcnt], devpath); + *devcnt += 1; + return (0); + } + } + + if (S_ISDIR(statbuf.st_mode) == 0) { + /* + * not a directory so + * we don't care about it - return + */ + return (0); + } + + /* + * It's a directory. Call ourself to + * traverse the path(s) + */ + ptr = devpath + strlen(devpath); + *ptr++ = '/'; + *ptr = 0; + + /* Forget the /devices/pseudo/ directory */ + if (strcmp(devpath, "/devices/pseudo/") == 0) { + return (0); + } + + if ((dp = opendir(devpath)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21017, "Error: %s Can't read directory\n"), devpath); + return (1); + } + + while ((dirp = readdir(dp)) != NULL) { + + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0) { + continue; + } + (void) strcpy(ptr, dirp->d_name); /* append name */ + err = q_getdevctlpath(devpath, devcnt); + } + + if (closedir(dp) < 0) { + (void) fprintf(stderr, + MSGSTR(21018, "Error: Can't close directory %s\n"), devpath); + return (1); + } + return (err); +} + +/* + * Get the boot device. Cannot load FCode to current boot device. + * Boot devices under volume management will prompt a warning. + */ +static int +q_getbootdev(uchar_t *bootpath) +{ + struct mnttab mp; + struct mnttab mpref; + FILE *fp = NULL; + static char buf[BUFSIZ]; + char *p = NULL, *p1 = NULL; /* p = full device, p1 = chunk to rm */ + char *slot = ":devctl"; + char *root = "/"; + + if ((fp = fopen(MNTTAB, "r")) == NULL) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), MNTTAB); + return (1); + } + + mntnull(&mpref); + mpref.mnt_mountp = (char *)root; + + if (getmntany(fp, &mp, &mpref) != 0 || + mpref.mnt_mountp == NULL) { + (void) fprintf(stderr, MSGSTR(21019, + "Error: Cannot get boot device, check %s.\n"), MNTTAB); + (void) fclose(fp); + return (1); + } + (void) fclose(fp); + + /* + * If we can't get a link, we may be dealing with a volume mgr + * so give a warning. If a colon is present, we likely have a + * non-local disk or cd-rom, so no warning is necessary. + * e.g. /devices/pci@1f,4000/scsi@3/sd@6,0:b (cdrom, no link) or + * storage-e4:/blah/blah remote boot server + */ + if (readlink(mp.mnt_special, buf, BUFSIZ) < 0) { + if (strstr(mp.mnt_special, ":") == NULL) { + (void) fprintf(stderr, MSGSTR(21020, + "\nWarning: Cannot read boot device link, check %s.\n"), MNTTAB); + (void) fprintf(stderr, MSGSTR(21021, + "Do not upgrade FCode on adapters controlling the boot device.\n")); + } + return (1); + } + /* + * Copy boot device path to bootpath. First remove leading + * path junk (../../..) then if it's an ifp device, chop off + * the disk and add the devctl to the end of the path. + */ + if (p = strstr(buf, "/devices")) { + if (strstr(buf, fc_trans) != NULL) { + p1 = strrchr(p, '/'); + *p1 = '\0'; + } + } + (void) strcpy((char *)bootpath, (char *)p); + if (p1) { + (void) strcat((char *)bootpath, slot); + } + return (0); +} + +/* + * Load FCode to card. + * uses ioctl: IFPIO_FCODE_DOWNLOAD + */ +static int +q_load_file(int fcode_fd, char *device) +{ + static int dev_fd, fcode_size; + struct stat stat; + ifp_download_t *download_p = NULL; + fcio_t fcio; + uint16_t file_id = 0; + uchar_t *bin; + + if (lseek(fcode_fd, 0, SEEK_SET) == -1) { + perror(MSGSTR(21022, "seek")); + (void) close(fcode_fd); + return (1); + } + if (fstat(fcode_fd, &stat) == -1) { + perror(MSGSTR(21023, "fstat")); + (void) close(fcode_fd); + return (1); + } + + fcode_size = stat.st_size; + + if (strstr(device, fc_trans)) { + if ((download_p = (ifp_download_t *)malloc( + sizeof (ifp_download_t) + fcode_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + (void) close(fcode_fd); + return (1); + } + } else { + if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + (void) close(fcode_fd); + return (1); + } + } + + if (strstr(device, fc_trans)) { + if (read(fcode_fd, download_p->dl_fcode, fcode_size) + != fcode_size) { + perror(MSGSTR(21001, "read")); + free(download_p); + (void) close(fcode_fd); + return (1); + } + } else { + if (read(fcode_fd, bin, fcode_size) + != fcode_size) { + perror(MSGSTR(21001, "read")); + free(bin); + (void) close(fcode_fd); + return (1); + } + } + + + if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) { + (void) fprintf(stderr, + MSGSTR(21000, "Error: Could not open %s\n"), device); + free(download_p); + return (1); + } + if (strstr(device, fc_trans)) { + download_p->dl_fcode_len = fcode_size; + file_id = download_p->dl_fcode[0x42] & 0xff; + file_id |= (download_p->dl_fcode[0x43] << 8) & 0xff00; + download_p->dl_chip_id = file_id; + if (ioctl(dev_fd, IFPIO_FCODE_DOWNLOAD, download_p) < 0) { + (void) fprintf(stderr, MSGSTR(21024, + "Error: Driver interface IFPIO_FCODE_DOWNLOAD failed\n")); + free(download_p); + (void) close(dev_fd); + return (1); + } + free(download_p); + } else if (strstr(device, fp_trans)) { + fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE; + /* Information read operation */ + fcio.fcio_xfer = FCIO_XFER_WRITE; + fcio.fcio_ibuf = (caddr_t)bin; + fcio.fcio_ilen = fcode_size; + + if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) { + (void) fprintf(stderr, MSGSTR(21036, + "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n")); + free(download_p); + (void) close(dev_fd); + return (1); + } + free(bin); + } + (void) close(dev_fd); + return (0); +} + +/* + * Issue warning strings and loop for Yes/No user interaction + * err# 0 -- we're ok, warn for pending FCode load + * 1 -- not in single user mode + * 2 -- can't get chip_id + * 3 -- card and file do not have same type (2100/2200) + */ +static int +q_warn(int errnum) +{ + char input[1024]; + input[0] = '\0'; + + if (errnum == 1) { + (void) fprintf(stderr, MSGSTR(21025, + "\nWarning: System is not in single-user mode.\n")); + (void) fprintf(stderr, MSGSTR(21026, + "Loading FCode will reset the adapter and terminate I/O activity\n")); + } else { + if (errnum == 2) { + (void) fprintf(stderr, MSGSTR(21027, + " Warning: FCode is missing or existing FCode has" + " unrecognized version.\n")); + return (1); + } else if (errnum == 3) { + (void) fprintf(stderr, MSGSTR(21028, + " Warning: New FCode file version does not match this" + " board type. Skipping...\n")); + return (1); + } + (void) fprintf(stderr, MSGSTR(21029, + "\nWARNING!! This program will update the FCode in this" + " FC100/PCI, ISP2200/PCI, ISP23xx/PCI " + " and Emulex devices.\n")); + (void) fprintf(stderr, MSGSTR(21030, + "This may take a few (5) minutes. Please be patient.\n")); + } + +loop1: + (void) fprintf(stderr, MSGSTR(21031, + "Do you wish to continue ? (y/n) ")); + + (void) gets(input); + + if ((strcmp(input, MSGSTR(21032, "y")) == 0) || + (strcmp(input, MSGSTR(40, "yes")) == 0)) { + return (0); + } else if ((strcmp(input, MSGSTR(21033, "n")) == 0) || + (strcmp(input, MSGSTR(45, "no")) == 0)) { + (void) fprintf(stderr, + MSGSTR(21034, "Not Downloading FCode\n")); + return (1); + } else { + (void) fprintf(stderr, MSGSTR(21035, "Invalid input\n")); + goto loop1; + } +} + +/* + * Name : memstrstr + * Input : pointer to buf1, pointer to buf2, size of buf1, size of buf2 + * Returns : + * Offset of the start of contents-of-buf2 in buf1 if it is found + * -1 if buf1 does not contain contents of buf2 + * Synopsis: + * This function works similar to strstr(). The difference is that null + * characters in the buffer are treated like any other character. So, buf1 + * and buf2 can have embedded null characters in them. + */ +static int +memstrstr(char *s1, char *s2, int size1, int size2) +{ + int count1, count2; + char *s1_ptr, *s2_ptr; + + count1 = size1; count2 = size2; + s1_ptr = s1; s2_ptr = s2; + + if ((size2 == 0)||(size1 == 0)) + return (-1); + + for (count1 = 0; count1 < (size1 - size2 + 1); count1++) { + if (*s1_ptr++ == *s2_ptr++) { + if (--count2 == 0) { + return (count1 - size2 + 1); + } + continue; + } + count2 = size2; + s2_ptr = s2; + } + + return (-1); +} + +/* + * generic fcode load file routine. given a file descriptor to a fcode file + * this routine will issue the FCIO_DOWNLOAD_FCODE ioctl to the given + * device. Any ioctl errors will be returned in fcio_errno + * + * Arguments: + * fcode_fd file descriptor to a fcode file + * device path to the device we will be downloading the fcode onto + * fcio_errno pointer to an int that will be used to return any errors + * back to the caller + * Retrurn Values: + * 0 successful download + * >0 otherwise + */ +static int +fcode_load_file(int fcode_fd, char *device, int *fcio_errno) +{ + + fcio_t fcio; + static int dev_fd, fcode_size; + uchar_t *bin; + struct stat stat; + + if (device == NULL || fcio_errno == NULL) { + return (FCODE_LOAD_FAILURE); + } + + *fcio_errno = 0; + if (lseek(fcode_fd, 0, SEEK_SET) == -1) { + perror(MSGSTR(21022, "seek")); + return (FCODE_LOAD_FAILURE); + } + + if (fstat(fcode_fd, &stat) == -1) { + perror(MSGSTR(21023, "fstat")); + return (FCODE_LOAD_FAILURE); + } + + fcode_size = stat.st_size; + + if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + return (FCODE_LOAD_FAILURE); + } + + if (read(fcode_fd, bin, fcode_size) + != fcode_size) { + perror(MSGSTR(21001, "read")); + free(bin); + return (FCODE_LOAD_FAILURE); + } + + if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) { + (void) fprintf(stderr, + MSGSTR(21122, "Error: Could not open %s, failed " + "with errno %d\n"), device, errno); + free(bin); + return (FCODE_LOAD_FAILURE); + } + + fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE; + fcio.fcio_xfer = FCIO_XFER_WRITE; + fcio.fcio_ibuf = (caddr_t)bin; + fcio.fcio_ilen = fcode_size; + + if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) { + (void) close(dev_fd); + *fcio_errno = fcio.fcio_errno; + free(bin); + return (FCODE_IOCTL_FAILURE); + } + + free(bin); + (void) close(dev_fd); + return (FCODE_SUCCESS); +} + +/* + * Searches for and updates the fcode for Emulex HBA cards + * args: FCode file; if NULL only the current FCode + * version is printed + */ + +int +emulex_update(char *file) +{ + + int fd, retval = 0; + int devcnt = 0; + uint_t state = 0, fflag = 0; + static uchar_t bootpath[PATH_MAX]; + int fcode_fd = -1; + static struct utmpx *utmpp = NULL; + di_node_t root; + di_node_t node, sib_node, count_node; + di_minor_t minor_node; + char phys_path[PATH_MAX], *path; + int errnum = 0, fcio_errno = 0; + static uchar_t prom_ver_data[MAXNAMELEN]; + static char ver_file[EMULEX_FCODE_VERSION_LENGTH]; + void (*sigint)(); + int prop_entries = -1; + int *port_data = NULL; + + if (file) { + /* set the fcode download flag */ + fflag++; + + /* check for a valid file */ + if ((fcode_fd = open(file, O_RDONLY)) < 0) { + (void) fprintf(stderr, + MSGSTR(21118, "Error: Could not open %s, failed " + "with errno %d\n"), file, errno); + return (1); + } + + /* check for single user mode */ + while ((utmpp = getutxent()) != NULL) { + if (strstr(utmpp->ut_line, "run-level") && + (strcmp(utmpp->ut_line, "run-level S") && + strcmp(utmpp->ut_line, "run-level 1"))) { + if (q_warn(1)) { + (void) endutxent(); + (void) close(fcode_fd); + return (1); + } + break; + } + } + (void) endutxent(); + + /* get bootpath */ + if (!q_getbootdev((uchar_t *)&bootpath[0]) && + getenv("_LUX_D_DEBUG") != NULL) { + (void) fprintf(stdout, " Bootpath: %s\n", bootpath); + } + } + + /* + * Download the Fcode to all the emulex cards found + */ + + /* Create a snapshot of the kernel device tree */ + if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { + (void) fprintf(stderr, MSGSTR(21114, + "Error: Could not get /devices path to " + "Emulex Devices.\n")); + retval++; + } + + /* point to first node which matches emulex driver */ + node = di_drv_first_node("emlxs", root); + + if (node == DI_NODE_NIL) { + /* + * Could not find any emulex cards + */ + (void) di_fini(root); + (void) fprintf(stderr, MSGSTR(21115, + "\n Found Path to %d Emulex Devices.\n"), devcnt); + retval++; + } else { + + count_node = node; + while (count_node != DI_NODE_NIL) { + state = di_state(count_node); + if ((state & DI_DRIVER_DETACHED) + != DI_DRIVER_DETACHED) { + devcnt++; + } + count_node = di_drv_next_node(count_node); + } + (void) fprintf(stdout, MSGSTR(21116, + "\n Found Path to %d Emulex Devices.\n"), devcnt); + } + + + /* + * Traverse device tree to find all emulex cards + */ + while (node != DI_NODE_NIL) { + + state = di_state(node); + if ((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) { + node = di_drv_next_node(node); + continue; + } + + sib_node = di_child_node(node); + while (sib_node != DI_NODE_NIL) { + + state = di_state(sib_node); + if ((state & DI_DRIVER_DETACHED) != + DI_DRIVER_DETACHED) { + + /* Found an attached node */ + prop_entries = di_prop_lookup_ints( + DDI_DEV_T_ANY, sib_node, + "port", &port_data); + if (prop_entries != -1) { + + /* Found a node with "port" property */ + minor_node = di_minor_next(sib_node, + DI_MINOR_NIL); + break; + } + } + sib_node = di_sibling_node(sib_node); + } + + if (sib_node == DI_NODE_NIL) { + return (1); + } + path = di_devfs_path(sib_node); + (void) strcpy(phys_path, "/devices"); + (void) strncat(phys_path, path, strlen(path)); + di_devfs_path_free(path); + + if (fflag && (strstr((char *)bootpath, + (char *)phys_path) != NULL)) { + (void) fprintf(stderr, + MSGSTR(21117, "Ignoring %s (bootpath)\n"), + phys_path); + node = di_drv_next_node(node); + continue; + } + + if (minor_node) { + (void) strncat(phys_path, ":", 1); + (void) strncat(phys_path, + di_minor_name(minor_node), + strlen(di_minor_name(minor_node))); + } + + (void) fprintf(stdout, + MSGSTR(21107, "\n Opening Device: %s\n"), + phys_path); + + /* Check if the device is valid */ + if ((fd = open(phys_path, O_RDWR)) < 0) { + (void) fprintf(stderr, + MSGSTR(21121, "Error: Could not open %s, failed " + "with errno %d\n"), phys_path, errno); + retval++; + node = di_drv_next_node(node); + continue; + } + + (void) close(fd); + + /* + * Check FCode version present on the adapter + * (at last boot) + */ + memset(prom_ver_data, 0, sizeof (prom_ver_data)); + if (emulex_fcodeversion(node, (uchar_t *)&prom_ver_data[0]) + == 0) { + errnum = 0; + if (strlen((char *)prom_ver_data) == 0) { + (void) fprintf(stdout, MSGSTR(21108, + " Detected FCode Version:\tNo version available for this FCode\n")); + } else { + (void) fprintf(stdout, MSGSTR(21109, + " Detected FCode Version:\t%s\n"), + prom_ver_data); + } + } else { + errnum = 2; /* can't get prom properties */ + retval++; + } + + if (fflag) { + + memset(ver_file, 0, sizeof (ver_file)); + if (emulex_fcode_reader(fcode_fd, "fcode-version", + ver_file, sizeof (ver_file)) == 0) { + (void) fprintf(stdout, MSGSTR(21110, + " New FCode Version:\t\t%s\n"), + ver_file); + } else { + di_fini(root); + (void) close(fcode_fd); + return (1); + } + + /* + * Load the New FCode + * Give warning if file doesn't appear to be correct + */ + if (!q_warn(errnum)) { + /* Disable user-interrupt Control-C */ + sigint = + (void (*)(int)) signal(SIGINT, SIG_IGN); + /* Load FCode */ + (void) fprintf(stdout, MSGSTR(21111, + " Loading FCode: %s\n"), file); + if (fcode_load_file(fcode_fd, phys_path, + &fcio_errno) == FCODE_SUCCESS) { + (void) fprintf(stdout, MSGSTR(21112, + " Successful FCode download: %s\n"), + phys_path); + } else { + handle_emulex_error(fcio_errno, + phys_path); + retval++; + } + + /* Restore SIGINT (user interrupt) setting */ + (void) signal(SIGINT, sigint); + } + } + + node = di_drv_next_node(node); + } + + di_fini(root); + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(125, "Complete\n")); + if (fcode_fd != -1) + (void) close(fcode_fd); + return (retval); + +} + +/* + * Retrieve the version from the card. + * uses PROM properties + */ +static int +emulex_fcodeversion(di_node_t node, uchar_t *ver) { + di_prom_prop_t promprop; + di_prom_handle_t ph; + char *promname; + uchar_t *ver_data = NULL; + int size, found = 0; + + /* check to make sure ver is not NULL */ + if (ver == NULL) { + return (1); + } + + if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) { + return (1); + } + + for (promprop = di_prom_prop_next(ph, node, + DI_PROM_PROP_NIL); + promprop != DI_PROM_PROP_NIL; + promprop = di_prom_prop_next(ph, node, promprop)) { + if (((promname = di_prom_prop_name( + promprop)) != NULL) && + (strcmp(promname, "fcode-version") == 0)) { + size = di_prom_prop_data(promprop, &ver_data); + (void) memset(ver, NULL, size); + (void) memcpy(ver, ver_data, size); + found = 1; + } + } + + if (found) { + return (0); + } else { + return (1); + } +} + +/* + * Retrieves information from the Emulex fcode + * + * Given a pattern, this routine will look for this pattern in the fcode + * file and if found will return the pattern value + * + * possible patterns are manufacturer and fcode-version + */ +int +emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value, + uint32_t pattern_value_size) { + int32_t i = 0; + uint32_t n = 0; + uint32_t b = 0; + char byte1; + char byte2; + char byte3; + char byte4; + char buffer1[EMULEX_READ_BUFFER_SIZE]; + char buffer2[EMULEX_READ_BUFFER_SIZE]; + uint32_t plen, image_size; + struct stat stat; + uchar_t *image; + + /* Check the arguments */ + if (!fcode_fd || !pattern_value || pattern_value_size < 8) { + return (1); + } + + if (fstat(fcode_fd, &stat) == -1) { + perror(MSGSTR(21023, "fstat")); + return (1); + } + image_size = stat.st_size; + if (image_size < 2) { + return (1); + } + if ((image = (uchar_t *)calloc(image_size, 1)) == NULL) { + (void) fprintf(stderr, + MSGSTR(21013, "Error: Memory allocation failed\n")); + return (1); + } + + /* Read the fcode image file */ + lseek(fcode_fd, 0, SEEK_SET); + read(fcode_fd, image, image_size); + + /* Initialize */ + bzero(buffer1, sizeof (buffer1)); + bzero(buffer2, sizeof (buffer2)); + /* Default pattern_value string */ + strcpy((char *)pattern_value, "<unknown>"); + plen = strlen(pattern); + n = 0; + b = 0; + i = 0; + + /* Search entire image for pattern string */ + while (i <= (image_size - 2)) { + /* Read next two bytes */ + byte1 = image[i++]; + byte2 = image[i++]; + + /* Check second byte first due to endianness */ + + /* Save byte in circular buffer */ + buffer1[b++] = byte2; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte2) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then + * exit loop + */ + if (n == plen) { + goto found; + } + } + + + /* Check first byte second due to endianness */ + /* Save byte in circular buffer */ + buffer1[b++] = byte1; + if (b == sizeof (buffer1)) { + b = 0; + } + /* Check byte for pattern match */ + if (pattern[n++] != byte1) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched + * then exit loop + */ + if (n == plen) { + goto found; + } + } + } + + /* Not found. Try again with different endianess */ + + /* Initialize */ + bzero(buffer1, sizeof (buffer1)); + bzero(buffer2, sizeof (buffer2)); + n = 0; + b = 0; + i = 0; + + /* Search entire 32bit endian image for pattern string */ + while (i <= (image_size - 4)) { + /* Read next four bytes */ + byte1 = image[i++]; + byte2 = image[i++]; + byte3 = image[i++]; + byte4 = image[i++]; + + /* Save byte in circular buffer */ + buffer1[b++] = byte4; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte4) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + + /* Save byte in circular buffer */ + buffer1[b++] = byte3; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte3) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + + /* Save byte in circular buffer */ + buffer1[b++] = byte2; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte2) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + + /* Save byte in circular buffer */ + buffer1[b++] = byte1; + if (b == sizeof (buffer1)) { + b = 0; + } + + /* Check byte for pattern match */ + if (pattern[n++] != byte1) { + /* If no match, then reset pattern */ + n = 0; + } else { + /* + * If complete pattern has been matched then exit loop + */ + if (n == plen) { + goto found; + } + } + } + + free(image); + return (1); + +found: + free(image); + + /* Align buffer and eliminate non-printable characters */ + for (i = 0; i < (sizeof (buffer1)-plen); i++) { + byte1 = buffer1[b++]; + if (b == sizeof (buffer1)) { + b = 0; + } + /* Zero any non-printable characters */ + if (byte1 >= 33 && byte1 <= 126) { + buffer2[i] = byte1; + } else { + buffer2[i] = 0; + } + } + + /* + * Scan backwards for first non-zero string. This will be the + * version string + */ + for (i = sizeof (buffer1)-plen-1; i >= 0; i--) { + if (buffer2[i] != 0) { + for (; i >= 0; i--) { + if (buffer2[i] == 0) { + i++; + strncpy((char *)pattern_value, + &buffer2[i], pattern_value_size); + break; + } + } + break; + } + } + return (0); +} + +/* + * error handling routine to handle emulex error conditions + */ +static void +handle_emulex_error(int fcio_errno, char *phys_path) { + if (fcio_errno == EMLX_IMAGE_BAD) { + fprintf(stderr, MSGSTR(21119, + "Error: Fcode download failed. " + "Bad fcode image.\n")); + } else if (fcio_errno == EMLX_IMAGE_INCOMPATIBLE) { + fprintf(stderr, MSGSTR(21120, + "Error: Fcode download failed. Fcode is not " + "compatible with card.\n")); + } else { + (void) fprintf(stderr, MSGSTR(21036, + "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n")); + (void) fprintf(stderr, + MSGSTR(21113, + "Error: FCode download failed: %s\n"), + phys_path); + } +} |
