/*
* 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
* 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 2009 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 <limits.h>
#include <signal.h>
#include <dirent.h>
#include <nl_types.h>
#include <utmpx.h>
#include <stgcom.h>
#include <libdevinfo.h>
#include "luxadm.h"
/* Error codes - used by the fcode_load_file routine */
/*
* EMULEX Fcode attributes
*/
/* Emulex specific error codes */
/* Diagnostic error codes */
/* Download image contains bad data */
/* Download image not compatible with current hardware */
/* Unable to take adapter offline */
/* Image download failed */
/*
* 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.
*/
/* Global variables */
/*
* The variable qlgc2200Sbus represents the string which is always the
* starting string of the version information in an ISP2200 Sbus Fcode.
*/
/* 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_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 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
/*ARGSUSED*/
{
int sbus_off;
void (*sigint)(); /* to store default SIGTERM setting */
/*
* The variables port1 and port2 are used to store the bus id
* e.g. the bus id for this path:
* is "sbus@12". They are initialized to a random value and are
* set such that they are not equal initially.
*/
if (file) {
fflag++;
/* check for a valid file */
return (1);
}
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
*/
if (isSbus == -1) {
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
*/
(isSbus))) {
"Error: %s is not a valid FC100/P, "
"ISP2200, ISP23xx FCode file.\n"),
file);
return (1);
}
/* check for single user mode */
if (q_warn(1)) {
(void) endutxent();
return (1);
}
break;
}
}
(void) endutxent();
/* get bootpath */
}
}
/*
* Get count of, and names of PCI slots with ifp device control
* (devctl) nodes. Search /devices.
*/
"\n Found Path to %d FC100/P, ISP2200, ISP23xx Devices\n"),
devcnt);
} else {
"Error: Could not get /devices path to FC100/P,"
"ISP2200, ISP23xx Cards.\n"));
retval++;
}
for (i = 0; i < devcnt; i++) {
strlen(&pcibus_list[i][0]));
&pcibus_list[i][0]);
continue;
}
/* Check if the device is valid */
&pcibus_list[i][0]);
retval++;
continue;
}
/*
* Check FCode version present on the adapter (at last boot)
*/
&chip_id) == 0) {
" Detected FCode Version:\tNo version available for this FCode\n"));
} else {
" 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.
*/
*ptr2 = '\0';
}
}
"/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)
*/
(q_findfileversion((char *)
" New FCode Version:\t\t%s\n"),
} else {
return (1);
}
/*
* Load the New FCode
* Give warning if file doesn't appear to be correct
*
*/
if (chip_id == 0) {
retval++;
retval++;
} else {
errnum = 0; /* everything is ok */
}
/* Disable user-interrupt Control-C */
sigint =
/* Load FCode */
" Loading FCode: %s\n"), file);
if (q_load_file(fcode_fd,
&pcibus_list[i][0]) == 0) {
" Successful FCode download: %s\n"),
&pcibus_list[i][0]);
} else {
"Error: FCode download failed: %s\n"),
&pcibus_list[i][0]);
retval++;
}
/* Restore SIGINT (user interrupt) setting */
}
}
}
if (fcode_fd != -1)
return (retval);
}
/*
* Retrieve the version banner from the card
* uses ioctl: FCIO_FCODE_MCODE_VERSION FCode revision
*/
static int
/*ARGSUSED*/
{
&pcibus_list[index][0]);
return (1);
}
sizeof (struct ifp_fm_version))) == NULL) {
return (1);
}
"Error: Driver interface FCIO_FCODE_MCODE_VERSION failed\n"));
return (1);
}
/* Need a way to get card MCODE (firmware) to track certain HW bugs */
}
/*
* 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).
*/
&pcibus_list[index][0]);
return (1);
}
/* Get the fcode version */
/* Information read operation */
/* wait 30 secs */
(void) sleep(MAX_WAIT_TIME);
continue;
}
return (L_FCIO_GET_FCODE_REV_FAIL);
}
break;
}
}
/* Get type of card from product name in FCode version banner */
*chip_id = 0x2100;
*chip_id = 0x2200;
*chip_id = SBUS_CHIP_ID;
}
*chip_id = 0x2300;
*chip_id = 0x2312;
} else {
*chip_id = 0x0;
}
return (0);
}
/*
* Retrieve the version banner and file type (2100 or 2200) from the file
*/
static int
int isSbus, int *sbus_offset)
{
int mark;
int qlc_offset = 0;
/*
* Get file version from FCode for 2100 or 2202
*/
if (isSbus) {
*file_id = SBUS_CHIP_ID;
} else {
} else {
}
}
/*
* 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;
/* Subtract 111 from the offset we add below for PCI Fcodes */
qlc_offset -= 111;
}
version_file[0] = '\0';
(void) strncat((char *)version_file,
break;
}
}
return (0);
}
/*
* Find if the FCode file is a ISP2200 SBUS Fcode file
*/
static int
{
static int file_size;
char *sbus_info;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
/*
* Search for the version string in the whole file
*/
return (1);
} else {
return (0);
}
}
/*
* Build a list of all the devctl entries for all the 2100/2200 based adapters
*/
static int
{
int err = 0;
int testopen;
return (1);
}
/* Verify the path is valid */
*devcnt += 1;
return (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++ = '/';
*ptr = 0;
return (0);
}
return (1);
}
continue;
}
}
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
{
return (1);
}
"Error: Cannot get boot device, check %s.\n"), MNTTAB);
return (1);
}
/*
* 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.
*/
"\nWarning: Cannot read boot device link, check %s.\n"), MNTTAB);
"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.
*/
*p1 = '\0';
}
}
if (p1) {
}
return (0);
}
/*
* Load FCode to card.
* uses ioctl: IFPIO_FCODE_DOWNLOAD
*/
static int
{
return (1);
}
return (1);
}
return (1);
}
} else {
return (1);
}
}
!= fcode_size) {
return (1);
}
} else {
!= fcode_size) {
return (1);
}
}
return (1);
}
"Error: Driver interface IFPIO_FCODE_DOWNLOAD failed\n"));
return (1);
}
/* Information read operation */
"Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n"));
return (1);
}
}
return (0);
}
/*
* 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
{
input[0] = '\0';
if (errnum == 1) {
"\nWarning: System is not in single-user mode.\n"));
"Loading FCode will reset the adapter and terminate I/O activity\n"));
} else {
if (errnum == 2) {
" Warning: FCode is missing or existing FCode has"
" unrecognized version.\n"));
return (1);
} else if (errnum == 3) {
" Warning: New FCode file version does not match this"
" board type. Skipping...\n"));
return (1);
}
"\nWARNING!! This program will update the FCode in this"
" and Emulex devices.\n"));
"This may take a few (5) minutes. Please be patient.\n"));
}
"Do you wish to continue ? (y/n) "));
return (0);
return (1);
} else {
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
{
return (-1);
if (--count2 == 0) {
}
continue;
}
}
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
{
return (FCODE_LOAD_FAILURE);
}
*fcio_errno = 0;
return (FCODE_LOAD_FAILURE);
}
return (FCODE_LOAD_FAILURE);
}
return (FCODE_LOAD_FAILURE);
}
!= fcode_size) {
return (FCODE_LOAD_FAILURE);
}
return (FCODE_LOAD_FAILURE);
}
return (FCODE_IOCTL_FAILURE);
}
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
{
int devcnt = 0;
void (*sigint)();
if (file) {
/* set the fcode download flag */
fflag++;
/* check for a valid file */
return (1);
}
/* check for single user mode */
if (q_warn(1)) {
(void) endutxent();
return (1);
}
break;
}
}
(void) endutxent();
/* get bootpath */
}
}
/*
* Download the Fcode to all the emulex cards found
*/
/* Create a snapshot of the kernel device tree */
"Error: Could not get /devices path to "
"Emulex Devices.\n"));
retval++;
}
/* point to first node which matches emulex driver */
if (node == DI_NODE_NIL) {
/*
* Could not find any emulex cards
*/
"\n Found Path to %d Emulex Devices.\n"), devcnt);
retval++;
} else {
count_node = node;
while (count_node != DI_NODE_NIL) {
if ((state & DI_DRIVER_DETACHED)
!= DI_DRIVER_DETACHED) {
while (sib_node != DI_NODE_NIL) {
if ((state & DI_DRIVER_DETACHED) !=
/* Found an attached node */
"port", &port_data);
if (prop_entries != -1) {
devcnt++;
break;
}
}
}
}
}
"\n Found Path to %d Emulex Devices.\n"), devcnt);
}
/*
* Traverse device tree to find all emulex cards
*/
while (node != DI_NODE_NIL) {
continue;
}
while (sib_node != DI_NODE_NIL) {
if ((state & DI_DRIVER_DETACHED) !=
/* Found an attached node */
"port", &port_data);
if (prop_entries != -1) {
/* Found a node with "port" property */
break;
}
}
}
if (sib_node == DI_NODE_NIL) {
goto try_next;
}
continue;
}
if (minor_node) {
}
/* Check if the device is valid */
retval++;
continue;
}
/*
* Check FCode version present on the adapter
* (at last boot)
*/
== 0) {
errnum = 0;
if (strlen((char *)prom_ver_data) == 0) {
" Detected FCode Version:\tNo version available for this FCode\n"));
} else {
" Detected FCode Version:\t%s\n"),
}
} else {
retval++;
}
if (fflag) {
" New FCode Version:\t\t%s\n"),
ver_file);
} else {
return (1);
}
/*
* Load the New FCode
* Give warning if file doesn't appear to be correct
*/
/* Disable user-interrupt Control-C */
sigint =
/* Load FCode */
" Loading FCode: %s\n"), file);
&fcio_errno) == FCODE_SUCCESS) {
" Successful FCode download: %s\n"),
} else {
retval++;
}
/* Restore SIGINT (user interrupt) setting */
}
}
}
if (fcode_fd != -1)
return (retval);
}
/*
* Retrieve the version from the card.
* uses PROM properties
*/
static int
char *promname;
/* check to make sure ver is not NULL */
return (1);
}
return (1);
}
if (((promname = di_prom_prop_name(
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
int32_t i = 0;
uint32_t n = 0;
uint32_t b = 0;
char byte1;
char byte2;
char byte3;
char byte4;
/* Check the arguments */
return (1);
}
return (1);
}
if (image_size < 2) {
return (1);
}
return (1);
}
/* Read the fcode image file */
/* Initialize */
/* Default pattern_value string */
n = 0;
b = 0;
i = 0;
/* Search entire image for pattern string */
while (i <= (image_size - 2)) {
/* Read next two bytes */
/* Check second byte first due to endianness */
/* Save byte in circular buffer */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Check byte for pattern match */
/* 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 */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Check byte for pattern match */
/* 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 */
n = 0;
b = 0;
i = 0;
/* Search entire 32bit endian image for pattern string */
while (i <= (image_size - 4)) {
/* Read next four bytes */
/* Save byte in circular buffer */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Check byte for pattern match */
/* 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 */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Check byte for pattern match */
/* 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 */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Check byte for pattern match */
/* 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 */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Check byte for pattern match */
/* If no match, then reset pattern */
n = 0;
} else {
/*
* If complete pattern has been matched then exit loop
*/
if (n == plen) {
goto found;
}
}
}
return (1);
/* Align buffer and eliminate non-printable characters */
if (b == sizeof (buffer1)) {
b = 0;
}
/* Zero any non-printable characters */
} else {
buffer2[i] = 0;
}
}
/*
* Scan backwards for first non-zero string. This will be the
* version string
*/
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
if (fcio_errno == EMLX_IMAGE_BAD) {
"Error: Fcode download failed. "
"Bad fcode image.\n"));
} else if (fcio_errno == EMLX_IMAGE_INCOMPATIBLE) {
"Error: Fcode download failed. Fcode is not "
"compatible with card.\n"));
} else {
"Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n"));
MSGSTR(21113,
"Error: FCode download failed: %s\n"),
}
}