#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"
#define FCODE_SUCCESS 0
#define FCODE_LOAD_FAILURE 1
#define FCODE_IOCTL_FAILURE 2
#define HBA_MAX 128
#define FCODE_HDR 200
#define MAX_RETRIES 3
#define MAX_WAIT_TIME 30
#define EMULEX_FCODE_VERSION_LENGTH 16
#define EMULEX_READ_BUFFER_SIZE 128
#define EMLX_ERRNO_START 0x100
#define EMLX_TEST_FAILED (EMLX_ERRNO_START + 0)
#define EMLX_IMAGE_BAD (EMLX_ERRNO_START + 1)
#define EMLX_IMAGE_INCOMPATIBLE (EMLX_ERRNO_START + 2)
#define EMLX_IMAGE_FAILED (EMLX_ERRNO_START + 3)
#define EMLX_OFFLINE_FAILED (EMLX_ERRNO_START + 4)
#define SBUS_CHIP_ID 0x1969
#define IVORY_BUS "/sbus@"
#define IVORY_DRVR "/SUNW,qlc@"
static char fc_trans[] = "SUNW,ifp";
static char fp_trans[] = "SUNW,qlc";
static char fp_trans_id[] = "fp@";
static char qlgc2100[] = "FC100/P";
static char qlgc2200[] = "ISP2200";
static char qlgc2300[] = "ISP2300";
static char qlgc2312[] = "ISP2312";
static char qlgc2200Sbus[] = "ISP2200 Sbus FC-AL Host Adapter Driver";
static char pcibus_list[HBA_MAX][PATH_MAX];
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 *);
static int emulex_fcodeversion(di_node_t, uchar_t *);
static void handle_emulex_error(int, char *);
int
q_qlgc_update(unsigned int verbose, char *file)
{
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)();
static struct utmpx *utmpp = NULL;
char *ptr1, *ptr2;
char phys_path[PATH_MAX];
static char port1[MAXNAMELEN] = { 0 };
static char port2[MAXNAMELEN] = { 0 };
if (file) {
fflag++;
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);
}
isSbus = q_findSbusfile(fcode_fd, &sbus_off);
if (isSbus == -1) {
(void) close(fcode_fd);
return (1);
}
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);
}
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();
if (!q_getbootdev((uchar_t *)&bootpath[0]) &&
getenv("_LUX_D_DEBUG") != NULL) {
(void) fprintf(stdout, " Bootpath: %s\n", bootpath);
}
}
(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]);
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);
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) {
(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;
}
}
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);
}
if (chip_id == 0) {
errnum = 2;
retval++;
} else if (chip_id - file_id != 0) {
errnum = 3;
retval++;
} else {
errnum = 0;
}
if (!q_warn(errnum)) {
sigint =
(void (*)(int)) signal(SIGINT, SIG_IGN);
(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++;
}
(void) signal(SIGINT, sigint);
}
}
}
(void) fprintf(stdout, " ");
(void) fprintf(stdout, MSGSTR(125, "Complete\n"));
if (fcode_fd != -1)
(void) close(fcode_fd);
return (retval);
}
static int
q_findversion(int verbose, int index, uchar_t *version, uint16_t *chip_id)
{
int fd = -1, ntries;
struct ifp_fm_version *version_buffer = NULL;
char prom_ver[100] = { 0 };
char mcode_ver[100] = { 0 };
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';
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)) {
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);
}
bzero(version, sizeof (version));
fcio.fcio_cmd = FCIO_GET_FCODE_REV;
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)) {
(void) sleep(MAX_WAIT_TIME);
continue;
}
(void) close(fd);
return (L_FCIO_GET_FCODE_REV_FAIL);
}
break;
}
version[MAXNAMELEN-1] = '\0';
}
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);
}
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] = { 0 };
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;
}
}
if ((*file_id == 0x2200) ||
(*file_id == 0x2300) ||
(*file_id == 0x2312)) {
qlc_offset = -32;
}
if (isSbus) {
*file_id = SBUS_CHIP_ID;
qlc_offset = *sbus_offset;
qlc_offset -= 111;
}
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);
}
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);
}
if ((*sbus_offset = memstrstr((char *)sbus_info, qlgc2200Sbus,
file_size, strlen(qlgc2200Sbus))) != -1) {
free(sbus_info);
return (1);
} else {
free(sbus_info);
return (0);
}
}
static int
q_getdevctlpath(char *devpath, int *devcnt)
{
struct stat statbuf;
struct dirent *dirp = NULL;
DIR *dp = NULL;
char *ptr = NULL;
int err = 0;
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")) {
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) {
return (0);
}
ptr = devpath + strlen(devpath);
*ptr++ = '/';
*ptr = 0;
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);
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);
}
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;
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 (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);
}
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);
}
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 = NULL;
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;
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);
}
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;
}
}
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);
}
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);
}
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) {
fflag++;
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);
}
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();
if (!q_getbootdev((uchar_t *)&bootpath[0]) &&
getenv("_LUX_D_DEBUG") != NULL) {
(void) fprintf(stdout, " Bootpath: %s\n", bootpath);
}
}
if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
(void) fprintf(stderr, MSGSTR(21114,
"Error: Could not get /devices path to "
"Emulex Devices.\n"));
retval++;
}
node = di_drv_first_node("emlxs", root);
if (node == DI_NODE_NIL) {
(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) {
sib_node = di_child_node(count_node);
while (sib_node != DI_NODE_NIL) {
state = di_state(sib_node);
if ((state & DI_DRIVER_DETACHED) !=
DI_DRIVER_DETACHED) {
prop_entries =
di_prop_lookup_ints(
DDI_DEV_T_ANY, sib_node,
"port", &port_data);
if (prop_entries != -1) {
devcnt++;
break;
}
}
sib_node = di_sibling_node(sib_node);
}
}
count_node = di_drv_next_node(count_node);
}
(void) fprintf(stdout, MSGSTR(21116,
"\n Found Path to %d Emulex Devices.\n"), devcnt);
}
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) {
prop_entries = di_prop_lookup_ints(
DDI_DEV_T_ANY, sib_node,
"port", &port_data);
if (prop_entries != -1) {
minor_node = di_minor_next(sib_node,
DI_MINOR_NIL);
break;
}
}
sib_node = di_sibling_node(sib_node);
}
if (sib_node == DI_NODE_NIL) {
goto try_next;
}
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);
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);
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;
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);
}
if (!q_warn(errnum)) {
sigint =
(void (*)(int)) signal(SIGINT, SIG_IGN);
(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++;
}
(void) signal(SIGINT, sigint);
}
}
try_next:
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);
}
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;
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, 0, size);
(void) memcpy(ver, ver_data, size);
found = 1;
}
}
if (found) {
return (0);
} else {
return (1);
}
}
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;
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);
}
lseek(fcode_fd, 0, SEEK_SET);
read(fcode_fd, image, image_size);
bzero(buffer1, sizeof (buffer1));
bzero(buffer2, sizeof (buffer2));
strcpy((char *)pattern_value, "<unknown>");
plen = strlen(pattern);
n = 0;
b = 0;
i = 0;
while (i <= (image_size - 2)) {
byte1 = image[i++];
byte2 = image[i++];
buffer1[b++] = byte2;
if (b == sizeof (buffer1)) {
b = 0;
}
if (pattern[n++] != byte2) {
n = 0;
} else {
if (n == plen) {
goto found;
}
}
buffer1[b++] = byte1;
if (b == sizeof (buffer1)) {
b = 0;
}
if (pattern[n++] != byte1) {
n = 0;
} else {
if (n == plen) {
goto found;
}
}
}
bzero(buffer1, sizeof (buffer1));
bzero(buffer2, sizeof (buffer2));
n = 0;
b = 0;
i = 0;
while (i <= (image_size - 4)) {
byte1 = image[i++];
byte2 = image[i++];
byte3 = image[i++];
byte4 = image[i++];
buffer1[b++] = byte4;
if (b == sizeof (buffer1)) {
b = 0;
}
if (pattern[n++] != byte4) {
n = 0;
} else {
if (n == plen) {
goto found;
}
}
buffer1[b++] = byte3;
if (b == sizeof (buffer1)) {
b = 0;
}
if (pattern[n++] != byte3) {
n = 0;
} else {
if (n == plen) {
goto found;
}
}
buffer1[b++] = byte2;
if (b == sizeof (buffer1)) {
b = 0;
}
if (pattern[n++] != byte2) {
n = 0;
} else {
if (n == plen) {
goto found;
}
}
buffer1[b++] = byte1;
if (b == sizeof (buffer1)) {
b = 0;
}
if (pattern[n++] != byte1) {
n = 0;
} else {
if (n == plen) {
goto found;
}
}
}
free(image);
return (1);
found:
free(image);
for (i = 0; i < (sizeof (buffer1)-plen); i++) {
byte1 = buffer1[b++];
if (b == sizeof (buffer1)) {
b = 0;
}
if (byte1 >= 33 && byte1 <= 126) {
buffer2[i] = byte1;
} else {
buffer2[i] = 0;
}
}
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);
}
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);
}
}