/*
* 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
*/
/*
*/
/*
* This file contains various support routines.
*/
/*
* SAS Topology Configuration
*/
/*
* Check current firmware version for correctness
* and try to flash the correct firmware if what is
* running isn't correct.
*
* Must be called after setup and MPI setup and
* interrupts are enabled.
*/
int
{
int errno;
int defret = 0;
/*
* If updating is disabled, we're done.
*/
if (pwp->fw_disable_update) {
"Firmware update disabled by conf file");
return (0);
}
/*
* If we're already running the right firmware, we're done.
*/
if (pwp->fw_force_update == 0) {
return (0);
}
"Firmware version matches, but still forcing update");
}
if (errno) {
"%s: Firmware module not available; will not upgrade",
__func__);
return (defret);
}
if (errno) {
"%s: unable to find symbol '%s'",
(void) ddi_modclose(modhp);
return (defret);
}
/*
* If the firmware version from the module isn't what we expect,
* and force updating is disabled, return the default (for this
* mode of operation) value.
*/
if (*fwvp != PMCS_FIRMWARE_VERSION) {
if (pwp->fw_force_update == 0) {
"%s: firmware module version wrong (0x%x)",
(void) ddi_modclose(modhp);
return (defret);
}
"%s: firmware module version wrong (0x%x) - update forced",
}
if (errno) {
(void) ddi_modclose(modhp);
return (defret);
}
if (errno) {
(void) ddi_modclose(modhp);
return (defret);
}
if (errno) {
(void) ddi_modclose(modhp);
return (defret);
}
if (errno) {
(void) ddi_modclose(modhp);
return (defret);
}
if (errno) {
(void) ddi_modclose(modhp);
return (defret);
}
if (errno) {
(void) ddi_modclose(modhp);
return (defret);
}
/*
* Get the ILA and firmware versions from the modules themselves
*/
/*
* If force update is not set, verify that what we're loading is
* what we expect.
*/
if (pwp->fw_force_update == 0) {
if (fw_version != PMCS_FIRMWARE_VERSION) {
"Expected fw version 0x%x, not 0x%lx: not "
(void) ddi_modclose(modhp);
return (defret);
}
}
"Upgrading firmware on card from 0x%x to 0x%lx (ILA version 0x%lx)",
/*
* The SPCBoot image must be updated first, and this is written to
* SEEPROM, not flash.
*/
"%s: unable to flash '%s' segment",
(void) ddi_modclose(modhp);
return (-1);
}
"%s: Beginning firmware update of %s image.",
"%s: unable to flash '%s' segment",
(void) ddi_modclose(modhp);
return (-1);
}
"%s: unable to flash '%s' segment",
(void) ddi_modclose(modhp);
return (-1);
}
"%s: soft reset after flash update failed", __func__);
(void) ddi_modclose(modhp);
return (-1);
} else {
"%s: %s image successfully upgraded.",
}
if (first_pass) {
first_pass = 0;
goto repeat;
}
"%s: Firmware successfully upgraded", __func__);
(void) ddi_modclose(modhp);
return (0);
}
/*
* Flash firmware support
* Called unlocked.
*/
int
{
/*
* Step 1- Validate firmware chunks within passed pointer.
*/
for (;;) {
"%s: partition 0x%x, Length 0x%x", __func__,
"%s: bad firmware length 0x%x",
return (EINVAL);
}
break;
}
"%s: out of bounds firmware length", __func__);
return (EINVAL);
}
}
/*
* Step 2- acquire scratch
*/
/*
* Step 3- loop through firmware chunks and send each one
* down to be flashed.
*/
for (;;) {
return (EIO);
}
break;
}
}
return (0);
}
static int
{
}
"%s: segment %d offset %u length %u",
return (ENOMEM);
}
if (off == 0) {
} else {
msg[4] = 0;
}
msg[5] = 0;
msg[6] = 0;
msg[7] = 0;
msg[8] = 0;
msg[9] = 0;
msg[10] = 0;
msg[11] = 0;
msg[15] = 0;
return (ENOMEM);
}
if (result) {
return (EIO);
}
"%s: segment %d complete pending reboot",
break;
case FLASH_UPDATE_IN_PROGRESS:
break;
case FLASH_UPDATE_HDR_ERR:
return (EIO);
case FLASH_UPDATE_OFFSET_ERR:
return (EIO);
return (EIO);
case FLASH_UPDATE_LENGTH_ERR:
return (EIO);
case FLASH_UPDATE_HW_ERR:
return (EIO);
"%s: segment %d download not supported error",
return (EIO);
case FLASH_UPDATE_DISABLED:
"%s: segment %d update disabled error",
return (EIO);
default:
"%s: segment %d unknown error %x",
return (EIO);
}
seg++;
}
return (0);
}
/*
* pmcs_validate_vpd
*
* Input: softstate pointer and pointer to vpd data buffer
* Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
*/
static boolean_t
{
/*
* Make sure we understand the format of this data
*/
/*
* Only VPD version 1 is VALID for Thebe-INT cards and
* Only VPD version 2 is valid for Thebe-EXT cards
*/
goto valid_version;
} else {
"%s: Detected Thebe card with SSID(%02x%02x)", __func__,
"%s: EEPROM(%d) unsupported; requires %d for INT(%02x%02x) "
" and %d for EXT(%02x%02x) cards.", __func__,
return (B_FALSE);
}
/*
* Do we have a valid SAS WWID?
*/
"%s: SAS WWN has invalid NAA (%d)", __func__,
return (B_FALSE);
}
}
"%s: Didn't see VPD start byte", __func__);
return (B_FALSE);
}
/*
* We only checksum the VPD data between (and including) VPD Start byte
* and the checksum value byte. The length of this data for CRC is
* 15 less than the length indicated in vpd_length field of the header.
* 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
* 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
*/
/*
* VPD length (little endian format) is represented as byte-array field
* & read the following way to avoid alignment issues (in SPARC)
*/
/* Validate VPD data checksum */
}
if (chksum) {
"%s: VPD checksum failure", __func__);
return (B_FALSE);
}
/*
* Get length of string ID tag and read it.
*/
/*
* String ID tag length (little endian format) is represented as
* byte-array & read the following way to avoid alignment issues
* (in SPARC)
*/
(vpd_header->strid_length[0]);
if (strid_length > 79) {
strid_length = 79;
}
tbuf[strid_length] = 0;
/*
* Skip VPD-R tag and length of read-only tag, then start reading
*/
tbuf[0] = 0;
}
/* Keyword is Manufacturer */
}
/* Keyword is Serial Number */
}
}
return (B_TRUE);
}
/*
* pmcs_get_nvmd
*
* This function will read the requested data from the non-volatile
* storage on the card. This could mean SEEPROM, VPD, or other areas
* as defined by the PM8001 programmer's manual.
*
* nvmd_type: The data type being requested
* offset: Must be 4K alignment
* buf: Pointer to memory region for retrieved data
* size_left: Total available bytes left in buf
*
* Returns: non-negative on success, -1 on failure
*/
/*ARGSUSED*/
int
{
switch (nvmd_type) {
case PMCS_NVMD_VPD:
doa[0] = 0;
doa[1] = 0;
doa[2] = 0;
break;
case PMCS_NVMD_REG_DUMP:
tda = 0;
tbn_tdps = 0;
break;
case PMCS_NVMD_EVENT_LOG:
tda = 0;
tbn_tdps = 0;
break;
default:
return (-1);
}
"%s: Unable to get work struct", __func__);
return (-1);
}
/*
* ptr will now point to the inbound queue message
*/
"!%s: Unable to get IQ entry", __func__);
return (-1);
}
if (result) {
return (-1);
}
if (status != PMCS_NVMD_STAT_SUCCESS) {
return (-1);
}
}
switch (nvmd) {
case PMCIN_NVMD_VPD:
result = 0;
} else {
result = -1;
}
break;
case PMCIN_NVMD_AAP1:
case PMCIN_NVMD_IOP:
i = 0;
if (nvmd_type == PMCS_NVMD_REG_DUMP) {
while ((i < PMCS_FLASH_CHUNK_SIZE) &&
"%c", chunkp[i]);
i++;
}
} else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
}
result = i;
break;
default:
"UNKNOWN NVMD DEVICE");
return (-1);
}
return (result);
}
/*
* pmcs_set_nvmd
*
* This function will write the requested data to non-volatile storage
* on the HBA. This could mean SEEPROM, VPD, or other areas as defined by
* the PM8001 programmer's manual.
*
* nvmd_type: The data type to be written
* buf: Pointer to memory region for data to write
* len: Length of the data buffer
*
* Returns: B_TRUE on success, B_FALSE on failure
*/
{
int result;
switch (nvmd_type) {
case PMCS_NVMD_SPCBOOT:
(len <= PMCS_SPCBOOT_MAX_SIZE));
break;
default:
return (B_FALSE);
}
"%s: Unable to get work struct", __func__);
return (B_FALSE);
}
"PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
}
/*
* ptr will now point to the inbound queue message
*/
"!%s: Unable to get IQ entry", __func__);
return (B_FALSE);
}
if (result) {
return (B_FALSE);
}
if (status != PMCS_NVMD_STAT_SUCCESS) {
return (B_FALSE);
}
return (B_TRUE);
}