root/usr/src/uts/common/io/fibre-channel/fca/emlxs/emlxs_download.c
/*
 * 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
 * http://www.opensource.org/licenses/cddl1.txt.
 * 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 (c) 2004-2012 Emulex. All rights reserved.
 * Use is subject to license terms.
 */

#include <emlxs.h>

/* Required for EMLXS_CONTEXT in EMLXS_MSGF calls */
EMLXS_MSG_DEF(EMLXS_DOWNLOAD_C);

#define MAX_BOOTID      10

static uint32_t emlxs_erase_fcode_flash(emlxs_hba_t *hba);

static uint32_t emlxs_write_fcode_flash(emlxs_hba_t *hba,
                        PIMAGE_HDR ImageHdr, caddr_t Buffer);

static int32_t  emlxs_build_parms(caddr_t Buffer, PWAKE_UP_PARMS AbsWakeUpParms,
                        uint32_t BufferSize, PAIF_HDR AifHeader);
static uint32_t emlxs_validate_image(emlxs_hba_t *hba, caddr_t Buffer,
                        uint32_t Size, emlxs_fw_image_t *fw_image);
static void     emlxs_format_dump(emlxs_hba_t *hba, MAILBOXQ *mbq,
                        uint32_t Type, uint32_t RegionId, uint32_t WordCnt,
                        uint32_t BaseAddr);
static uint32_t emlxs_start_abs_download(emlxs_hba_t *hba, PAIF_HDR AifHdr,
                        caddr_t Buffer, uint32_t len,
                        PWAKE_UP_PARMS WakeUpParms);
static uint32_t emlxs_start_abs_download_2mb(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t len, uint32_t offline,
                        emlxs_fw_image_t *fw_image);
static uint32_t emlxs_proc_abs_2mb(emlxs_hba_t *hba,
                        caddr_t EntireBuffer, uint32_t FileType,
                        uint32_t extType);
static void     emlxs_format_load_area_cmd(MAILBOXQ *mbq, uint32_t Base,
                        uint32_t DlByteCount, uint32_t Function,
                        uint32_t Complete, uint32_t DataOffset, uint32_t AreaId,
                        uint8_t MbxCmd, uint32_t StepCmd);
static uint32_t emlxs_build_parms_2mb_bwc(emlxs_hba_t *hba, PAIF_HDR AifHdr,
                        uint32_t extType, PWAKE_UP_PARMS AbsWakeUpParms);
static uint32_t emlxs_update_exp_rom(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms);
extern uint32_t emlxs_get_max_sram(emlxs_hba_t *hba, uint32_t *MaxRbusSize,
                        uint32_t *MaxIbusSize);
static void     emlxs_format_prog_flash(MAILBOXQ *mbq, uint32_t Base,
                        uint32_t DlByteCount, uint32_t Function,
                        uint32_t Complete, uint32_t BdeAddress,
                        uint32_t BdeSize, PROG_ID *ProgId, uint32_t keep);
static void     emlxs_format_update_parms(MAILBOXQ *mbq,
                        PWAKE_UP_PARMS WakeUpParms);
static void     emlxs_format_update_pci_cfg(emlxs_hba_t *hba, MAILBOXQ *mbq,
                        uint32_t region_id, uint32_t size);
static uint32_t emlxs_update_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS AbsWakeUpParms,
                        PWAKE_UP_PARMS WakeUpParms);
static uint32_t emlxs_update_boot_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms, PROG_ID *id,
                        uint32_t proc_erom);
static uint32_t emlxs_update_ff_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms, PROG_ID *id);
static uint32_t emlxs_update_sli1_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms, PROG_ID *id);
static uint32_t emlxs_update_sli2_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms, PROG_ID *id);
static uint32_t emlxs_update_sli3_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms, PROG_ID *id);
static uint32_t emlxs_update_sli4_wakeup_parms(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS WakeUpParms, PROG_ID *id);
static uint32_t emlxs_start_rel_download(emlxs_hba_t *hba, PIMAGE_HDR ImageHdr,
                        caddr_t Buffer, PWAKE_UP_PARMS WakeUpParms,
                        uint32_t dwc_flag);
static uint32_t emlxs_read_load_list(emlxs_hba_t *hba, LOAD_LIST *LoadList);

static uint32_t emlxs_valid_cksum(uint32_t *StartAddr, uint32_t *EndAddr);

static void     emlxs_disp_aif_header(emlxs_hba_t *hba, PAIF_HDR AifHdr);

static void     emlxs_dump_image_header(emlxs_hba_t *hba, PIMAGE_HDR image);


static uint32_t emlxs_type_check(uint32_t type);

static uint32_t emlxs_kern_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_stub_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_sli1_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_sli2_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_sli3_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_sli4_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_bios_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_sbus_fcode_check(emlxs_hba_t *hba, uint32_t version);

static uint32_t emlxs_validate_version(emlxs_hba_t *hba,
                        emlxs_fw_file_t *file, uint32_t id, uint32_t type,
                        char *file_type);
static uint32_t emlxs_be2_validate_image(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t len, emlxs_be_fw_image_t *fw_image);
static uint32_t emlxs_be3_validate_image(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t len, emlxs_be_fw_image_t *fw_image);


static int32_t emlxs_be_verify_phy(emlxs_hba_t *hba, emlxs_be_fw_file_t *file,
                        MAILBOXQ *mbq, MATCHMAP *mp);
static int32_t emlxs_be_verify_crc(emlxs_hba_t *hba,
                        emlxs_be_fw_file_t *file,
                        MAILBOXQ *mbq, MATCHMAP *mp);
static int32_t emlxs_be_flash_image(emlxs_hba_t *hba, caddr_t buffer,
                        emlxs_be_fw_file_t *file, MAILBOXQ *mbq, MATCHMAP *mp);
static int32_t emlxs_be_fw_download(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t len, uint32_t offline);
static int32_t emlxs_obj_fw_download(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t len, uint32_t offline);
static int32_t emlxs_obj_flash_image(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t size, MAILBOXQ *mbq, MATCHMAP *mp,
                        uint32_t *change_status);
static uint32_t emlxs_obj_validate_image(emlxs_hba_t *hba, caddr_t buffer,
                        uint32_t len, emlxs_obj_header_t *obj_hdr);
static uint32_t emlxs_be_version(caddr_t buffer, uint32_t size,
                        uint32_t *plus_flag);
static uint32_t emlxs_proc_rel_2mb(emlxs_hba_t *hba, caddr_t buffer,
                        emlxs_fw_image_t *fw_image);
static uint32_t emlxs_delete_load_entry(emlxs_hba_t *hba, PROG_ID *progId);

static void emlxs_verify_image(emlxs_hba_t *hba, emlxs_fw_image_t *image);

static uint32_t emlxs_clean_flash(emlxs_hba_t *hba,
                        PWAKE_UP_PARMS OldWakeUpParms,
                        PWAKE_UP_PARMS NewWakeUpParms);

/* ************************************************************************* */

extern int32_t
emlxs_fw_download(emlxs_hba_t *hba, caddr_t buffer, uint32_t len,
    uint32_t offline)
{
        emlxs_port_t *port = &PPORT;
        uint32_t *Uptr;
        IMAGE_HDR ImageHdr;
        AIF_HDR AifHdr;
        uint32_t ImageType;
        WAKE_UP_PARMS WakeUpParms;
        uint32_t rval = 0;
        emlxs_fw_image_t fw_image;
        uint32_t i;

#ifdef EMLXS_LITTLE_ENDIAN
        caddr_t local_buffer;
        uint32_t *bptr1;
        uint32_t *bptr2;
#endif /* EMLXS_LITTLE_ENDIAN */

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                if (hba->model_info.chip & EMLXS_BE_CHIPS) {
                        rval = emlxs_be_fw_download(hba, buffer, len, offline);
                } else {
                        rval = emlxs_obj_fw_download(hba, buffer, len, offline);
                }
                return (rval);
        }

        if (buffer == NULL || len == 0) {
                return (EMLXS_IMAGE_BAD);
        }

#ifdef EMLXS_LITTLE_ENDIAN
        /* We need to swap the image buffer before we start */

        /*
         * Use KM_SLEEP to allocate a temporary buffer
         */
        local_buffer = (caddr_t)kmem_zalloc(len, KM_SLEEP);

        /* Perform a 32 bit swap of the image */
        bptr1 = (uint32_t *)local_buffer;
        bptr2 = (uint32_t *)buffer;
        for (i = 0; i < (len / 4); i++) {
                *bptr1 = LE_SWAP32(*bptr2);
                bptr1++;
                bptr2++;
        }

        /* Replace the original buffer */
        buffer = local_buffer;
#endif /* EMLXS_LITTLE_ENDIAN */

        bzero(&fw_image, sizeof (emlxs_fw_image_t));
        for (i = 0; i < MAX_PROG_TYPES; i++) {
                (void) strlcpy(fw_image.prog[i].label, "none",
                    sizeof (fw_image.prog[i].label));
        }

        /* Validate image */
        if ((rval = emlxs_validate_image(hba, buffer, len, &fw_image))) {
                goto done;
        }

        /* Verify image */
        emlxs_verify_image(hba, &fw_image);

        /* Get image type */
        Uptr = (uint32_t *)buffer;
        ImageType = *Uptr;

        /*
         * Pegasus and beyond FW download is done differently
         * for absolute download.
         */

        /* Check for absolute image */
        if ((ImageType == NOP_IMAGE_TYPE) &&
            !(hba->model_info.chip &
            (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP))) {
                /*
                 * Because 2Mb flash download file format is different from
                 * 512k, it needs to be handled differently
                 */
                if (rval = emlxs_start_abs_download_2mb(hba, buffer, len,
                    offline, &fw_image)) {
                        goto done;
                }

                /* Offline already handled */
                offline = 0;

                goto SLI_DOWNLOAD_EXIT;
        }

        /* Pre-pegasus adapters only */

        /* Initialize headers */
        if (ImageType == NOP_IMAGE_TYPE) {
                bcopy(buffer, &AifHdr, sizeof (AIF_HDR));
                bzero((void *)&ImageHdr, sizeof (IMAGE_HDR));
        } else { /* PRG file */
                bzero((void *)&AifHdr, sizeof (AIF_HDR));
                bcopy(buffer, &ImageHdr, sizeof (IMAGE_HDR));
        }

        /* Everything checks out, now to just do it */

        if (offline) {
                if (emlxs_offline(hba, 0) != FC_SUCCESS) {
                        offline = 0;

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to take adapter offline.");

                        rval = EMLXS_OFFLINE_FAILED;
                        goto SLI_DOWNLOAD_EXIT;
                }

                if (EMLXS_SLI_HBA_RESET(hba, 1, 1, 0) != FC_SUCCESS) {
                        offline = 0;

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to restart adapter.");

                        rval = EMLXS_OFFLINE_FAILED;
                        goto SLI_DOWNLOAD_EXIT;
                }
        }

        /* Pre-pegasus adapters */

        if (ImageHdr.Id.Type == SBUS_FCODE) {
                /* Erase Flash */
                if (emlxs_erase_fcode_flash(hba)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to erase flash.");

                        rval = EMLXS_IMAGE_FAILED;
                        goto SLI_DOWNLOAD_EXIT;
                }

                /* Write FCODE */
                if (emlxs_write_fcode_flash(hba, &ImageHdr, buffer)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to write flash.");

                        rval = EMLXS_IMAGE_FAILED;
                        goto SLI_DOWNLOAD_EXIT;
                }

                goto SLI_DOWNLOAD_EXIT;
        }

        /* Pre-pegasus PCI adapters */

        if (emlxs_read_wakeup_parms(hba, &WakeUpParms, 1)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to get parameters.");

                rval = EMLXS_IMAGE_FAILED;

                goto SLI_DOWNLOAD_EXIT;
        }

        if (ImageType == NOP_IMAGE_TYPE) {
                if (emlxs_start_abs_download(hba, &AifHdr,
                    buffer, len, &WakeUpParms)) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_download_failed_msg,
                            "Failed to program flash.");

                        rval = EMLXS_IMAGE_FAILED;

                        goto SLI_DOWNLOAD_EXIT;
                }

        } else { /* Relative PRG file */
                if (emlxs_start_rel_download(hba, &ImageHdr, buffer,
                    &WakeUpParms, 0)) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_download_failed_msg,
                            "Failed to program flash.");

                        rval = EMLXS_IMAGE_FAILED;

                        goto SLI_DOWNLOAD_EXIT;
                }
        }

SLI_DOWNLOAD_EXIT:

        if (offline) {
                (void) emlxs_online(hba);
        }

        if (rval == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_complete_msg,
                    "Status good.");
        }

done:

#ifdef EMLXS_LITTLE_ENDIAN
        /* Free the local buffer */
        kmem_free(local_buffer, len);
#endif /* EMLXS_LITTLE_ENDIAN */

        return (rval);

} /* emlxs_fw_download */


extern void
emlxs_memset(uint8_t *buffer, uint8_t value, uint32_t size)
{
        while (size--) {
                *buffer++ = value;
        }

} /* emlxs_memset () */


static int32_t
emlxs_be_flash_image(emlxs_hba_t *hba, caddr_t buffer,
    emlxs_be_fw_file_t *file, MAILBOXQ *mbq, MATCHMAP *mp)
{
        emlxs_port_t *port = &PPORT;
        uint8_t *image_ptr;
        uint32_t *wptr;
        uint8_t *payload;
        MAILBOX4 *mb;
        IOCTL_COMMON_FLASHROM *flashrom;
        mbox_req_hdr_t  *hdr_req;
        uint32_t        image_size;
        uint32_t        block_size;
        uint32_t        xfer_size;
        uint32_t        block_offset;
        uint32_t        count;
        uint32_t        rval = 0;

        if (file->image_size == 0) {
                return (0);
        }

        image_ptr  = (uint8_t *)buffer + file->image_offset;
        image_size = file->image_size;
        block_size = file->block_size;
        block_offset = 0;
        mb = (MAILBOX4*)mbq;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "%s: Downloading...", file->label);

        while (block_size) {
                bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);
                bzero((void *) mp->virt, mp->size);

                xfer_size = min(BE_MAX_XFER_SIZE, block_size);

                mb->un.varSLIConfig.be.embedded = 0;
                mbq->nonembed = (void *)mp;
                mbq->mbox_cmpl = NULL;

                mb->mbxCommand = MBX_SLI_CONFIG;
                mb->mbxOwner = OWN_HOST;

                hdr_req = (mbox_req_hdr_t *)mp->virt;
                hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
                hdr_req->opcode = COMMON_OPCODE_WRITE_FLASHROM;
                hdr_req->timeout = 0;
                hdr_req->req_length = sizeof (IOCTL_COMMON_FLASHROM) +
                    xfer_size;

                flashrom = (IOCTL_COMMON_FLASHROM *)(hdr_req + 1);

                if (file->type == MGMT_FLASHROM_OPTYPE_PHY_FIRMWARE) {
                        flashrom->params.opcode = ((block_size == xfer_size)?
                            MGMT_PHY_FLASHROM_OPCODE_FLASH:
                            MGMT_PHY_FLASHROM_OPCODE_SAVE);
                        flashrom->params.optype = 0; /* ignored */
                } else {
                        flashrom->params.opcode = ((block_size == xfer_size)?
                            MGMT_FLASHROM_OPCODE_FLASH:
                            MGMT_FLASHROM_OPCODE_SAVE);
                        flashrom->params.optype = file->type;
                }

                flashrom->params.data_buffer_size = xfer_size;
                flashrom->params.offset = block_offset;

                /* Build data buffer payload */
                payload = (uint8_t *)(&flashrom->params.data_buffer);
                emlxs_memset(payload, 0xff, xfer_size);

                /* Copy remaining image into payload */
                if (image_size) {
                        count = min(image_size, xfer_size);
                        BE_SWAP32_BCOPY(image_ptr, payload, count);
                        image_size -= count;
                        image_ptr  += count;
                }

                if ((flashrom->params.opcode == MGMT_FLASHROM_OPCODE_FLASH) ||
                    (flashrom->params.opcode ==
                    MGMT_PHY_FLASHROM_OPCODE_FLASH)) {
                        wptr = (uint32_t *)&payload[(xfer_size - 12)];

                        wptr[0] = file->load_address;
                        wptr[1] = file->image_size;
                        wptr[2] = file->block_crc;
                }

                /* Send write request */
                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "%s: Unable to download image. status=%x",
                            file->label, mb->mbxStatus);
                        rval = EMLXS_IMAGE_FAILED;
                        goto done;
                }

                block_size -= xfer_size;
                block_offset += xfer_size;
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "%s: Download complete.", file->label);
done:

        return (rval);

} /* emlxs_be_flash_image() */




static int32_t
emlxs_be_verify_crc(emlxs_hba_t *hba,
    emlxs_be_fw_file_t *file, MAILBOXQ *mbq, MATCHMAP *mp)
{
        emlxs_port_t *port = &PPORT;
        uint32_t *wptr;
        uint8_t *payload;
        MAILBOX4 *mb;
        IOCTL_COMMON_FLASHROM *flashrom;
        mbox_req_hdr_t  *hdr_req;
        uint32_t        xfer_size;
        uint32_t        block_offset;
        uint32_t        rval = 0;
        uint32_t        value;

        if (file->type == MGMT_FLASHROM_OPTYPE_PHY_FIRMWARE) {
                /* PHY Firmware can't be verified */
                return (1);
        }

        xfer_size = 8;
        block_offset = file->block_size - xfer_size;
        mb = (MAILBOX4*)mbq;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
            "%s: Verifying CRC...", file->label);

        bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);
        bzero((void *) mp->virt, mp->size);

        mb->un.varSLIConfig.be.embedded = 0;
        mbq->nonembed = (void *)mp;
        mbq->mbox_cmpl = NULL;

        mb->mbxCommand = MBX_SLI_CONFIG;
        mb->mbxOwner = OWN_HOST;

        hdr_req = (mbox_req_hdr_t *)mp->virt;
        hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
        hdr_req->opcode = COMMON_OPCODE_READ_FLASHROM;
        hdr_req->timeout = 0;
        hdr_req->req_length = sizeof (IOCTL_COMMON_FLASHROM) +
            xfer_size;

        flashrom = (IOCTL_COMMON_FLASHROM *)(hdr_req + 1);
        flashrom->params.opcode = MGMT_FLASHROM_OPCODE_REPORT;
        flashrom->params.optype = file->type;
        flashrom->params.data_buffer_size = xfer_size;
        flashrom->params.offset = block_offset;

        /* Send read request */
        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) !=
            MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: Unable to read CRC. status=%x",
                    file->label, mb->mbxStatus);

                rval = 2;
                goto done;
        }

        payload = (uint8_t *)(&flashrom->params.data_buffer);
        wptr = (uint32_t *)(payload + xfer_size - 8);

        /* Verify image size */
        value = *wptr++;
        if (value != file->image_size) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: Image size mismatch. %08x != %08x",
                    file->label, value, file->image_size);

                rval = 1;
                goto done;
        }

        /* Verify block crc */
        value = *wptr;
        if (value != file->block_crc) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: CRC mismatch. %08x != %08x",
                    file->label, value, file->block_crc);
                rval = 1;
        }

done:

        if (rval == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: CRC verified.", file->label);
        }

        return (rval);

} /* emlxs_be_verify_crc() */


static int32_t
emlxs_be_verify_phy(emlxs_hba_t *hba,
    emlxs_be_fw_file_t *file, MAILBOXQ *mbq, MATCHMAP *mp)
{
        emlxs_port_t *port = &PPORT;
        MAILBOX4 *mb;
        IOCTL_COMMON_GET_PHY_DETAILS *phy;
        mbox_req_hdr_t  *hdr_req;
        uint32_t        rval = 0;

        if (file->type != MGMT_FLASHROM_OPTYPE_PHY_FIRMWARE) {
                return (1);
        }

        mb = (MAILBOX4*)mbq;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
            "%s: Getting PHY Details...", file->label);

        bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);
        bzero((void *) mp->virt, mp->size);

        mb->un.varSLIConfig.be.embedded = 0;
        mbq->nonembed = (void *)mp;
        mbq->mbox_cmpl = NULL;

        mb->mbxCommand = MBX_SLI_CONFIG;
        mb->mbxOwner = OWN_HOST;

        hdr_req = (mbox_req_hdr_t *)mp->virt;
        hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
        hdr_req->opcode = COMMON_OPCODE_GET_PHY_DETAILS;
        hdr_req->timeout = 0;
        hdr_req->req_length = sizeof (IOCTL_COMMON_GET_PHY_DETAILS);

        phy = (IOCTL_COMMON_GET_PHY_DETAILS *)(hdr_req + 1);

        /* Send read request */
        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) !=
            MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: Unable to get PHY details. status=%x",
                    file->label, mb->mbxStatus);

                rval = 2;
                goto done;
        }

        if ((phy->params.response.phy_type != PHY_TN_8022) ||
            (phy->params.response.interface_type != BASET_10GB_TYPE)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: PHY not applicable. %08x,%08x",
                    file->label,
                    phy->params.response.phy_type,
                    phy->params.response.interface_type);

                rval = 1;
        }

done:

        if (rval == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: PHY verified. %x,%x",
                    file->label,
                    phy->params.response.phy_type,
                    phy->params.response.interface_type);
        }

        return (rval);

} /* emlxs_be_verify_phy() */


extern int32_t
emlxs_be_read_fw_version(emlxs_hba_t *hba, emlxs_firmware_t *fw)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbq = NULL;
        MATCHMAP *mp = NULL;
        MAILBOX4 *mb;
        uint32_t *wptr;
        uint8_t *payload;
        IOCTL_COMMON_FLASHROM *flashrom;
        mbox_req_hdr_t  *hdr_req;
        uint32_t        xfer_size;
        uint32_t        block_offset;
        uint32_t        rval = 0;

        bzero((void *) fw, sizeof (emlxs_firmware_t));

        if ((mbq = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_SLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                    "read_fw_version: Unable to allocate mailbox buffer.");

                rval = 1;
                goto done;
        }

        if ((mp = emlxs_mem_buf_alloc(hba, (sizeof (mbox_req_hdr_t) +
            sizeof (IOCTL_COMMON_FLASHROM) + 32))) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                    "read_fw_version: Unable to allocate payload buffer.");

                rval = EMLXS_IMAGE_FAILED;
                goto done;
        }

        mb = (MAILBOX4*)mbq;

        /* Read CRC and size */
        xfer_size = 8;
        block_offset = 0x140000 - xfer_size;

        bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);
        bzero((void *) mp->virt, mp->size);

        mb->un.varSLIConfig.be.embedded = 0;
        mbq->nonembed = (void *)mp;
        mbq->mbox_cmpl = NULL;

        mb->mbxCommand = MBX_SLI_CONFIG;
        mb->mbxOwner = OWN_HOST;

        hdr_req = (mbox_req_hdr_t *)mp->virt;
        hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
        hdr_req->opcode = COMMON_OPCODE_READ_FLASHROM;
        hdr_req->timeout = 0;
        hdr_req->req_length = sizeof (IOCTL_COMMON_FLASHROM) +
            xfer_size;

        flashrom = (IOCTL_COMMON_FLASHROM *)(hdr_req + 1);
        flashrom->params.opcode = MGMT_FLASHROM_OPCODE_REPORT;
        flashrom->params.optype = MGMT_FLASHROM_OPTYPE_FCOE_FIRMWARE;
        flashrom->params.data_buffer_size = xfer_size;
        flashrom->params.offset = block_offset;

        /* Send read request */
        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) !=
            MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                    "read_fw_version: Unable to read CRC. status=%x",
                    mb->mbxStatus);

                rval = 1;
                goto done;
        }

        payload = (uint8_t *)(&flashrom->params.data_buffer);

        wptr = (uint32_t *)payload;
        fw->size = *wptr++; /* image size */
        fw->sli4 = *wptr;   /* block crc */
        fw->kern = *wptr;
        fw->stub = *wptr;

        /* Read version label */
        xfer_size = 32;
        block_offset = 0x30;

        bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);
        bzero((void *) mp->virt, mp->size);

        mb->un.varSLIConfig.be.embedded = 0;
        mbq->nonembed = (void *)mp;
        mbq->mbox_cmpl = NULL;

        mb->mbxCommand = MBX_SLI_CONFIG;
        mb->mbxOwner = OWN_HOST;

        hdr_req = (mbox_req_hdr_t *)mp->virt;
        hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
        hdr_req->opcode = COMMON_OPCODE_READ_FLASHROM;
        hdr_req->timeout = 0;
        hdr_req->req_length = sizeof (IOCTL_COMMON_FLASHROM) +
            xfer_size;

        flashrom = (IOCTL_COMMON_FLASHROM *)(hdr_req + 1);
        flashrom->params.opcode = MGMT_FLASHROM_OPCODE_REPORT;
        flashrom->params.optype = MGMT_FLASHROM_OPTYPE_FCOE_FIRMWARE;
        flashrom->params.data_buffer_size = xfer_size;
        flashrom->params.offset = block_offset;

        /* Send read request */
        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) !=
            MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                    "read_fw_version: Unable to read version string. status=%x",
                    mb->mbxStatus);

                rval = 1;
                goto done;
        }

        payload = (uint8_t *)(&flashrom->params.data_buffer);
        BE_SWAP32_BCOPY(payload, (uint8_t *)fw->label, 32);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
            "FCOE FIRMWARE: size=%x version=%s (0x%08x)",
            fw->size, fw->label, fw->sli4);

done:

        if (mbq) {
                emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
        }

        if (mp) {
                emlxs_mem_buf_free(hba, mp);
        }

        return (rval);

} /* emlxs_be_read_fw_version() */


static uint32_t
emlxs_be_version(caddr_t buffer, uint32_t size, uint32_t *plus_flag)
{
        emlxs_be2_ufi_header_t *ufi_hdr;
        char signature[BE2_SIGNATURE_SIZE];
        uint32_t be_version = 0;

        if (size < sizeof (emlxs_be2_ufi_header_t)) {
                return (0);
        }
        ufi_hdr = (emlxs_be2_ufi_header_t *)buffer;

        (void) snprintf(signature, BE2_SIGNATURE_SIZE, "%s+", BE_SIGNATURE);

        /* Check if this is a UFI image */
        if (strncmp(signature, (char *)ufi_hdr->signature,
            strlen(BE_SIGNATURE)) != 0) {
                return (0);
        }

        /* Check if this is a UFI plus image */
        if (plus_flag) {
                /* Check if this is a UFI plus image */
                if (strncmp(signature, (char *)ufi_hdr->signature,
                    strlen(BE_SIGNATURE)+1) == 0) {
                        *plus_flag = 1;
                } else {
                        *plus_flag = 0;
                }
        }

        if ((ufi_hdr->build[0] >= '1') && (ufi_hdr->build[0] <= '9')) {
                be_version = ufi_hdr->build[0] - '0';
        }

        return (be_version);

} /* emlxs_be_version() */


static uint32_t
emlxs_be2_validate_image(emlxs_hba_t *hba, caddr_t buffer,
    uint32_t len, emlxs_be_fw_image_t *fw_image)
{
        emlxs_port_t *port = &PPORT;
        emlxs_be2_ufi_header_t *ufi_hdr;
        emlxs_be2_flash_dir_t *flash_dir;
        emlxs_be2_flash_entry_t *entry;
        uint8_t *bptr;
        uint32_t *wptr;
        uint32_t i;
        uint32_t k;
        uint32_t mask;
        uint32_t value;
        uint32_t image_size;
        emlxs_be_fw_file_t *file;
        emlxs_be_fw_file_t *file2;
        uint32_t ufi_plus = 0;
        uint32_t be_version = 0;
        uint32_t found;

        bzero(fw_image, sizeof (emlxs_be_fw_image_t));

        if (hba->model_info.chip != EMLXS_BE2_CHIP) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter model.");
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (len < (sizeof (emlxs_be2_ufi_header_t) +
            sizeof (emlxs_be2_flash_dir_t))) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Image too small. (%d < %d)",
                    len, (sizeof (emlxs_be2_ufi_header_t) +
                    sizeof (emlxs_be2_flash_dir_t)));
                return (EMLXS_IMAGE_BAD);
        }

        be_version = emlxs_be_version(buffer, len, &ufi_plus);

        /* Check if this is a standard BE2 image */
        if (be_version != 2) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid image provided.");
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        ufi_hdr = (emlxs_be2_ufi_header_t *)buffer;

#ifdef EMLXS_BIG_ENDIAN
        /* Big Endian Swapping */
        /* Swap ufi header */
        ufi_hdr->checksum =
            SWAP32(ufi_hdr->checksum);
        ufi_hdr->antidote =
            SWAP32(ufi_hdr->antidote);
        ufi_hdr->controller.vendor_id =
            SWAP32(ufi_hdr->controller.vendor_id);
        ufi_hdr->controller.device_id =
            SWAP32(ufi_hdr->controller.device_id);
        ufi_hdr->controller.sub_vendor_id =
            SWAP32(ufi_hdr->controller.sub_vendor_id);
        ufi_hdr->controller.sub_device_id =
            SWAP32(ufi_hdr->controller.sub_device_id);
        ufi_hdr->file_length =
            SWAP32(ufi_hdr->file_length);
        ufi_hdr->chunk_num =
            SWAP32(ufi_hdr->chunk_num);
        ufi_hdr->chunk_cnt =
            SWAP32(ufi_hdr->chunk_cnt);
        ufi_hdr->image_cnt =
            SWAP32(ufi_hdr->image_cnt);
#endif /* EMLXS_BIG_ENDIAN */

        if (len != ufi_hdr->file_length) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid image size (%d != %d)",
                    len, ufi_hdr->file_length);

                return (EMLXS_IMAGE_BAD);
        }

        /* Scan for flash dir signature */
        bptr = (uint8_t *)buffer;
        flash_dir = NULL;
        for (i = 0; i < len; i++, bptr++) {
                if (strncmp((char *)bptr, BE_DIR_SIGNATURE,
                    sizeof (BE_DIR_SIGNATURE)) == 0) {
                        flash_dir = (emlxs_be2_flash_dir_t *)bptr;
                        break;
                }
        }

        if (!flash_dir) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Unable to find flash directory.");

                return (EMLXS_IMAGE_BAD);
        }

#ifdef EMLXS_BIG_ENDIAN
        /* Big Endian Swapping */
        /* Swap flash dir */
        flash_dir->header.format_rev =
            SWAP32(flash_dir->header.format_rev);
        flash_dir->header.checksum =
            SWAP32(flash_dir->header.checksum);
        flash_dir->header.antidote =
            SWAP32(flash_dir->header.antidote);
        flash_dir->header.build_num =
            SWAP32(flash_dir->header.build_num);
        flash_dir->header.active_entry_mask =
            SWAP32(flash_dir->header.active_entry_mask);
        flash_dir->header.valid_entry_mask =
            SWAP32(flash_dir->header.valid_entry_mask);
        flash_dir->header.orig_content_mask =
            SWAP32(flash_dir->header.orig_content_mask);
        flash_dir->header.resv0 = SWAP32(flash_dir->header.resv0);
        flash_dir->header.resv1 = SWAP32(flash_dir->header.resv1);
        flash_dir->header.resv2 = SWAP32(flash_dir->header.resv2);
        flash_dir->header.resv3 = SWAP32(flash_dir->header.resv3);
        flash_dir->header.resv4 = SWAP32(flash_dir->header.resv4);

        for (i = 0; i < BE_CONTROLLER_SIZE; i++) {
                flash_dir->header.controller[i].vendor_id =
                    SWAP32(flash_dir->header.controller[i].vendor_id);
                flash_dir->header.controller[i].device_id =
                    SWAP32(flash_dir->header.controller[i].device_id);
                flash_dir->header.controller[i].sub_vendor_id =
                    SWAP32(flash_dir->header.controller[i].sub_vendor_id);
                flash_dir->header.controller[i].sub_device_id =
                    SWAP32(flash_dir->header.controller[i].sub_device_id);
        }

        for (i = 0, mask = 1; i < BE_FLASH_ENTRIES; i++,  mask <<= 1) {

                if (!(flash_dir->header.valid_entry_mask & mask)) {
                        continue;
                }

                entry = &flash_dir->entry[i];

                if ((entry->type == 0) ||
                    (entry->type == (uint32_t)-1) ||
                    (entry->image_size == 0)) {
                        continue;
                }

                flash_dir->entry[i].type =
                    SWAP32(flash_dir->entry[i].type);
                flash_dir->entry[i].offset =
                    SWAP32(flash_dir->entry[i].offset);
                flash_dir->entry[i].pad_size =
                    SWAP32(flash_dir->entry[i].pad_size);
                flash_dir->entry[i].image_size =
                    SWAP32(flash_dir->entry[i].image_size);
                flash_dir->entry[i].checksum =
                    SWAP32(flash_dir->entry[i].checksum);
                flash_dir->entry[i].entry_point =
                    SWAP32(flash_dir->entry[i].entry_point);
                flash_dir->entry[i].resv0 =
                    SWAP32(flash_dir->entry[i].resv0);
                flash_dir->entry[i].resv1 =
                    SWAP32(flash_dir->entry[i].resv1);
        }
#endif /* EMLXS_BIG_ENDIAN */

        /* Verify adapter model */
        found = 0;
        for (i = 0; i < BE_CONTROLLER_SIZE; i++) {
                if (flash_dir->header.controller[i].device_id ==
                    hba->model_info.device_id) {
                        found = 1;
                }
        }

        if (!found) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter device id=0x%x.",
                    hba->model_info.device_id);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        /* Build fw_image table */
        fw_image->be_version = 2;
        fw_image->ufi_plus = ufi_plus;
        for (i = 0, mask = 1; i < BE_FLASH_ENTRIES; i++, mask <<= 1) {

                if (!(flash_dir->header.valid_entry_mask & mask)) {
                        continue;
                }

                entry = &flash_dir->entry[i];

                if ((entry->type == 0) ||
                    (entry->type == (uint32_t)-1) ||
                    (entry->image_size == 0)) {
                        continue;
                }

                switch (entry->type) {
                case BE_FLASHTYPE_REDBOOT:
                        file = &fw_image->file[REDBOOT_FLASHTYPE];
                        (void) strlcpy(file->label, "REDBOOT",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_REDBOOT;
                        break;
                case BE_FLASHTYPE_ISCSI_BIOS:
                        file = &fw_image->file[ISCSI_BIOS_FLASHTYPE];
                        (void) strlcpy(file->label, "ISCSI BIOS",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_ISCSI_BIOS;
                        break;
                case BE_FLASHTYPE_PXE_BIOS:
                        file = &fw_image->file[PXE_BIOS_FLASHTYPE];
                        (void) strlcpy(file->label, "PXE BIOS",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_PXE_BIOS;
                        break;
                case BE_FLASHTYPE_FCOE_BIOS:
                        file = &fw_image->file[FCOE_BIOS_FLASHTYPE];
                        (void) strlcpy(file->label, "FCOE BIOS",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_FCOE_BIOS;
                        break;
                case BE_FLASHTYPE_ISCSI_FIRMWARE:
                        file = &fw_image->file[ISCSI_FIRMWARE_FLASHTYPE];
                        (void) strlcpy(file->label, "ISCSI FIRMWARE",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_ISCSI_FIRMWARE;
                        break;
                case BE_FLASHTYPE_FCOE_FIRMWARE:
                        file = &fw_image->file[FCOE_FIRMWARE_FLASHTYPE];
                        (void) strlcpy(file->label, "FCOE FIRMWARE",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_FCOE_FIRMWARE;
                        break;
                case BE_FLASHTYPE_FCOE_BACKUP:
                case BE_FLASHTYPE_ISCSI_BACKUP:
                        continue;

                default:
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Unknown image type found.  type=%x",
                            entry->type);
                        continue;
                }

                file->be_version = fw_image->be_version;
                file->ufi_plus = fw_image->ufi_plus;
                file->image_size = entry->image_size;
                image_size = BE_SWAP32(entry->image_size);

                if (ufi_plus) {
                        file->image_offset = entry->offset;
                        file->block_size   = entry->pad_size;
                        file->block_crc    = entry->checksum;
                        file->load_address = entry->entry_point;

                } else {
                        file->image_offset = entry->offset +
                            sizeof (emlxs_be2_ufi_header_t);

                        /* Get entry block size and crc */
                        k = file->image_offset + file->image_size;
                        k &= 0xFFFFFFFC;

                        wptr = (uint32_t *)(buffer +  k);
                        for (; k < len; k += 4) {
                                if (*wptr++ == image_size) {
                                        /* Calculate block_size */
                                        file->block_size = (k + 8) -
                                            file->image_offset;

                                        /* Read load_address */
                                        value = *(wptr - 2);
                                        file->load_address = BE_SWAP32(value);

                                        /* Read block_crc */
                                        value = *wptr;
                                        file->block_crc = BE_SWAP32(value);

                                        break;
                                }
                        }

                        if (k >= len) {
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                                    "%s: End of block not found. offset=%x",
                                    file->label, file->image_offset);

                                bzero(fw_image, sizeof (emlxs_be_fw_image_t));
                                return (EMLXS_IMAGE_BAD);
                        }
                }

                /* Make sure image will fit in block specified */
                if (file->image_size + 12 > file->block_size) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "%s: Image too large for block. image=%x block=%x",
                            file->label, file->image_size, file->block_size);

                        bzero(fw_image, sizeof (emlxs_be_fw_image_t));
                        return (EMLXS_IMAGE_BAD);
                }

                /* Automatically create a backup file entry for firmware */
                if (file->type == MGMT_FLASHROM_OPTYPE_FCOE_FIRMWARE) {
                        file2 = &fw_image->file[FCOE_BACKUP_FLASHTYPE];

                        bcopy((uint8_t *)file, (uint8_t *)file2,
                            sizeof (emlxs_be_fw_file_t));
                        file2->type = MGMT_FLASHROM_OPTYPE_FCOE_BACKUP;
                        (void) strlcpy(file2->label, "FCOE BACKUP",
                            sizeof (file2->label));

                        /* Save FCOE version info */
                        bptr = (uint8_t *)buffer + file->image_offset + 0x30;
                        (void) strncpy(fw_image->fcoe_label, (char *)bptr,
                            BE_VERSION_SIZE);
                        fw_image->fcoe_version = file->block_crc;

                } else if (file->type ==
                    MGMT_FLASHROM_OPTYPE_ISCSI_FIRMWARE) {
                        file2 = &fw_image->file[ISCSI_BACKUP_FLASHTYPE];

                        bcopy((uint8_t *)file, (uint8_t *)file2,
                            sizeof (emlxs_be_fw_file_t));
                        file2->type = MGMT_FLASHROM_OPTYPE_ISCSI_BACKUP;
                        (void) strlcpy(file2->label, "ISCSI BACKUP",
                            sizeof (file2->label));

                        /* Save ISCSI version info */
                        bptr = (uint8_t *)buffer + file->image_offset + 0x30;
                        (void) strncpy(fw_image->iscsi_label, (char *)bptr,
                            BE_VERSION_SIZE);
                        fw_image->iscsi_version = file->block_crc;
                }
        }

        if (fw_image->fcoe_version == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Unable to find FCOE firmware component.");

                bzero(fw_image, sizeof (emlxs_be_fw_image_t));
                return (EMLXS_IMAGE_BAD);
        }

        /* Display contents */
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
            "BE2 UFI Image: %08x, %s", fw_image->fcoe_version,
            fw_image->fcoe_label);

        for (i = 0; i < BE_MAX_FLASHTYPES; i++) {
                file = &fw_image->file[i];

                if (file->image_size == 0) {
                        continue;
                }

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: be=%x%s type=%x block=%x image=%x offset=%x crc=%x "
                    "load=%x",
                    file->label, file->be_version, (file->ufi_plus)?"+":"",
                    file->type, file->block_size, file->image_size,
                    file->image_offset, file->block_crc, file->load_address);
        }

        return (0);

} /* emlxs_be2_validate_image() */


static uint32_t
emlxs_be3_validate_image(emlxs_hba_t *hba, caddr_t buffer,
    uint32_t len, emlxs_be_fw_image_t *fw_image)
{
        emlxs_port_t *port = &PPORT;
        emlxs_be3_ufi_header_t *ufi_hdr;
        emlxs_be3_flash_dir_t *flash_dir;
        emlxs_be3_flash_entry_t *entry;
        emlxs_be3_image_header_t *flash_image_hdr;
        emlxs_be3_image_header_t *image_hdr;
        uint8_t *bptr;
        uint32_t *wptr;
        uint32_t i;
        uint32_t value;
        emlxs_be_fw_file_t *file;
        emlxs_be_fw_file_t *file2;
        uint32_t ufi_plus = 0;
        uint32_t be_version = 0;
        uint32_t found;

        bzero(fw_image, sizeof (emlxs_be_fw_image_t));

        if (hba->model_info.chip != EMLXS_BE3_CHIP) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter model.");
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (len < (sizeof (emlxs_be3_ufi_header_t) +
            sizeof (emlxs_be3_flash_dir_t))) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Image too small. (%d < %d)",
                    len, (sizeof (emlxs_be3_ufi_header_t) +
                    sizeof (emlxs_be3_flash_dir_t)));
                return (EMLXS_IMAGE_BAD);
        }

        be_version = emlxs_be_version(buffer, len, &ufi_plus);

        /* Check if this is a standard BE3 image */
        if (be_version != 3) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid image provided.");
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        ufi_hdr = (emlxs_be3_ufi_header_t *)buffer;

#ifdef EMLXS_BIG_ENDIAN
        /* Big Endian Swapping */
        /* Swap ufi header */
        ufi_hdr->ufi_version =
            SWAP32(ufi_hdr->ufi_version);
        ufi_hdr->file_length =
            SWAP32(ufi_hdr->file_length);
        ufi_hdr->checksum =
            SWAP32(ufi_hdr->checksum);
        ufi_hdr->antidote =
            SWAP32(ufi_hdr->antidote);
        ufi_hdr->image_cnt =
            SWAP32(ufi_hdr->image_cnt);
#endif /* EMLXS_BIG_ENDIAN */

        if (len != ufi_hdr->file_length) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid image size (%d != %d)",
                    len, ufi_hdr->file_length);

                return (EMLXS_IMAGE_BAD);
        }

        flash_image_hdr = NULL;
        image_hdr = (emlxs_be3_image_header_t *)(buffer +
            sizeof (emlxs_be3_ufi_header_t));
        for (i = 0; i < ufi_hdr->image_cnt; i++, image_hdr++) {
#ifdef EMLXS_BIG_ENDIAN
                image_hdr->id = SWAP32(image_hdr->id);
                image_hdr->offset = SWAP32(image_hdr->offset);
                image_hdr->length = SWAP32(image_hdr->length);
                image_hdr->checksum = SWAP32(image_hdr->checksum);
#endif /* EMLXS_BIG_ENDIAN */

                if (image_hdr->id == UFI_BE3_FLASH_ID) {
                        flash_image_hdr = image_hdr;
                }
        }

        if (!flash_image_hdr) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "No flash image found.");

                return (EMLXS_IMAGE_BAD);
        }

        /* Scan for flash dir signature */
        bptr = (uint8_t *)buffer + flash_image_hdr->offset;
        flash_dir = NULL;
        for (i = 0; i < flash_image_hdr->length; i++, bptr++) {
                if (strncmp((char *)bptr, BE_DIR_SIGNATURE,
                    sizeof (BE_DIR_SIGNATURE)) == 0) {
                        flash_dir = (emlxs_be3_flash_dir_t *)bptr;
                        break;
                }
        }

        if (!flash_dir) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Unable to find flash directory.");

                return (EMLXS_IMAGE_BAD);
        }

#ifdef EMLXS_BIG_ENDIAN
        /* Big Endian Swapping */
        /* Swap flash dir */
        flash_dir->header.format_rev =
            SWAP32(flash_dir->header.format_rev);
        flash_dir->header.checksum =
            SWAP32(flash_dir->header.checksum);
        flash_dir->header.antidote =
            SWAP32(flash_dir->header.antidote);
        flash_dir->header.entry_count =
            SWAP32(flash_dir->header.entry_count);
        flash_dir->header.resv0 = SWAP32(flash_dir->header.resv0);
        flash_dir->header.resv1 = SWAP32(flash_dir->header.resv1);
        flash_dir->header.resv2 = SWAP32(flash_dir->header.resv2);
        flash_dir->header.resv3 = SWAP32(flash_dir->header.resv3);

        for (i = 0; i < BE_CONTROLLER_SIZE; i++) {
                flash_dir->header.controller[i].vendor_id =
                    SWAP32(flash_dir->header.controller[i].vendor_id);
                flash_dir->header.controller[i].device_id =
                    SWAP32(flash_dir->header.controller[i].device_id);
                flash_dir->header.controller[i].sub_vendor_id =
                    SWAP32(flash_dir->header.controller[i].sub_vendor_id);
                flash_dir->header.controller[i].sub_device_id =
                    SWAP32(flash_dir->header.controller[i].sub_device_id);
        }

        for (i = 0; i < flash_dir->header.entry_count; i++) {
                entry = &flash_dir->entry[i];

                if ((entry->type == 0) ||
                    (entry->type == (uint32_t)-1) ||
                    (entry->image_size == 0)) {
                        continue;
                }

                flash_dir->entry[i].type =
                    SWAP32(flash_dir->entry[i].type);
                flash_dir->entry[i].offset =
                    SWAP32(flash_dir->entry[i].offset);
                flash_dir->entry[i].block_size =
                    SWAP32(flash_dir->entry[i].block_size);
                flash_dir->entry[i].image_size =
                    SWAP32(flash_dir->entry[i].image_size);
                flash_dir->entry[i].checksum =
                    SWAP32(flash_dir->entry[i].checksum);
                flash_dir->entry[i].entry_point =
                    SWAP32(flash_dir->entry[i].entry_point);
                flash_dir->entry[i].resv0 =
                    SWAP32(flash_dir->entry[i].resv0);
                flash_dir->entry[i].resv1 =
                    SWAP32(flash_dir->entry[i].resv1);
        }
#endif /* EMLXS_BIG_ENDIAN */

        /* Verify image checksum */
        if (flash_dir->header.checksum != flash_image_hdr->checksum) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid flash directory checksum. (%x != %x)\n",
                    flash_dir->header.checksum, flash_image_hdr->checksum);
                return (EMLXS_IMAGE_BAD);
        }

        /* Verify adapter model */
        found = 0;
        for (i = 0; i < BE_CONTROLLER_SIZE; i++) {
                if (flash_dir->header.controller[i].device_id ==
                    hba->model_info.device_id) {
                        found = 1;
                }
        }

        if (!found) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter device id=0x%x.",
                    hba->model_info.device_id);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        /* Build fw_image table */
        fw_image->be_version = 3;
        fw_image->ufi_plus = ufi_plus;
        for (i = 0; i < flash_dir->header.entry_count; i++) {
                entry = &flash_dir->entry[i];

                if ((entry->type == 0) ||
                    (entry->type == (uint32_t)-1) ||
                    (entry->image_size == 0)) {
                        continue;
                }

                switch (entry->type) {
                case BE_FLASHTYPE_REDBOOT:
                        file = &fw_image->file[REDBOOT_FLASHTYPE];
                        (void) strlcpy(file->label, "REDBOOT",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_REDBOOT;
                        break;
                case BE_FLASHTYPE_ISCSI_BIOS:
                        file = &fw_image->file[ISCSI_BIOS_FLASHTYPE];
                        (void) strlcpy(file->label, "ISCSI BIOS",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_ISCSI_BIOS;
                        break;
                case BE_FLASHTYPE_PXE_BIOS:
                        file = &fw_image->file[PXE_BIOS_FLASHTYPE];
                        (void) strlcpy(file->label, "PXE BIOS",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_PXE_BIOS;
                        break;
                case BE_FLASHTYPE_FCOE_BIOS:
                        file = &fw_image->file[FCOE_BIOS_FLASHTYPE];
                        (void) strlcpy(file->label, "FCOE BIOS",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_FCOE_BIOS;
                        break;
                case BE_FLASHTYPE_ISCSI_FIRMWARE:
                        file = &fw_image->file[ISCSI_FIRMWARE_FLASHTYPE];
                        (void) strlcpy(file->label, "ISCSI FIRMWARE",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_ISCSI_FIRMWARE;
                        break;
                case BE_FLASHTYPE_FCOE_FIRMWARE:
                        file = &fw_image->file[FCOE_FIRMWARE_FLASHTYPE];
                        (void) strlcpy(file->label, "FCOE FIRMWARE",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_FCOE_FIRMWARE;
                        break;
                case BE_FLASHTYPE_NCSI_FIRMWARE:
                        file = &fw_image->file[NCSI_FIRMWARE_FLASHTYPE];
                        (void) strlcpy(file->label, "NCSI FIRMWARE",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_NCSI_FIRMWARE;
                        break;
                case BE_FLASHTYPE_FLASH_ISM:
                case BE_FLASHTYPE_FCOE_BACKUP:
                case BE_FLASHTYPE_ISCSI_BACKUP:
                        continue;
                case BE_FLASHTYPE_PHY_FIRMWARE:
                        file = &fw_image->file[PHY_FIRMWARE_FLASHTYPE];
                        (void) strlcpy(file->label, "PHY FIRMWARE",
                            sizeof (file->label));
                        file->type = MGMT_FLASHROM_OPTYPE_PHY_FIRMWARE;
                        break;

                default:
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Unknown image type found.  type=%x",
                            entry->type);
                        continue;
                }

                file->be_version = fw_image->be_version;
                file->ufi_plus = fw_image->ufi_plus;
                file->image_size = entry->image_size;

                if (ufi_plus) {
                        file->image_offset = entry->offset;
                        file->block_size   = entry->block_size;
                        file->block_crc    = entry->checksum;
                        file->load_address = entry->entry_point;
                } else {
                        file->image_offset = entry->offset +
                            flash_image_hdr->offset;
                        file->block_size   = entry->block_size;

                        wptr = (uint32_t *)(buffer +  file->image_offset +
                            file->block_size);

                        /* Read load address */
                        value = *(wptr - 3);
                        file->load_address = BE_SWAP32(value);

                        /* Read block_crc */
                        value = *(wptr - 1);
                        file->block_crc = BE_SWAP32(value);
                }

                /* Make sure image will fit in block specified */
                if (file->image_size + 12 > file->block_size) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "%s: Image too large for block. image=%x block=%x",
                            file->label, file->image_size, file->block_size);

                        bzero(fw_image, sizeof (emlxs_be_fw_image_t));
                        return (EMLXS_IMAGE_BAD);
                }

                /* Automatically create a backup file entry for firmware */
                if (file->type == MGMT_FLASHROM_OPTYPE_FCOE_FIRMWARE) {
                        file2 = &fw_image->file[FCOE_BACKUP_FLASHTYPE];

                        bcopy((uint8_t *)file, (uint8_t *)file2,
                            sizeof (emlxs_be_fw_file_t));
                        file2->type = MGMT_FLASHROM_OPTYPE_FCOE_BACKUP;
                        (void) strlcpy(file2->label, "FCOE BACKUP",
                            sizeof (file2->label));

                        /* Save FCOE version info */
                        bptr = (uint8_t *)buffer + file->image_offset + 0x30;
                        (void) strncpy(fw_image->fcoe_label, (char *)bptr,
                            BE_VERSION_SIZE);
                        fw_image->fcoe_version = file->block_crc;

                } else if (file->type ==
                    MGMT_FLASHROM_OPTYPE_ISCSI_FIRMWARE) {
                        file2 = &fw_image->file[ISCSI_BACKUP_FLASHTYPE];

                        bcopy((uint8_t *)file, (uint8_t *)file2,
                            sizeof (emlxs_be_fw_file_t));
                        file2->type = MGMT_FLASHROM_OPTYPE_ISCSI_BACKUP;
                        (void) strlcpy(file2->label, "ISCSI BACKUP",
                            sizeof (file->label));

                        /* Save ISCSI version info */
                        bptr = (uint8_t *)buffer + file->image_offset + 0x30;
                        (void) strncpy(fw_image->iscsi_label, (char *)bptr,
                            BE_VERSION_SIZE);
                        fw_image->iscsi_version = file->block_crc;
                }
        }

        if (fw_image->fcoe_version == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Unable to find FCOE firmware component.");

                bzero(fw_image, sizeof (emlxs_be_fw_image_t));
                return (EMLXS_IMAGE_BAD);
        }

        /* Display contents */
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
            "BE3 UFI Image: %08x, %s", fw_image->fcoe_version,
            fw_image->fcoe_label);

        for (i = 0; i < BE_MAX_FLASHTYPES; i++) {
                file = &fw_image->file[i];

                if (file->image_size == 0) {
                        continue;
                }

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: be=%x%s type=%x block=%x image=%x offset=%x crc=%x "
                    "load=%x",
                    file->label, file->be_version, (file->ufi_plus)? "+":"",
                    file->type, file->block_size, file->image_size,
                    file->image_offset, file->block_crc, file->load_address);
        }

        return (0);

} /* emlxs_be3_validate_image() */


static int32_t
emlxs_be_fw_download(emlxs_hba_t *hba, caddr_t buffer, uint32_t len,
    uint32_t offline)
{
        emlxs_port_t *port = &PPORT;
        uint32_t i;
        uint32_t update = 0;
        uint32_t rval = 0;
        MAILBOXQ *mbq = NULL;
        MATCHMAP *mp = NULL;
        emlxs_be_fw_image_t fw_image;
        emlxs_be_fw_file_t *file;
        uint32_t be_version;

        /* For now we will not take the driver offline during a download */
        offline = 0;

        if (hba->sli_mode != EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid sli_mode. mode=%d", hba->sli_mode);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (!(hba->model_info.chip & EMLXS_BE_CHIPS)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter model. chip=%x", hba->model_info.chip);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (buffer == NULL || len == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Empty buffer provided. buf=%p size=%d", buffer, len);
                return (EMLXS_IMAGE_BAD);
        }

        be_version = emlxs_be_version(buffer, len, 0);

        switch (be_version) {
        case 0:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid image provided. Non-UFI format.");
                return (EMLXS_IMAGE_INCOMPATIBLE);
        case 2:
                rval = emlxs_be2_validate_image(hba, buffer, len, &fw_image);
                if (rval) {
                        return (rval);
                }
                break;
        case 3:
                rval = emlxs_be3_validate_image(hba, buffer, len, &fw_image);
                if (rval) {
                        return (rval);
                }
                break;
        default:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid image provided. Unknown BE version. (%x)",
                    be_version);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        /* Allocate resources */

        if ((mbq = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_SLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                offline = 0;
                rval = EMLXS_IMAGE_FAILED;
                goto done;
        }

        if ((mp = emlxs_mem_buf_alloc(hba, (sizeof (mbox_req_hdr_t) +
            sizeof (IOCTL_COMMON_FLASHROM) + BE_MAX_XFER_SIZE))) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate flash buffer.");

                offline = 0;
                rval = EMLXS_IMAGE_FAILED;
                goto done;
        }

        /* Check if update is required */
        for (i = 0; i < BE_MAX_FLASHTYPES; i++) {
                file = &fw_image.file[i];

                if (file->image_size == 0) {
                        continue;
                }

                if (file->type == MGMT_FLASHROM_OPTYPE_PHY_FIRMWARE) {
                        rval = emlxs_be_verify_phy(hba, file, mbq, mp);

                        if (rval != 0) {
                                /* Do not update */
                                file->image_size = 0;
                                continue;
                        }
                } else {
                        rval = emlxs_be_verify_crc(hba, file, mbq, mp);
                        if (rval == 0) {
                                /* Do not update */
                                file->image_size = 0;
                                continue;
                        }
                }

                update++;
        }

        if (!update) {
                offline = 0;
                rval = 0;
                goto done;
        }

        /*
         * Everything checks out, now to just do it
         */
        if (offline) {
                if (emlxs_offline(hba, 0) != FC_SUCCESS) {

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to take adapter offline.");

                        offline = 0;
                        rval = EMLXS_OFFLINE_FAILED;
                        goto done;
                }
        }

        /* Download entries which require update */
        for (i = 0; i < BE_MAX_FLASHTYPES; i++) {
                file = &fw_image.file[i];

                if (file->image_size == 0) {
                        continue;
                }

                rval = emlxs_be_flash_image(hba, buffer, file, mbq, mp);

                if (rval != 0) {
                        goto done;
                }
        }

done:
        if (mbq) {
                emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
        }

        if (mp) {
                emlxs_mem_buf_free(hba, mp);
        }

        if (offline) {
                (void) emlxs_online(hba);
        }

        if (rval == 0) {
                if (update) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_complete_msg,
                            "Status good.");

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fw_updated_msg,
                            "The new firmware will not be activated until "
                            "the adapter is power cycled: %s",
                            fw_image.fcoe_label);

                } else {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
                            "No firmware update required.");
                }
        }

        return (rval);

} /* emlxs_be_fw_download() */


static int32_t
emlxs_obj_flash_image(emlxs_hba_t *hba, caddr_t buffer, uint32_t size,
    MAILBOXQ *mbq, MATCHMAP *mp, uint32_t *change_status)
{
        emlxs_port_t *port = &PPORT;
        uint8_t *image_ptr;
        MAILBOX4 *mb;
        mbox_req_hdr_t  *hdr_req;
        mbox_rsp_hdr_t  *hdr_rsp;
        uint32_t        image_size;
        uint32_t        xfer_size;
        uint32_t        image_offset;
        uint32_t        rval = 0;
        IOCTL_COMMON_WRITE_OBJECT *write_obj;
        uint32_t        cstatus = 0;

        if (!buffer || size == 0) {
                return (0);
        }

        image_ptr  = (uint8_t *)buffer;
        image_size = size;
        image_offset = 0;
        mb = (MAILBOX4*)mbq;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "OBJ File: Downloading...");

        while (image_size) {
                bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);
                bzero((void *) mp->virt, mp->size);

                xfer_size = min(OBJ_MAX_XFER_SIZE, image_size);

                mb->un.varSLIConfig.be.embedded = 1;
                mbq->nonembed  = NULL;
                mbq->mbox_cmpl = NULL;

                mb->mbxCommand = MBX_SLI_CONFIG;
                mb->mbxOwner = OWN_HOST;

                hdr_req = (mbox_req_hdr_t *)
                    &mb->un.varSLIConfig.be.un_hdr.hdr_req;
                hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
                hdr_req->opcode = COMMON_OPCODE_WRITE_OBJ;
                hdr_req->timeout = 0;

                write_obj = (IOCTL_COMMON_WRITE_OBJECT *)(hdr_req + 1);
                write_obj->params.request.EOF =
                    ((xfer_size == image_size)? 1:0);
                write_obj->params.request.desired_write_length = xfer_size;
                write_obj->params.request.write_offset = image_offset;

                (void) strlcpy((char *)write_obj->params.request.object_name,
                    "/prg", sizeof (write_obj->params.request.object_name));
                BE_SWAP32_BUFFER((uint8_t *)
                    write_obj->params.request.object_name,
                    sizeof (write_obj->params.request.object_name));

                write_obj->params.request.buffer_desc_count = 1;
                write_obj->params.request.buffer_length = xfer_size;
                write_obj->params.request.buffer_addrlo = PADDR_LO(mp->phys);
                write_obj->params.request.buffer_addrhi = PADDR_HI(mp->phys);

                hdr_req->req_length = 116 +
                    (write_obj->params.request.buffer_desc_count * 12);

                bcopy(image_ptr, mp->virt, xfer_size);

                hdr_rsp = (mbox_rsp_hdr_t *)
                    &mb->un.varSLIConfig.be.un_hdr.hdr_rsp;
                write_obj = (IOCTL_COMMON_WRITE_OBJECT *)(hdr_rsp + 1);

                /* Send write request */
                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "OBJ File: Unable to download image. status=%x "
                            "(%x,%x)",
                            mb->mbxStatus, hdr_rsp->status,
                            hdr_rsp->extra_status);

                        return (EMLXS_IMAGE_FAILED);
                }

                /* Check header status */
                if (hdr_rsp->status) {
                        if ((hdr_rsp->status == MBX_RSP_STATUS_FAILED) &&
                            (hdr_rsp->extra_status ==
                            MGMT_ADDI_STATUS_INCOMPATIBLE)) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_download_failed_msg,
                                    "OBJ File: Image file incompatible with "
                                    "adapter hardware.");
                                return (EMLXS_IMAGE_INCOMPATIBLE);
                        }

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "OBJ File: Unable to download image. "
                            "hdr_status=%x,%x size=%d,%d",
                            hdr_rsp->status, hdr_rsp->extra_status,
                            write_obj->params.response.actual_write_length,
                            xfer_size);

                        return (EMLXS_IMAGE_FAILED);
                }

                /* Check response length */
                if (write_obj->params.response.actual_write_length == 0) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "OBJ File: No data actually written.");

                        return (EMLXS_IMAGE_FAILED);
                }

                if (write_obj->params.response.actual_write_length >
                    xfer_size) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "OBJ File: Mismatch in data xfer size. "
                            "size=%d,%d offset=%d",
                            write_obj->params.response.actual_write_length,
                            xfer_size, image_offset);

                        return (EMLXS_IMAGE_FAILED);
                }

                /* Set xfer_size to actual write length */
                xfer_size = write_obj->params.response.actual_write_length;

                image_ptr  += xfer_size;
                image_offset += xfer_size;
                image_size -= xfer_size;

                if (image_size == 0) {
                        cstatus = write_obj->params.response.change_status;
                }
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "OBJ File: Download complete. (cstatus=%d)",
            cstatus);

        if (change_status) {
                *change_status = cstatus;
        }

        return (rval);

} /* emlxs_obj_flash_image() */


static uint32_t
emlxs_obj_validate_image(emlxs_hba_t *hba, caddr_t buffer, uint32_t len,
    emlxs_obj_header_t *obj_hdr_in)
{
        emlxs_port_t *port = &PPORT;
        emlxs_obj_header_t obj_hdr;

        if (obj_hdr_in) {
                bzero(obj_hdr_in, sizeof (emlxs_obj_header_t));
        }

        if (hba->sli_mode != EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid sli_mode. mode=%d", hba->sli_mode);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (hba->model_info.chip & EMLXS_BE_CHIPS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter model. chip=%x", hba->model_info.chip);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (len < sizeof (emlxs_obj_header_t)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Image too small. (%d < %d)",
                    len,  sizeof (emlxs_obj_header_t));

                return (EMLXS_IMAGE_BAD);
        }

        bcopy(buffer, (uint8_t *)&obj_hdr, sizeof (emlxs_obj_header_t));

        /* Swap first 3 words */
        LE_SWAP32_BUFFER((uint8_t *)&obj_hdr, 12);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Object Header: size=%d magic=%04x,%04x type=%02x id=%02x",
            obj_hdr.FileSize,
            obj_hdr.MagicNumHi, obj_hdr.MagicNumLo,
            obj_hdr.FileType,
            obj_hdr.Id);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Object Header: Date=%s Rev=%s",
            obj_hdr.Date,
            obj_hdr.Revision);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Object Header: Name=%s",
            obj_hdr.RevName);

        if ((obj_hdr.MagicNumHi != OBJ_MAGIC_NUM_HI) ||
            (obj_hdr.MagicNumLo != OBJ_MAGIC_NUM_LO)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Wrong Magic Number: %x,%x",
                    obj_hdr.MagicNumHi, obj_hdr.MagicNumLo);

                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (obj_hdr.FileSize != len) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Image too small. (%d < %d)",
                    len, obj_hdr.FileSize);

                return (EMLXS_IMAGE_BAD);
        }

        if ((hba->model_info.chip & EMLXS_LANCER_CHIP) &&
            (obj_hdr.Id != OBJ_LANCER_ID)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter model. chip=%x fwid=%x",
                    hba->model_info.chip,
                    obj_hdr.Id);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (obj_hdr_in) {
                bcopy(&obj_hdr, obj_hdr_in, sizeof (emlxs_obj_header_t));
        }

        return (0);

} /* emlxs_obj_validate_image() */


static int32_t
emlxs_obj_fw_download(emlxs_hba_t *hba, caddr_t buffer, uint32_t len,
    uint32_t offline)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        MAILBOXQ *mbq = NULL;
        MATCHMAP *mp = NULL;
        uint32_t change_status = 0;

        /* For now we will not take the driver offline during a download */
        offline = 0;

        if (hba->sli_mode != EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid sli_mode. mode=%d", hba->sli_mode);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (hba->model_info.chip & EMLXS_BE_CHIPS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid adapter model. chip=%x", hba->model_info.chip);
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        if (buffer == NULL || len == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Empty buffer provided. buf=%p size=%d", buffer, len);
                return (EMLXS_IMAGE_BAD);
        }

        rval = emlxs_obj_validate_image(hba, buffer, len, 0);

        if (rval) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Invalid image provided.");
                return (EMLXS_IMAGE_INCOMPATIBLE);
        }

        /* Allocate resources */

        if ((mbq = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_SLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                offline = 0;
                rval = EMLXS_IMAGE_FAILED;
                goto done;
        }

        if ((mp = emlxs_mem_buf_alloc(hba, OBJ_MAX_XFER_SIZE)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate flash buffer.");

                offline = 0;
                rval = EMLXS_IMAGE_FAILED;
                goto done;
        }

        /*
         * Everything checks out, now to just do it
         */
        if (offline) {
                if (emlxs_offline(hba, 0) != FC_SUCCESS) {

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to take adapter offline.");

                        offline = 0;
                        rval = EMLXS_OFFLINE_FAILED;
                        goto done;
                }
        }

        rval = emlxs_obj_flash_image(hba, buffer, len, mbq, mp, &change_status);

done:
        if (mbq) {
                emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
        }

        if (mp) {
                emlxs_mem_buf_free(hba, mp);
        }

        if (offline) {
                (void) emlxs_online(hba);
        }

        if (rval == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_complete_msg,
                    "Status good.");

                switch (change_status) {
                case CS_NO_RESET:
                        break;

                case CS_REBOOT_RQD:
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fw_updated_msg,
                            "The new firmware will not be activated until "
                            "the adapter is power cycled.");
                        rval = EMLXS_REBOOT_REQUIRED;
                        break;

                case CS_FW_RESET_RQD:
                case CS_PROTO_RESET_RQD:
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fw_updated_msg,
                            "Resetting all ports to activate new firmware.");

                        emlxs_sli4_hba_reset_all(hba, 0);
                }
        }

        return (rval);

} /* emlxs_obj_fw_download() */


extern int32_t
emlxs_cfl_download(emlxs_hba_t *hba, uint32_t region, caddr_t buffer,
    uint32_t len)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbox = NULL;
        MAILBOX *mb;
        uint32_t rval = 0;
        uint32_t region_id;
        uint32_t id;
#ifdef EMLXS_BIG_ENDIAN
        caddr_t local_buffer;
        uint32_t *bptr1;
        uint32_t *bptr2;
        uint32_t i;
#endif /* EMLXS_BIG_ENDIAN */

        if (buffer == NULL || len == 0) {
                return (EMLXS_IMAGE_BAD);
        }

#ifdef EMLXS_BIG_ENDIAN
        /* We need to swap the image buffer before we start */

        /*
         * Use KM_SLEEP to allocate a temporary buffer
         */
        local_buffer = (caddr_t)kmem_zalloc(len, KM_SLEEP);

        /* Perform a 32 bit swap of the image */
        bptr1 = (uint32_t *)local_buffer;
        bptr2 = (uint32_t *)buffer;

        for (i = 0; i < (len / 4); i++) {
                *bptr1 = SWAP32(*bptr2);
                bptr1++;
                bptr2++;
        }

        /* Replace the original buffer */
        buffer = local_buffer;

#endif /* EMLXS_BIG_ENDIAN */

        if (len > 128) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid image length: 0x%x > 128", len);

                return (EMLXS_IMAGE_BAD);
        }

        /* Check the region number */
        if ((region > 2) && (region != 0xff)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid region id: 0x%x", region);

                return (EMLXS_IMAGE_BAD);

        }

        /* Check the image vendor id */
        id = *(int32_t *)buffer;
        if ((id & 0xffff) != 0x10df) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid image id: 0x%x", id);

                return (EMLXS_IMAGE_BAD);
        }

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                rval = 1;

                goto done;
        }

        mb = (MAILBOX *)mbox;

        /*
         * Everything checks out, now to just do it
         */
        if (emlxs_offline(hba, 0) != FC_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to take HBA offline.");

                rval = EMLXS_OFFLINE_FAILED;

                goto done;
        }

        if (EMLXS_SLI_HBA_RESET(hba, 1, 1, 0) != FC_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to restart adapter.");

                rval = EMLXS_OFFLINE_FAILED;

                goto done;
        }

        /* Check if default region is requested */
        if (region == 0xff) {
                /*
                 * Sun-branded Helios and Zypher have different
                 * default PCI region
                 */
                if ((hba->model_info.flags & EMLXS_ORACLE_BRANDED) &&
                    (hba->model_info.chip &
                    (EMLXS_HELIOS_CHIP | EMLXS_ZEPHYR_CHIP))) {
                        region = 2;
                } else {
                        region = 0;
                }
        }

        /* Set region id based on PCI region requested */
        region_id = DEF_PCI_CFG_REGION_ID + region;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "PCI configuration: PCI%d region=%d id=0x%x size=%d", region,
            region_id, id, len);

        /* Copy the data buffer to SLIM */
        WRITE_SLIM_COPY(hba, (uint32_t *)buffer,
            (volatile uint32_t *)((volatile char *)hba->sli.sli3.slim_addr +
            sizeof (MAILBOX)), (len / sizeof (uint32_t)));

#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);
                rval = 1;
        }
#endif  /* FMA_SUPPORT */

        emlxs_format_update_pci_cfg(hba, mbox, region_id, len);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update PCI configuration: Mailbox cmd=%x "
                    "status=%x info=%d", mb->mbxCommand, mb->mbxStatus,
                    mb->un.varUpdateCfg.rsp_info);

                rval = 1;
        }

        (void) emlxs_online(hba);

        if (rval == 0) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_complete_msg,
                    "Status good.");
        }

done:

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

#ifdef EMLXS_BIG_ENDIAN
        /* Free the local buffer */
        kmem_free(local_buffer, len);
#endif /* EMLXS_BIG_ENDIAN */

        return (rval);

} /* emlxs_cfl_download */


static uint32_t
emlxs_valid_cksum(uint32_t *StartAddr, uint32_t *EndAddr)
{
        uint32_t Temp;
        uint32_t CkSum;

        EndAddr++;
        CkSum = SLI_CKSUM_SEED;

        CkSum = (CkSum >> 1) | (CkSum << 31);
        while (StartAddr != EndAddr) {
                CkSum = (CkSum << 1) | (CkSum >> 31);
                Temp = *StartAddr;

                CkSum ^= Temp;
                StartAddr++;
        }

        return (CkSum << 1) | (CkSum >> 31);

} /* emlxs_valid_cksum() */


static void
emlxs_disp_aif_header(emlxs_hba_t *hba, PAIF_HDR AifHdr)
{
        emlxs_port_t *port = &PPORT;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg, "AIF Header: ");
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: compress_br = 0x%x", AifHdr->CompressBr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: reloc_br = 0x%x", AifHdr->RelocBr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: zinit_br = 0x%x", AifHdr->ZinitBr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: entry_br = 0x%x", AifHdr->EntryBr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: area_id = 0x%x", AifHdr->Area_ID);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: rosize = 0x%x", AifHdr->RoSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: dbgsize = 0x%x", AifHdr->DbgSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: zinitsize = 0x%x", AifHdr->ZinitSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: dbgtype = 0x%x", AifHdr->DbgType);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: imagebase = 0x%x", AifHdr->ImageBase);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: area_size = 0x%x", AifHdr->Area_Size);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: address_mode = 0x%x", AifHdr->AddressMode);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: database = 0x%x", AifHdr->DataBase);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: aversion = 0x%x", AifHdr->AVersion);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: spare2 = 0x%x", AifHdr->Spare2);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: debug_swi = 0x%x", AifHdr->DebugSwi);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: zinitcode[0] = 0x%x", AifHdr->ZinitCode[0]);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "AIF Header: zinitcode[1] = 0x%x", AifHdr->ZinitCode[1]);

} /* emlxs_disp_aif_header() */



static void
emlxs_dump_image_header(emlxs_hba_t *hba, PIMAGE_HDR image)
{
        emlxs_port_t *port = &PPORT;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg, "Img Header: ");
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: BlockSize = 0x%x", image->BlockSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: PROG_ID Type = 0x%x", image->Id.Type);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: PROG_ID Id = 0x%x", image->Id.Id);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: PROG_ID Ver = 0x%x", image->Id.Ver);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: PROG_ID Rev = 0x%x", image->Id.Rev);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: PROG_ID revcomp = 0x%x", image->Id.un.revcomp);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: Flags = 0x%x", image->Flags);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: EntryAdr = 0x%x", image->EntryAdr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: InitAdr = 0x%x", image->InitAdr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: ExitAdr = 0x%x", image->ExitAdr);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: ImageBase = 0x%x", image->ImageBase);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: ImageSize = 0x%x", image->ImageSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: ZinitSize = 0x%x", image->ZinitSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: RelocSize = 0x%x", image->RelocSize);
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Img Header: HdrCks = 0x%x", image->HdrCks);

} /* emlxs_dump_image_header() */


static void
emlxs_format_dump(emlxs_hba_t *hba, MAILBOXQ *mbq, uint32_t Type,
    uint32_t RegionId, uint32_t WordCount, uint32_t BaseAddr)
{

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                MAILBOX4 *mb = (MAILBOX4 *)mbq;

                /* Clear the local dump_region */
                bzero(hba->sli.sli4.dump_region.virt,
                    hba->sli.sli4.dump_region.size);

                bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);

                mb->mbxCommand = MBX_DUMP_MEMORY;
                mb->un.varDmp4.type = Type;
                mb->un.varDmp4.entry_index = BaseAddr;
                mb->un.varDmp4.region_id = RegionId;

                mb->un.varDmp4.available_cnt = min((WordCount*4),
                    hba->sli.sli4.dump_region.size);
                mb->un.varDmp4.addrHigh =
                    PADDR_HI(hba->sli.sli4.dump_region.phys);
                mb->un.varDmp4.addrLow =
                    PADDR_LO(hba->sli.sli4.dump_region.phys);
                mb->un.varDmp4.rsp_cnt = 0;

                mb->mbxOwner = OWN_HOST;

        } else {
                MAILBOX *mb = (MAILBOX *)mbq;

                bzero((void *)mb, MAILBOX_CMD_BSIZE);

                mb->mbxCommand = MBX_DUMP_MEMORY;
                mb->un.varDmp.type = Type;
                mb->un.varDmp.region_id = RegionId;
                mb->un.varDmp.word_cnt = WordCount;
                mb->un.varDmp.base_adr = BaseAddr;
                mb->mbxOwner = OWN_HOST;
        }

        mbq->mbox_cmpl = NULL; /* no cmpl needed */

        return;

} /* emlxs_format_dump() */


/* ARGSUSED */
static uint32_t
emlxs_start_abs_download(emlxs_hba_t *hba,
    PAIF_HDR AifHdr,
    caddr_t Buffer,
    uint32_t len,
    PWAKE_UP_PARMS WakeUpParms)
{
        emlxs_port_t *port = &PPORT;
        uint32_t DlByteCount = AifHdr->RoSize + AifHdr->RwSize;
        uint32_t *Src;
        uint32_t *Dst;
        caddr_t DataBuffer = NULL;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        uint32_t rval = 1;
        uint32_t SegSize = DL_SLIM_SEG_BYTE_COUNT;
        uint32_t DlToAddr = AifHdr->ImageBase;
        uint32_t DlCount;
        uint32_t i;
        WAKE_UP_PARMS AbsWakeUpParms;
        int32_t AbsChangeParams;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Performing absolute download...");

        if ((DataBuffer = (caddr_t)kmem_zalloc(DL_SLIM_SEG_BYTE_COUNT,
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate data buffer.");

                return (rval);
        }

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                kmem_free(DataBuffer, DL_SLIM_SEG_BYTE_COUNT);

                return (rval);
        }
        mb = (MAILBOX *)mbox;

        AbsChangeParams = emlxs_build_parms(Buffer,
            &AbsWakeUpParms, len, AifHdr);

        Buffer += sizeof (AIF_HDR);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg, "Erasing flash...");

        if (AifHdr->ImageBase == 0x20000) {
                /* DWC File */
                emlxs_format_prog_flash(mbox, 0x20000, 0x50000, ERASE_FLASH, 0,
                    0, 0, NULL, 0);
        } else {
                emlxs_format_prog_flash(mbox, DlToAddr, DlByteCount,
                    ERASE_FLASH, 0, 0, 0, NULL, 0);
        }

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to erase Flash: Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                rval = 1;

                goto EXIT_ABS_DOWNLOAD;
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Programming flash...");

        while (DlByteCount) {

                if (DlByteCount > SegSize) {
                        DlCount = SegSize;
                } else {
                        DlCount = DlByteCount;
                }
                DlByteCount -= DlCount;

                Dst = (uint32_t *)DataBuffer;
                Src = (uint32_t *)Buffer;

                for (i = 0; i < (DlCount / 4); i++) {
                        *Dst = *Src;
                        Dst++;
                        Src++;
                }

                WRITE_SLIM_COPY(hba, (uint32_t *)DataBuffer,
                    (volatile uint32_t *)
                    ((volatile char *)hba->sli.sli3.slim_addr +
                    sizeof (MAILBOX)), (DlCount / sizeof (uint32_t)));

                emlxs_format_prog_flash(mbox, DlToAddr, DlCount,
                    PROGRAM_FLASH, (DlByteCount) ? 0 : 1, 0, DlCount, NULL, 0);

                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to program Flash: Mailbox cmd=%x status=%x",
                            mb->mbxCommand, mb->mbxStatus);

                        rval = 1;

                        goto EXIT_ABS_DOWNLOAD;
                }

                Buffer += DlCount;
                DlToAddr += DlCount;
        }

#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);

                rval = 1;

                goto EXIT_ABS_DOWNLOAD;
        }
#endif  /* FMA_SUPPORT */

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg, "Updating params...");

        if (AbsChangeParams) {
                rval =
                    emlxs_update_wakeup_parms(hba, &AbsWakeUpParms,
                    WakeUpParms);
        }

EXIT_ABS_DOWNLOAD:
        if (DataBuffer) {
                kmem_free(DataBuffer, DL_SLIM_SEG_BYTE_COUNT);
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_start_abs_download() */


/* ARGSUSED */
static void
emlxs_format_prog_flash(MAILBOXQ *mbq,
    uint32_t Base,
    uint32_t DlByteCount,
    uint32_t Function,
    uint32_t Complete,
    uint32_t BdeAddress,
    uint32_t BdeSize,
    PROG_ID *ProgId,
    uint32_t keep)
{
        MAILBOX *mb = (MAILBOX *)mbq;

        bzero((void *)mb, MAILBOX_CMD_BSIZE);

        if (ProgId) {
                mb->mbxCommand = MBX_DOWN_LOAD;
        } else {
                mb->mbxCommand = MBX_LOAD_SM;
        }

        mb->un.varLdSM.load_cmplt = Complete;
        mb->un.varLdSM.method = DL_FROM_SLIM;
        mb->un.varLdSM.update_flash = 1;
        mb->un.varLdSM.erase_or_prog = Function;
        mb->un.varLdSM.dl_to_adr = Base;
        mb->un.varLdSM.dl_len = DlByteCount;
        mb->un.varLdSM.keep = keep;

        if (BdeSize) {
                mb->un.varLdSM.un.dl_from_slim_offset = DL_FROM_SLIM_OFFSET;
        } else if (ProgId) {
                mb->un.varLdSM.un.prog_id = *ProgId;
        } else {
                mb->un.varLdSM.un.dl_from_slim_offset = 0;
        }

        mb->mbxOwner = OWN_HOST;
        mbq->mbox_cmpl = NULL;

} /* emlxs_format_prog_flash() */


static void
emlxs_format_update_parms(MAILBOXQ *mbq, PWAKE_UP_PARMS WakeUpParms)
{
        MAILBOX *mb = (MAILBOX *)mbq;

        bzero((void *)mb, MAILBOX_CMD_BSIZE);

        mb->mbxCommand = MBX_UPDATE_CFG;
        mb->un.varUpdateCfg.req_type = UPDATE_DATA;
        mb->un.varUpdateCfg.region_id = WAKE_UP_PARMS_REGION_ID;
        mb->un.varUpdateCfg.entry_len = sizeof (WAKE_UP_PARMS);
        mb->un.varUpdateCfg.byte_len = sizeof (WAKE_UP_PARMS);

        bcopy((caddr_t)WakeUpParms,
            (caddr_t)&(mb->un.varUpdateCfg.cfg_data),
            sizeof (WAKE_UP_PARMS));
        mbq->mbox_cmpl = NULL;

} /* emlxs_format_update_parms () */


/* ARGSUSED */
static void
emlxs_format_update_pci_cfg(emlxs_hba_t *hba, MAILBOXQ *mbq,
    uint32_t region_id, uint32_t size)
{
        MAILBOX *mb = (MAILBOX *)mbq;

        bzero((void *)mb, MAILBOX_CMD_BSIZE);

        mb->mbxCommand = MBX_UPDATE_CFG;
        mb->un.varUpdateCfg.Vbit = 1;
        mb->un.varUpdateCfg.Obit = 1;
        mb->un.varUpdateCfg.cfg_data = DL_FROM_SLIM_OFFSET;
        mb->un.varUpdateCfg.req_type = UPDATE_DATA;
        mb->un.varUpdateCfg.region_id = region_id;
        mb->un.varUpdateCfg.entry_len = size;
        mb->un.varUpdateCfg.byte_len = size;
        mbq->mbox_cmpl = NULL;

} /* emlxs_format_update_pci_cfg() */



static uint32_t
emlxs_update_boot_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    PROG_ID * prog_id, uint32_t proc_erom)
{
        emlxs_port_t *port = &PPORT;
        MAILBOX *mb;
        MAILBOXQ *mbox;
        uint32_t rval = 0;
        PROG_ID old_prog_id;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX *)mbox;

        if (proc_erom && !(hba->model_info.chip &
            (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP))) {
                WakeUpParms->u1.EROM_prog_id = *prog_id;
                (void) emlxs_update_exp_rom(hba, WakeUpParms);
        }

        old_prog_id = WakeUpParms->u0.boot_bios_id;
        WakeUpParms->u0.boot_bios_id = *prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update boot wakeup parms: Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                WakeUpParms->u0.boot_bios_id = old_prog_id;
                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_boot_wakeup_parms() */



static uint32_t
emlxs_update_ff_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    PROG_ID *prog_id)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        PROG_ID old_prog_id;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "FF: Updating parms...");

        mb = (MAILBOX *)mbox;

        old_prog_id = WakeUpParms->prog_id;
        WakeUpParms->prog_id = *prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update wakeup parameters: Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                WakeUpParms->prog_id = old_prog_id;
                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_ff_wakeup_parms() */


static uint32_t
emlxs_update_sli1_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    PROG_ID * prog_id)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        PROG_ID old_prog_id;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "SLI1: Updating parms...");

        mb = (MAILBOX *)mbox;

        old_prog_id = WakeUpParms->sli1_prog_id;
        WakeUpParms->sli1_prog_id = *prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update wakeup parameters. Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                WakeUpParms->sli1_prog_id = old_prog_id;
                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_sli1_wakeup_parms() */


static uint32_t
emlxs_update_sli2_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    PROG_ID * prog_id)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        PROG_ID old_prog_id;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "SLI2: Updating parms...");

        mb = (MAILBOX *)mbox;

        old_prog_id = WakeUpParms->sli2_prog_id;
        WakeUpParms->sli2_prog_id = *prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update wakeup parameters. Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                WakeUpParms->sli2_prog_id = old_prog_id;
                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_sli2_wakeup_parms() */


static uint32_t
emlxs_update_sli3_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    PROG_ID *prog_id)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        PROG_ID old_prog_id;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "SLI3: Updating parms...");

        mb = (MAILBOX *)mbox;

        old_prog_id = WakeUpParms->sli3_prog_id;
        WakeUpParms->sli3_prog_id = *prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update wakeup parameters. Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                WakeUpParms->sli3_prog_id = old_prog_id;
                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_sli3_wakeup_parms() */


static uint32_t
emlxs_update_sli4_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    PROG_ID *prog_id)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        PROG_ID old_prog_id;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
            "SLI4: Updating parms...");

        mb = (MAILBOX *)mbox;

        old_prog_id = WakeUpParms->sli4_prog_id;
        WakeUpParms->sli4_prog_id = *prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update wakeup parameters. Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                WakeUpParms->sli4_prog_id = old_prog_id;
                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_sli4_wakeup_parms() */


static uint32_t
emlxs_clean_flash(emlxs_hba_t *hba,
    PWAKE_UP_PARMS OldWakeUpParms, PWAKE_UP_PARMS NewWakeUpParms)
{
        emlxs_port_t *port = &PPORT;
        PROG_ID load_list[MAX_LOAD_ENTRY];
        PROG_ID *wakeup_list[MAX_LOAD_ENTRY];
        uint32_t count;
        uint32_t i;
        uint32_t j;
        uint32_t k = 0;
        uint32_t *wptr;

        if (!NewWakeUpParms) {
                return (1);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
            "Cleaning flash...");

        /* If old wakeup parameter list is available, */
        /* then cleanup old entries */
        if (OldWakeUpParms) {
                if (bcmp(&OldWakeUpParms->prog_id, &NewWakeUpParms->prog_id,
                    sizeof (PROG_ID))) {

                        wptr = (uint32_t *)&OldWakeUpParms->prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "OLD:      prog_id: 0x%08x 0x%08x  Removing.",
                            wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba,
                            &OldWakeUpParms->prog_id);
                }

                if (bcmp(&OldWakeUpParms->u0.boot_bios_id,
                    &NewWakeUpParms->u0.boot_bios_id, sizeof (PROG_ID))) {

                        wptr = (uint32_t *)&OldWakeUpParms->u0.boot_bios_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "OLD: boot_bios_id: 0x%08x 0x%08x  Removing.",
                            wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba,
                            &OldWakeUpParms->u0.boot_bios_id);
                }

                if (bcmp(&OldWakeUpParms->sli1_prog_id,
                    &NewWakeUpParms->sli1_prog_id, sizeof (PROG_ID))) {

                        wptr = (uint32_t *)&OldWakeUpParms->sli1_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "OLD: sli1_prog_id: 0x%08x 0x%08x  Removing.",
                            wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba,
                            &OldWakeUpParms->sli1_prog_id);
                }

                if (bcmp(&OldWakeUpParms->sli2_prog_id,
                    &NewWakeUpParms->sli2_prog_id, sizeof (PROG_ID))) {

                        wptr = (uint32_t *)&OldWakeUpParms->sli2_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "OLD: sli2_prog_id: 0x%08x 0x%08x  Removing.",
                            wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba,
                            &OldWakeUpParms->sli2_prog_id);
                }

                if (bcmp(&OldWakeUpParms->sli3_prog_id,
                    &NewWakeUpParms->sli3_prog_id, sizeof (PROG_ID))) {

                        wptr = (uint32_t *)&OldWakeUpParms->sli3_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "OLD: sli3_prog_id: 0x%08x 0x%08x  Removing.",
                            wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba,
                            &OldWakeUpParms->sli3_prog_id);
                }

                if (bcmp(&OldWakeUpParms->sli4_prog_id,
                    &NewWakeUpParms->sli4_prog_id, sizeof (PROG_ID))) {

                        wptr = (uint32_t *)&OldWakeUpParms->sli4_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "OLD: sli4_prog_id: 0x%08x 0x%08x  Removing.",
                            wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba,
                            &OldWakeUpParms->sli4_prog_id);
                }

                return (0);
        }

        /* Otherwise use the current load list */
        count = emlxs_get_load_list(hba, load_list);

        if (!count) {
                return (1);
        }

        /* Init the wakeup list */
        wptr = (uint32_t *)&NewWakeUpParms->prog_id;
        if (*wptr) {
                wakeup_list[k++] = &NewWakeUpParms->prog_id;
        }

        wptr = (uint32_t *)&NewWakeUpParms->u0.boot_bios_id;
        if (*wptr) {
                wakeup_list[k++] = &NewWakeUpParms->u0.boot_bios_id;
        }

        wptr = (uint32_t *)&NewWakeUpParms->sli1_prog_id;
        if (*wptr) {
                wakeup_list[k++] = &NewWakeUpParms->sli1_prog_id;
        }

        wptr = (uint32_t *)&NewWakeUpParms->sli2_prog_id;
        if (*wptr) {
                wakeup_list[k++] = &NewWakeUpParms->sli2_prog_id;
        }

        wptr = (uint32_t *)&NewWakeUpParms->sli3_prog_id;
        if (*wptr) {
                wakeup_list[k++] = &NewWakeUpParms->sli3_prog_id;
        }

        wptr = (uint32_t *)&NewWakeUpParms->sli4_prog_id;
        if (*wptr) {
                wakeup_list[k++] = &NewWakeUpParms->sli4_prog_id;
        }

        if (k == 0) {
                return (0);
        }

        /* Match load list to wakeup list */
        for (i = 0; i < count; i++) {

                wptr = (uint32_t *)&load_list[i];

                for (j = 0; j < k; j++) {
                        if (bcmp((uint8_t *)wakeup_list[j],
                            (uint8_t *)&load_list[i], sizeof (PROG_ID)) == 0) {
                                break;
                        }
                }

                /* No match */
                if (j == k) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "Load List[%d]: %08x %08x  Removing.",
                            i, wptr[0], wptr[1]);

                        (void) emlxs_delete_load_entry(hba, &load_list[i]);
                } else {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "Load List[%d]: %08x %08x  Preserving.",
                            i, wptr[0], wptr[1]);
                }
        }

        return (0);

} /* emlxs_clean_flash() */


/* ARGSUSED */
static uint32_t
emlxs_start_rel_download(emlxs_hba_t *hba,
    PIMAGE_HDR ImageHdr,
    caddr_t Buffer,
    PWAKE_UP_PARMS WakeUpParms,
    uint32_t dwc_flag)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        uint32_t *Src;
        uint32_t *Dst;
        caddr_t DataBuffer = NULL;
        uint32_t rval = 0;
        uint32_t DlByteCount;
        uint32_t SegSize = DL_SLIM_SEG_BYTE_COUNT;
        uint32_t DlCount;
        uint32_t i;
        uint32_t *wptr;

        wptr = (uint32_t *)&ImageHdr->Id;
        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Relative download: %08x %08x", wptr[0], wptr[1]);

        if ((DataBuffer = (caddr_t)kmem_zalloc(DL_SLIM_SEG_BYTE_COUNT,
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate data buffer.");

                return (1);
        }

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                kmem_free(DataBuffer, DL_SLIM_SEG_BYTE_COUNT);

                return (1);
        }

        mb = (MAILBOX *)mbox;

        DlByteCount = ImageHdr->BlockSize;

        emlxs_format_prog_flash(mbox, 0, DlByteCount, ERASE_FLASH, 0, 0, 0,
            &ImageHdr->Id, 0);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "  Erasing flash...");

        rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0);

        if (rval) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to erase flash. Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                goto EXIT_REL_DOWNLOAD;
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "  Programming flash...");

        while (DlByteCount) {
                if (DlByteCount > SegSize) {
                        DlCount = SegSize;
                } else {
                        DlCount = DlByteCount;
                }
                DlByteCount -= DlCount;

                Dst = (uint32_t *)DataBuffer;
                Src = (uint32_t *)Buffer;

                for (i = 0; i < (DlCount / 4); i++) {
                        *Dst = *Src;
                        Dst++;
                        Src++;
                }

                WRITE_SLIM_COPY(hba, (uint32_t *)DataBuffer,
                    (volatile uint32_t *)
                    ((volatile char *)hba->sli.sli3.slim_addr +
                    sizeof (MAILBOX)), (DlCount / sizeof (uint32_t)));

                emlxs_format_prog_flash(mbox,
                    0,
                    DlCount,
                    PROGRAM_FLASH,
                    (DlByteCount) ? 0 : 1,
                    0, DlCount, &ImageHdr->Id, dwc_flag);

                rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0);

                if (rval) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to program flash. Mailbox cmd=%x status=%x",
                            mb->mbxCommand, mb->mbxStatus);

                        goto EXIT_REL_DOWNLOAD;
                }

                Buffer += DlCount;
        }

#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);

                rval = 1;

                goto EXIT_REL_DOWNLOAD;
        }
#endif  /* FMA_SUPPORT */

        /* Update wakeup parameters */
        switch (ImageHdr->Id.Type) {
        case TEST_PROGRAM:
                break;

        case FUNC_FIRMWARE:
                if (!dwc_flag) {
                        rval = emlxs_update_ff_wakeup_parms(hba, WakeUpParms,
                            &ImageHdr->Id);
                } else {
                        WakeUpParms->prog_id = ImageHdr->Id;
                }
                break;

        case BOOT_BIOS:
                if (!dwc_flag) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                            "BOOT: Updating parms...");

                        rval = emlxs_update_boot_wakeup_parms(hba, WakeUpParms,
                            &ImageHdr->Id, 1);
                } else {
                        if (hba->wakeup_parms.u0.boot_bios_wd[0]) {
                                WakeUpParms->u0.boot_bios_id = ImageHdr->Id;
                        }

                        if (!(hba->model_info.chip &
                            (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP))) {
                                WakeUpParms->u1.EROM_prog_id = ImageHdr->Id;
                        }
                }
                break;

        case SLI1_OVERLAY:
                if (!dwc_flag) {
                        rval = emlxs_update_sli1_wakeup_parms(hba, WakeUpParms,
                            &ImageHdr->Id);
                } else {
                        WakeUpParms->sli1_prog_id = ImageHdr->Id;
                }
                break;

        case SLI2_OVERLAY:
                if (!dwc_flag) {
                        rval = emlxs_update_sli2_wakeup_parms(hba, WakeUpParms,
                            &ImageHdr->Id);
                } else {
                        WakeUpParms->sli2_prog_id = ImageHdr->Id;
                }
                break;

        case SLI3_OVERLAY:
                if (!dwc_flag) {
                        rval = emlxs_update_sli3_wakeup_parms(hba, WakeUpParms,
                            &ImageHdr->Id);
                } else {
                        WakeUpParms->sli3_prog_id = ImageHdr->Id;
                }
                break;

        case SLI4_OVERLAY:
                if (!dwc_flag) {
                        rval = emlxs_update_sli4_wakeup_parms(hba, WakeUpParms,
                            &ImageHdr->Id);
                } else {
                        WakeUpParms->sli4_prog_id = ImageHdr->Id;
                }
                break;

        default:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                    "Image type not supported. Type=%x", ImageHdr->Id.Type);

                break;
        }

EXIT_REL_DOWNLOAD:
        if (DataBuffer) {
                kmem_free(DataBuffer, DL_SLIM_SEG_BYTE_COUNT);
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_start_rel_download() */


static uint32_t
emlxs_proc_rel_2mb(emlxs_hba_t *hba, caddr_t buffer, emlxs_fw_image_t *fw_image)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;
        WAKE_UP_PARMS RelWakeUpParms;
        WAKE_UP_PARMS WakeUpParms;
        uint32_t i;
        IMAGE_HDR ImageHdr;
        caddr_t bptr;
        uint32_t flash_cleaned = 0;

        if (emlxs_read_wakeup_parms(hba, &WakeUpParms, 0)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to get wakeup parameters.");

                return (EMLXS_IMAGE_FAILED);
        }

download:

        bcopy(&WakeUpParms, &RelWakeUpParms, sizeof (WAKE_UP_PARMS));

        for (i = 0; i < MAX_PROG_TYPES; i++) {
                if (!fw_image->prog[i].version) {
                        continue;
                }

                bptr = buffer + fw_image->prog[i].offset;

                bcopy(bptr, &ImageHdr, sizeof (IMAGE_HDR));

                rval = emlxs_start_rel_download(hba, &ImageHdr, bptr,
                    &RelWakeUpParms, 1);

                if (rval) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_download_failed_msg,
                            "Failed to program flash.");

                        if ((rval == NO_FLASH_MEM_AVAIL) && !flash_cleaned) {
                                /* Cleanup using current load list */
                                (void) emlxs_clean_flash(hba, 0, &WakeUpParms);

                                flash_cleaned = 1;
                                goto download;
                        }

                        return (EMLXS_IMAGE_FAILED);
                }
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_detail_msg,
            "Updating wakeup parameters.");

        if (emlxs_update_wakeup_parms(hba, &RelWakeUpParms,
            &RelWakeUpParms)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update parameters.");

                return (EMLXS_IMAGE_FAILED);
        }

        /* Cleanup using old wakeup paramters */
        (void) emlxs_clean_flash(hba, &WakeUpParms, &RelWakeUpParms);

        return (0);

} /* emlxs_proc_rel_2mb() */


#define FLASH_POLLING_BIT       0x80
#define FLASH_ERROR_BIT         0x20

typedef struct _flash_t
{
        uint32_t        offset;
        uint8_t         val;
} flash_t;



static uint32_t
emlxs_write_fcode_flash(emlxs_hba_t *hba,
    PIMAGE_HDR ImageHdr, caddr_t Buffer)
{
        emlxs_port_t *port = &PPORT;
        uint8_t bb;
        uint8_t cc;
        uint8_t *src;
        uint32_t DlByteCount = ImageHdr->BlockSize;
        uint32_t i;
        uint32_t j;
        uint32_t k;

        flash_t wr[3] = {
                {0x555, 0xaa},
                {0x2aa, 0x55},
                {0x555, 0xa0}
        };

        /* Load Fcode */
        src = (uint8_t *)Buffer + sizeof (IMAGE_HDR);
        for (i = 0; i < DlByteCount; i++) {
                for (k = 0; k < 3; k++) {
                        SBUS_WRITE_FLASH_COPY(hba, wr[k].offset, wr[k].val);
                }

                /* Reverse Endian word alignment */
                j = (i & 3) ^ 3;

                bb = src[j];

                if (j == 0) {
                        src += 4;
                }

                SBUS_WRITE_FLASH_COPY(hba, i, bb);

                /* check for complete */
                for (;;) {
                        BUSYWAIT_US(20);

                        cc = SBUS_READ_FLASH_COPY(hba, i);

                        /* If data matches then continue */
                        if (cc == bb) {
                                break;
                        }

                        /* Polling bit will be inverse final value */
                        /* while active */
                        if ((cc ^ bb) & FLASH_POLLING_BIT) {
                                /* Still busy */

                                /* Check for error bit */
                                if (cc & FLASH_ERROR_BIT) {
                                        /* Read data one more time */
                                        cc = SBUS_READ_FLASH_COPY(hba, i);

                                        /* Check if data matches */
                                        if (cc == bb) {
                                                break;
                                        }

                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_download_failed_msg,
                                            "FCode write error: offset:%x "
                                            "wrote:%x read:%x\n", i, bb, cc);

                                        return (1);
                                }
                        }
                }
        }

        /* Load Header */
        src = (uint8_t *)ImageHdr;

        for (i = (0xFFFF - sizeof (IMAGE_HDR)); i < 0xFFFF; i++) {
                for (k = 0; k < 3; k++) {
                        SBUS_WRITE_FLASH_COPY(hba, wr[k].offset, wr[k].val);
                }

                /* Reverse Endian word alignment */
                j = (i & 3) ^ 3;

                bb = src[j];

                if (j == 0) {
                        src += 4;
                }

                SBUS_WRITE_FLASH_COPY(hba, i, bb);

                /* check for complete */
                for (;;) {
                        BUSYWAIT_US(20);

                        cc = SBUS_READ_FLASH_COPY(hba, i);

                        /* If data matches then continue */
                        if (cc == bb) {
                                break;
                        }

                        /* Polling bit will be inverse final value */
                        /* while active */
                        if ((cc ^ bb) & FLASH_POLLING_BIT) {
                                /* Still busy */

                                /* Check for error bit */
                                if (cc & FLASH_ERROR_BIT) {
                                        /* Read data one more time */
                                        cc = SBUS_READ_FLASH_COPY(hba, i);

                                        /* Check if data matches */
                                        if (cc == bb) {
                                                break;
                                        }

                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_download_failed_msg,
                                            "FCode write error: offset:%x "
                                            "wrote:%x read:%x\n", i, bb, cc);

                                        return (1);
                                }
                        }
                }
        }

#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.sbus_flash_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);
                return (1);
        }
#endif  /* FMA_SUPPORT */

        return (0);

} /* emlxs_write_fcode_flash() */



static uint32_t
emlxs_erase_fcode_flash(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        int32_t i, j;
        uint8_t cc;
        uint32_t offset;

        flash_t ef[6] = {
                {0x555, 0xaa},
                {0x2aa, 0x55},
                {0x555, 0x80},
                {0x555, 0xaa},
                {0x2aa, 0x55},
                {0x555, 0x10}
        };

        /* Auto select */
        flash_t as[3] = {
                {0x555, 0xaa},
                {0x2aa, 0x55},
                {0x555, 0x90}
        };


        /* Check Manufacturers Code */
        for (i = 0; i < 3; i++) {
                SBUS_WRITE_FLASH_COPY(hba, as[i].offset, as[i].val);
        }

        cc = SBUS_READ_FLASH_COPY(hba, 0);

        /* Check Device Code */
        for (i = 0; i < 3; i++) {
                SBUS_WRITE_FLASH_COPY(hba, as[i].offset, as[i].val);
        }

        cc = SBUS_READ_FLASH_COPY(hba, 1);


        /* Check block protections (up to 4 16K blocks = 64K) */
        for (j = 0; j < 4; j++) {
                for (i = 0; i < 3; i++) {
                        SBUS_WRITE_FLASH_COPY(hba, as[i].offset, as[i].val);
                }

                offset = (j << 14) | 0x2;

                cc = SBUS_READ_FLASH_COPY(hba, offset);

                if (cc == 0x01) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Block %d is protected and can't be erased.", j);
                }
        }

        /* Write erase flash sequence */
        for (i = 0; i < 6; i++) {
                SBUS_WRITE_FLASH_COPY(hba, ef[i].offset, ef[i].val);
        }

        /* check for complete */
        for (;;) {
                /* Delay 3 seconds */
                BUSYWAIT_MS(3000);

                cc = SBUS_READ_FLASH_COPY(hba, 0);


                /* If data matches then continue; */
                if (cc == 0xff) {
                        break;
                }

                /* Polling bit will be inverse final value while active */
                if ((cc ^ 0xff) & FLASH_POLLING_BIT) {
                        /* Still busy */

                        /* Check for error bit */
                        if (cc & FLASH_ERROR_BIT) {
                                /* Read data one more time */
                                cc = SBUS_READ_FLASH_COPY(hba, 0);

                                /* Check if data matches */
                                if (cc == 0xff) {
                                        break;
                                }

                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_download_failed_msg,
                                    "FCode write error: offset:%x wrote:%x "
                                    "read:%x\n", i, 0xff, cc);

                                return (1);
                        }
                }
        }

#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.sbus_flash_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);
                return (1);
        }
#endif  /* FMA_SUPPORT */

        return (0);

} /* emlxs_erase_fcode_flash() */


static uint32_t
emlxs_delete_load_entry(emlxs_hba_t *hba, PROG_ID *progId)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbox = NULL;
        MAILBOX *mb;
        uint32_t rval = 0;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX *)mbox;
        mb->mbxCommand = MBX_DEL_LD_ENTRY;
        mb->un.varDelLdEntry.list_req = FLASH_LOAD_LIST;
        mb->un.varDelLdEntry.prog_id = *progId;

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to delete load entry: Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_delete_load_entry() */


extern uint32_t
emlxs_get_load_list(emlxs_hba_t *hba, PROG_ID *load_list)
{
        emlxs_port_t *port = &PPORT;
        LOAD_ENTRY *LoadEntry;
        LOAD_LIST *LoadList = NULL;
        uint32_t i;
        uint32_t count = 0;

        bzero(load_list, (sizeof (PROG_ID) * MAX_LOAD_ENTRY));

        if ((LoadList = (LOAD_LIST *)kmem_zalloc(sizeof (LOAD_LIST),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to allocate LOADLIST buffer.");

                goto done;
        }

        if (emlxs_read_load_list(hba, LoadList)) {
                goto done;
        }

        for (i = 0; i < LoadList->entry_cnt; i++) {
                LoadEntry = &LoadList->load_entry[i];
                if ((LoadEntry->un.wd[0] != 0) &&
                    (LoadEntry->un.wd[0] != 0xffffffff)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Load List[%d]: %08x %08x", count,
                            LoadEntry->un.wd[0], LoadEntry->un.wd[1]);

                        load_list[count++] = LoadEntry->un.id;
                }
        }

done:

        if (LoadList) {
                kmem_free(LoadList, sizeof (LOAD_LIST));
        }

        return (count);

} /* emlxs_get_load_list() */


extern uint32_t
emlxs_read_wakeup_parms(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms,
    uint32_t verbose)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        uint32_t rval = 0;
        uint32_t *wd;

        bzero(WakeUpParms, sizeof (WAKE_UP_PARMS));

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX *)mbox;

        emlxs_format_dump(hba, mbox,
            DMP_NV_PARAMS,
            WAKE_UP_PARMS_REGION_ID,
            sizeof (WAKE_UP_PARMS) / sizeof (uint32_t), 0);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to get parameters: Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                if (mb->un.varDmp.word_cnt == (uint32_t)CFG_DATA_NO_REGION) {
                        rval = (uint32_t)CFG_DATA_NO_REGION;
                } else {
                        rval = 1;
                }
        } else {
                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        EMLXS_MPDATA_SYNC(hba->sli.sli4.dump_region.dma_handle,
                            0, hba->sli.sli4.dump_region.size,
                            DDI_DMA_SYNC_FORKERNEL);

                        bcopy((caddr_t)hba->sli.sli4.dump_region.virt,
                            (caddr_t)WakeUpParms, sizeof (WAKE_UP_PARMS));
                } else {
                        bcopy((caddr_t)&mb->un.varDmp.resp_offset,
                            (caddr_t)WakeUpParms, sizeof (WAKE_UP_PARMS));
                }

                if (verbose) {
                        wd = (uint32_t *)&WakeUpParms->prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup:      prog_id=%08x %08x", wd[0], wd[1]);

                        wd = (uint32_t *)&WakeUpParms->u0.boot_bios_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup: boot_bios_id=%08x %08x", wd[0], wd[1]);

                        wd = (uint32_t *)&WakeUpParms->sli1_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup: sli1_prog_id=%08x %08x", wd[0], wd[1]);

                        wd = (uint32_t *)&WakeUpParms->sli2_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup: sli2_prog_id=%08x %08x", wd[0], wd[1]);

                        wd = (uint32_t *)&WakeUpParms->sli3_prog_id;
                        if (wd[0] || wd[1]) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_init_debug_msg,
                                    "Wakeup: sli3_prog_id=%08x %08x", wd[0],
                                    wd[1]);
                        }

                        wd = (uint32_t *)&WakeUpParms->sli4_prog_id;
                        if (wd[0] || wd[1]) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_init_debug_msg,
                                    "Wakeup: sli4_prog_id=%08x %08x", wd[0],
                                    wd[1]);
                        }

                        wd = (uint32_t *)&WakeUpParms->u1.EROM_prog_id;
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup: EROM_prog_id=%08x %08x", wd[0], wd[1]);

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup: pci_cfg_rsvd=%x",
                            WakeUpParms->pci_cfg_rsvd);
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup:  use_hdw_def=%x",
                            WakeUpParms->use_hdw_def);
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup:  pci_cfg_sel=%x",
                            WakeUpParms->pci_cfg_sel);
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
                            "Wakeup:   cfg_lookup=%x",
                            WakeUpParms->pci_cfg_lookup_sel);
                }
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

#ifdef FMA_SUPPORT
        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                if (emlxs_fm_check_dma_handle(hba,
                    hba->sli.sli4.dump_region.dma_handle) != DDI_FM_OK) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_invalid_dma_handle_msg,
                            "read_wakeup_parms: hdl=%p",
                            hba->sli.sli4.dump_region.dma_handle);
                        rval = 1;
                }
        }
#endif  /* FMA_SUPPORT */

        return (rval);

} /* emlxs_read_wakeup_parms() */


static uint32_t
emlxs_read_load_list(emlxs_hba_t *hba, LOAD_LIST *LoadList)
{
        emlxs_port_t *port = &PPORT;
        LOAD_ENTRY *LoadEntry;
        uint32_t *Uptr;
        uint32_t CurEntryAddr;
        MAILBOXQ *mbox = NULL;
        MAILBOX *mb;

        bzero((caddr_t)LoadList, sizeof (LOAD_LIST));

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX *)mbox;

        emlxs_format_dump(hba, mbox, DMP_MEM_REG, 0, 2, FLASH_LOAD_LIST_ADR);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "Unable to get load list: Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                goto done;
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                EMLXS_MPDATA_SYNC(hba->sli.sli4.dump_region.dma_handle, 0,
                    hba->sli.sli4.dump_region.size, DDI_DMA_SYNC_FORKERNEL);
                Uptr = (uint32_t *)hba->sli.sli4.dump_region.virt;
        } else {
                Uptr = (uint32_t *)&mb->un.varDmp.resp_offset;
        }

        LoadList->head = Uptr[0];
        LoadList->tail = Uptr[1];

        CurEntryAddr = LoadList->head;

        while ((CurEntryAddr != FLASH_LOAD_LIST_ADR) &&
            (LoadList->entry_cnt < MAX_LOAD_ENTRY)) {
                LoadEntry = &LoadList->load_entry[LoadList->entry_cnt];
                LoadList->entry_cnt++;

                emlxs_format_dump(hba, mbox,
                    DMP_MEM_REG, 0, FLASH_LOAD_ENTRY_SIZE, CurEntryAddr);

                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                            "Unable to get load list (%d): Mailbox cmd=%x "
                            "status=%x", LoadList->entry_cnt, mb->mbxCommand,
                            mb->mbxStatus);

                        goto done;
                }

                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        EMLXS_MPDATA_SYNC(hba->sli.sli4.dump_region.dma_handle,
                            0, hba->sli.sli4.dump_region.size,
                            DDI_DMA_SYNC_FORKERNEL);
                        Uptr = (uint32_t *)hba->sli.sli4.dump_region.virt;
                } else {
                        Uptr = (uint32_t *)&mb->un.varDmp.resp_offset;
                }

                LoadEntry->next = Uptr[0];
                LoadEntry->prev = Uptr[1];
                LoadEntry->start_adr = Uptr[2];
                LoadEntry->len = Uptr[3];
                LoadEntry->un.wd[0] = Uptr[4];
                LoadEntry->un.wd[1] = Uptr[5];

                /* update next current load entry address */
                CurEntryAddr = LoadEntry->next;

        }       /* end of while (not end of list) */

done:

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

#ifdef FMA_SUPPORT
        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                if (emlxs_fm_check_dma_handle(hba,
                    hba->sli.sli4.dump_region.dma_handle) != DDI_FM_OK) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_invalid_dma_handle_msg,
                            "read_load_list: hdl=%p",
                            hba->sli.sli4.dump_region.dma_handle);
                        return (1);
                }
        }
#endif  /* FMA_SUPPORT */

        return (0);

} /* emlxs_read_load_list() */


extern uint32_t
emlxs_get_boot_config(emlxs_hba_t *hba, uint8_t *boot_state)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbq;
        MAILBOX4 *mb;
        mbox_req_hdr_t  *hdr_req;
        IOCTL_COMMON_BOOT_CFG *boot_cfg;

        if (hba->sli_mode != EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                    "Invalid sli_mode. mode=%d", hba->sli_mode);

                return (1);
        }

        if ((mbq = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX4 *)mbq;
        bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);

        mb->un.varSLIConfig.be.embedded = 1;
        mbq->nonembed = NULL;
        mbq->mbox_cmpl = NULL;

        mb->mbxCommand = MBX_SLI_CONFIG;
        mb->mbxOwner = OWN_HOST;

        hdr_req = (mbox_req_hdr_t *)
            &mb->un.varSLIConfig.be.un_hdr.hdr_req;
        hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
        hdr_req->opcode = COMMON_OPCODE_GET_BOOT_CFG;

        boot_cfg = (IOCTL_COMMON_BOOT_CFG *)(hdr_req + 1);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                    "Unable to read boot config: Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                kmem_free(mbq, sizeof (MAILBOXQ));
                return (1);
        }

        *boot_state = boot_cfg->params.response.boot_status;

        kmem_free(mbq, sizeof (MAILBOXQ));
        return (0);
}


extern uint32_t
emlxs_set_boot_config(emlxs_hba_t *hba, uint8_t boot_state)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbq;
        MAILBOX4 *mb;
        mbox_req_hdr_t  *hdr_req;
        IOCTL_COMMON_BOOT_CFG *boot_cfg;

        if (hba->sli_mode != EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                    "Invalid sli_mode. mode=%d", hba->sli_mode);

                return (1);
        }

        if ((mbq = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX4 *)mbq;
        bzero((void *) mb, MAILBOX_CMD_SLI4_BSIZE);

        mb->un.varSLIConfig.be.embedded = 1;
        mbq->nonembed = NULL;
        mbq->mbox_cmpl = NULL;

        mb->mbxCommand = MBX_SLI_CONFIG;
        mb->mbxOwner = OWN_HOST;

        hdr_req = (mbox_req_hdr_t *)
            &mb->un.varSLIConfig.be.un_hdr.hdr_req;
        hdr_req->subsystem = IOCTL_SUBSYSTEM_COMMON;
        hdr_req->opcode = COMMON_OPCODE_SET_BOOT_CFG;

        boot_cfg = (IOCTL_COMMON_BOOT_CFG *)(hdr_req + 1);
        boot_cfg->params.request.boot_status = boot_state;

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                    "Unable to read boot config: Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                kmem_free(mbq, sizeof (MAILBOXQ));
                return (1);
        }

        kmem_free(mbq, sizeof (MAILBOXQ));
        return (0);
}




static int
emlxs_build_parms(caddr_t Buffer,
    PWAKE_UP_PARMS AbsWakeUpParms,
    uint32_t BufferSize, PAIF_HDR AifHeader)
{
        IMAGE_HDR ImageHdr;
        uint32_t NextImage;
        uint32_t i;
        int32_t ChangeParams = FALSE;
        caddr_t Sptr;
        caddr_t Dptr;

        bzero((caddr_t)AbsWakeUpParms, sizeof (WAKE_UP_PARMS));

        if ((AifHeader->ImageBase != 0x20000) &&
            ((AifHeader->RoSize + AifHeader->RwSize) <= 0x20000)) {
                return (FALSE);
        }

        NextImage = SLI_IMAGE_START - AifHeader->ImageBase;

        while (BufferSize > NextImage) {
                Sptr = &Buffer[NextImage];
                Dptr = (caddr_t)&ImageHdr;
                for (i = 0; i < sizeof (IMAGE_HDR); i++) {
                        Dptr[i] = Sptr[i];
                }

                if (ImageHdr.BlockSize == 0xffffffff)
                        break;

                switch (ImageHdr.Id.Type) {
                case TEST_PROGRAM:
                        break;
                case FUNC_FIRMWARE:
                        AbsWakeUpParms->prog_id = ImageHdr.Id;
                        ChangeParams = TRUE;
                        break;
                case BOOT_BIOS:
                        AbsWakeUpParms->u0.boot_bios_id = ImageHdr.Id;
                        ChangeParams = TRUE;
                        break;
                case SLI1_OVERLAY:
                        AbsWakeUpParms->sli1_prog_id = ImageHdr.Id;
                        ChangeParams = TRUE;
                        break;
                case SLI2_OVERLAY:
                        AbsWakeUpParms->sli2_prog_id = ImageHdr.Id;
                        ChangeParams = TRUE;
                        break;
                case SLI3_OVERLAY:
                        AbsWakeUpParms->sli3_prog_id = ImageHdr.Id;
                        ChangeParams = TRUE;
                        break;
                case SLI4_OVERLAY:
                        AbsWakeUpParms->sli4_prog_id = ImageHdr.Id;
                        ChangeParams = TRUE;
                        break;
                default:
                        break;
                }

                NextImage += ImageHdr.BlockSize;
        }

        return (ChangeParams);

} /* emlxs_build_parms() */


static uint32_t
emlxs_update_wakeup_parms(emlxs_hba_t *hba,
    PWAKE_UP_PARMS AbsWakeUpParms, PWAKE_UP_PARMS WakeUpParms)
{
        emlxs_port_t *port = &PPORT;
        MAILBOX *mb;
        MAILBOXQ *mbox;
        uint32_t rval = 0;

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX *)mbox;

        WakeUpParms->prog_id = AbsWakeUpParms->prog_id;
        WakeUpParms->u0.boot_bios_id = AbsWakeUpParms->u0.boot_bios_id;
        WakeUpParms->sli1_prog_id = AbsWakeUpParms->sli1_prog_id;
        WakeUpParms->sli2_prog_id = AbsWakeUpParms->sli2_prog_id;
        WakeUpParms->sli3_prog_id = AbsWakeUpParms->sli3_prog_id;
        WakeUpParms->sli4_prog_id = AbsWakeUpParms->sli4_prog_id;

        emlxs_format_update_parms(mbox, WakeUpParms);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to update wakeup parameters: Mailbox cmd=%x "
                    "status=%x", mb->mbxCommand, mb->mbxStatus);

                rval = 1;
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_wakeup_parms() */


static uint32_t
emlxs_validate_version(emlxs_hba_t *hba, emlxs_fw_file_t *file, uint32_t id,
    uint32_t type, char *file_type)
{
        emlxs_port_t *port = &PPORT;

        /* Create the version label */
        emlxs_decode_version(file->version, file->label, sizeof (file->label));

        /* Process the DWC type */
        switch (type) {
        case TEST_PROGRAM:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: TEST: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                break;

        case BOOT_BIOS:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: BOOT: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_bios_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "BOOT Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case FUNC_FIRMWARE:     /* Stub */

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: STUB: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_stub_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "STUB Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case SLI1_OVERLAY:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: SLI1: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_sli1_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "SLI1 Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case SLI2_OVERLAY:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: SLI2: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_sli2_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "SLI2 Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case SLI3_OVERLAY:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: SLI3: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_sli3_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "SLI3 Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case SLI4_OVERLAY:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: SLI4: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_sli4_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "SLI4 Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case SBUS_FCODE:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: SBUS FCODE: offset=%08x  version=%08x, %s",
                    file_type, file->offset, file->version, file->label);

                if (!emlxs_sbus_fcode_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "SBUS FCODE Check: Image not compatible with %s. "
                            "id=%02x", hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        case KERNEL_CODE:

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_msg,
                    "%s: KERN: offset=%08x  version=%08x, %s", file_type,
                    file->offset, file->version, file->label);

                if (!emlxs_kern_check(hba, id)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_incompat_msg,
                            "KERN Check: Image not compatible with %s. id=%02x",
                            hba->model_info.model, id);

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }

                break;

        default:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "%s: Image type not supported. type=%x", file_type, type);

                return (EMLXS_IMAGE_BAD);
        }

        return (0);

} /* emlxs_validate_version() */


static void
emlxs_verify_image(emlxs_hba_t *hba, emlxs_fw_image_t *fw_image)
{
        emlxs_port_t *port = &PPORT;
        emlxs_vpd_t *vpd = &VPD;
        uint32_t i;
        uint32_t count;

        /* Check for AWC file */
        if (fw_image->awc.version) {
                if (fw_image->awc.version == vpd->postKernRev) {
                        fw_image->awc.version = 0;
                }

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                    "AWC file: KERN: old=%s  new=%s  %s.",
                    vpd->postKernName,
                    fw_image->awc.label,
                    (fw_image->awc.version)? "Update":"Skip");
        }

        /* Check for BWC file */
        if (fw_image->bwc.version) {
                if (strcmp(vpd->fcode_version, fw_image->bwc.label) == 0) {
                        fw_image->bwc.version = 0;
                }

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                    "BWC file: BOOT: old=%s  new=%s  %s.",
                    vpd->fcode_version,
                    fw_image->bwc.label,
                    (fw_image->bwc.version)? "Update":"Skip");
        }

        /* Check for DWC file */
        if (fw_image->dwc.version) {
                /* Check for program files */
                count = 0;
                for (i = 0; i < MAX_PROG_TYPES; i++) {
                        if (!fw_image->prog[i].version) {
                                continue;
                        }

                        /* Skip components that don't need updating */
                        switch (i) {
                        case TEST_PROGRAM:
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: TEST:             new=%s  "
                                    "Update.",
                                    fw_image->prog[i].label);
                                break;

                        case BOOT_BIOS:
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: BOOT:             new=%s  "
                                    "Update.",
                                    fw_image->prog[i].label);
                                break;

                        case FUNC_FIRMWARE:
                                if (vpd->opFwRev &&
                                    (fw_image->prog[i].version ==
                                    vpd->opFwRev)) {
                                        fw_image->prog[i].version = 0;
                                }

                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: STUB: old=%s  new=%s  %s.",
                                    vpd->opFwName,
                                    fw_image->prog[i].label,
                                    (fw_image->prog[i].version)?
                                    "Update":"Skip");
                                break;

                        case SLI1_OVERLAY:
                                if (vpd->sli1FwRev &&
                                    (fw_image->prog[i].version ==
                                    vpd->sli1FwRev)) {
                                        fw_image->prog[i].version = 0;
                                }

                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: SLI1: old=%s  new=%s  %s.",
                                    vpd->sli1FwName,
                                    fw_image->prog[i].label,
                                    (fw_image->prog[i].version)?
                                    "Update":"Skip");
                                break;

                        case SLI2_OVERLAY:
                                if (vpd->sli2FwRev &&
                                    (fw_image->prog[i].version ==
                                    vpd->sli2FwRev)) {
                                        fw_image->prog[i].version = 0;
                                }

                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: SLI2: old=%s  new=%s  %s.",
                                    vpd->sli2FwName,
                                    fw_image->prog[i].label,
                                    (fw_image->prog[i].version)?
                                    "Update":"Skip");
                                break;

                        case SLI3_OVERLAY:
                                if (vpd->sli3FwRev &&
                                    (fw_image->prog[i].version ==
                                    vpd->sli3FwRev)) {
                                        fw_image->prog[i].version = 0;
                                }

                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: SLI3: old=%s  new=%s  %s.",
                                    vpd->sli3FwName,
                                    fw_image->prog[i].label,
                                    (fw_image->prog[i].version)?
                                    "Update":"Skip");
                                break;

                        case SLI4_OVERLAY:
                                if (vpd->sli4FwRev &&
                                    (fw_image->prog[i].version ==
                                    vpd->sli4FwRev)) {
                                        fw_image->prog[i].version = 0;
                                }

                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: SLI4: old=%s  new=%s  %s.",
                                    vpd->sli4FwRev,
                                    fw_image->prog[i].label,
                                    (fw_image->prog[i].version)?
                                    "Update":"Skip");
                                break;

                        default:
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                                    "DWC file: type=%x version=%x label=%s  "
                                    "Update.",
                                    i,
                                    fw_image->prog[i].version,
                                    fw_image->prog[i].label);
                        }

                        if (fw_image->prog[i].version) {
                                count++;
                        }
                }

                if (!count) {
                        fw_image->dwc.version = 0;
                }
        }

        return;

} /* emlxs_verify_image() */


static uint32_t
emlxs_validate_image(emlxs_hba_t *hba, caddr_t Buffer, uint32_t Size,
    emlxs_fw_image_t *image)
{
        emlxs_port_t *port = &PPORT;
        uint32_t ImageType;
        AIF_HDR AifHdr;
        IMAGE_HDR ImageHdr;
        uint32_t NextImage;
        uint32_t count;
        uint32_t FileType;
        uint32_t FileLen = 0;
        uint32_t TotalLen = 0;
        uint32_t *CkSumEnd;
        uint32_t id;
        uint32_t type;
        uint32_t ver;
        uint32_t ImageLength;
        uint32_t BufferSize;
        uint32_t rval = 0;
        caddr_t bptr;
        emlxs_vpd_t *vpd;

        vpd = &VPD;

        /* Get image type */
        ImageType = *((uint32_t *)Buffer);

        /* Pegasus and beyond adapters */
        if ((ImageType == NOP_IMAGE_TYPE) &&
            !(hba->model_info.chip &
            (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP))) {
                bptr = Buffer;
                TotalLen = sizeof (uint32_t);

                while (TotalLen < Size) {
                        if (Size < sizeof (AIF_HDR)) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_image_bad_msg,
                                    "Invalid image header length: 0x%x < 0x%x",
                                    Size, sizeof (AIF_HDR));

                                return (EMLXS_IMAGE_BAD);
                        }

                        bcopy(bptr, &AifHdr, sizeof (AIF_HDR));
                        emlxs_disp_aif_header(hba, &AifHdr);

                        ImageLength = AifHdr.RoSize;

                        /* Validate checksum */
                        CkSumEnd =
                            (uint32_t *)(bptr + ImageLength +
                            sizeof (AIF_HDR));
                        if (emlxs_valid_cksum((uint32_t *)bptr, CkSumEnd)) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_image_bad_msg,
                                    "Invalid checksum found.");

                                return (EMLXS_IMAGE_BAD);
                        }

                        FileType = AifHdr.ZinitBr;
                        switch (FileType) {
                        case FILE_TYPE_AWC:
                                image->awc.offset =
                                    (uint32_t)((uintptr_t)bptr -
                                    (uintptr_t)Buffer);
                                image->awc.version = AifHdr.AVersion;
                                image->awc.revcomp = 0;

                                id = (AifHdr.AVersion & 0x00ff0000) >> 16;
                                type = emlxs_type_check(
                                    (AifHdr.AVersion & 0xff000000) >> 24);

                                /* Validate the file version */
                                if ((rval = emlxs_validate_version(hba,
                                    &image->awc, id, type, "AWC file"))) {
                                        return (rval);
                                }

                                break;

                        case FILE_TYPE_BWC:
                                image->bwc.offset =
                                    (uint32_t)((uintptr_t)bptr -
                                    (uintptr_t)Buffer);
                                image->bwc.version = AifHdr.AVersion;
                                image->bwc.revcomp = 0;

                                id = (AifHdr.AVersion & 0x00ff0000) >> 16;
                                type = emlxs_type_check(
                                    (AifHdr.AVersion & 0xff000000) >> 24);

                                /* Validate the file version */
                                if ((rval = emlxs_validate_version(hba,
                                    &image->bwc, id, type, "BWC file"))) {
                                        return (rval);
                                }

                                break;

                        case FILE_TYPE_DWC:
                                image->dwc.offset =
                                    (uint32_t)((uintptr_t)bptr -
                                    (uintptr_t)Buffer);
                                image->dwc.version = AifHdr.AVersion;
                                image->dwc.revcomp = 0;

                                id = (AifHdr.AVersion & 0x00ff0000) >> 16;
                                type = emlxs_type_check(
                                    (AifHdr.AVersion & 0xff000000) >> 24);

                                /* Validate the file version */
                                if ((rval = emlxs_validate_version(hba,
                                    &image->dwc, id, type, "DWC file"))) {
                                        return (rval);
                                }

                                /* Scan for program types */
                                NextImage = sizeof (AIF_HDR) + 4;
                                BufferSize = AifHdr.RoSize + AifHdr.RwSize;

                                count = 0;
                                while (BufferSize > NextImage) {
                                        bcopy(&bptr[NextImage], &ImageHdr,
                                            sizeof (IMAGE_HDR));
                                        emlxs_dump_image_header(hba,
                                            &ImageHdr);

                                        /* Validate block size */
                                        if (ImageHdr.BlockSize == 0xffffffff) {
                                                break;
                                        }

                                        type = emlxs_type_check(
                                            ImageHdr.Id.Type);

                                        /* Calculate the program offset */
                                        image->prog[type].offset =
                                            (uint32_t)((uintptr_t)
                                            &bptr[NextImage] -
                                            (uintptr_t)Buffer);

                                        /* Acquire the versions */
                                        image->prog[type].version =
                                            (ImageHdr.Id.Type << 24) |
                                            (ImageHdr.Id.Id << 16) |
                                            (ImageHdr.Id.Ver << 8) |
                                            ImageHdr.Id.Rev;

                                        image->prog[type].revcomp =
                                            ImageHdr.Id.un.revcomp;

                                        /* Validate the file version */
                                        if ((rval = emlxs_validate_version(hba,
                                            &image->prog[type], ImageHdr.Id.Id,
                                            type, "DWC prog"))) {
                                                return (rval);
                                        }

                                        count++;
                                        NextImage += ImageHdr.BlockSize;

                                }       /* while () */

                                if (count == 0) {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_image_bad_msg,
                                            "DWC file has no PRG images.");

                                        return (EMLXS_IMAGE_BAD);
                                }

                                break;
                        }

                        FileLen =
                            sizeof (AIF_HDR) + ImageLength +
                            sizeof (uint32_t);
                        TotalLen += FileLen;
                        bptr += FileLen;
                }
        }

        /* Pre-pegasus adapters */

        else if (ImageType == NOP_IMAGE_TYPE) {
                if (Size < sizeof (AIF_HDR)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid image header length: 0x%x < 0x%x", Size,
                            sizeof (AIF_HDR));

                        return (EMLXS_IMAGE_BAD);
                }

                bcopy(Buffer, &AifHdr, sizeof (AIF_HDR));
                emlxs_disp_aif_header(hba, &AifHdr);

                ImageLength = AifHdr.RoSize + AifHdr.RwSize;

                if (Size != (sizeof (AIF_HDR) + ImageLength + sizeof (int))) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Image length incorrect: 0x%x != 0x%x", Size,
                            sizeof (AIF_HDR) + ImageLength +
                            sizeof (uint32_t));

                        return (EMLXS_IMAGE_BAD);
                }

                if (AifHdr.ImageBase && AifHdr.ImageBase != 0x20000) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid imageBase value %x != 0x20000",
                            AifHdr.ImageBase);

                        return (EMLXS_IMAGE_BAD);
                }

                CkSumEnd =
                    (uint32_t *)(Buffer + ImageLength + sizeof (AIF_HDR));
                if (emlxs_valid_cksum((uint32_t *)Buffer, CkSumEnd)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid checksum found.");

                        return (EMLXS_IMAGE_BAD);
                }

                image->dwc.offset = 0;
                image->dwc.version = AifHdr.AVersion;
                image->dwc.revcomp = 0;

                id = (AifHdr.AVersion & 0x00ff0000) >> 16;
                type = emlxs_type_check((AifHdr.AVersion & 0xff000000) >> 24);

                /* Validate the file version */
                if ((rval = emlxs_validate_version(hba, &image->dwc, id, type,
                    "DWC file"))) {
                        return (rval);
                }

                NextImage = SLI_IMAGE_START - AifHdr.ImageBase;
                while (Size > NextImage) {
                        bcopy(&Buffer[NextImage], &ImageHdr,
                            sizeof (IMAGE_HDR));
                        emlxs_dump_image_header(hba, &ImageHdr);

                        /* Validate block size */
                        if (ImageHdr.BlockSize == 0xffffffff) {
                                break;
                        }

                        type = emlxs_type_check(ImageHdr.Id.Type);

                        /* Calculate the program offset */
                        image->prog[type].offset = NextImage;

                        /* Acquire the versions */
                        image->prog[type].version =
                            (ImageHdr.Id.Type << 24) |
                            (ImageHdr.Id.Id << 16) |
                            (ImageHdr.Id.Ver << 8) |
                            ImageHdr.Id.Rev;

                        image->prog[type].revcomp = ImageHdr.Id.un.revcomp;

                        /* Validate the file version */
                        if ((rval = emlxs_validate_version(hba,
                            &image->prog[type], ImageHdr.Id.Id, type,
                            "DWC prog"))) {
                                return (rval);
                        }

                        NextImage += ImageHdr.BlockSize;
                }

        } else { /* PRG File */

                /* Precheck image size */
                if (Size < sizeof (IMAGE_HDR)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid image header length: 0x%x < 0x%x", Size,
                            sizeof (IMAGE_HDR));

                        return (EMLXS_IMAGE_BAD);
                }

                bcopy(Buffer, &ImageHdr, sizeof (IMAGE_HDR));
                emlxs_dump_image_header(hba, &ImageHdr);

                /* Validate block size */
                if (ImageHdr.BlockSize == 0xffffffff) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid block size.");

                        return (EMLXS_IMAGE_BAD);
                }

                ImageLength = ImageHdr.BlockSize;

                /* Validate image length */
                if (Size != ImageLength) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid image length: 0x%x != 0x%x", Size,
                            ImageLength);

                        return (EMLXS_IMAGE_BAD);
                }

                /* Validate Checksum */
                CkSumEnd =
                    (uint32_t *)Buffer + (ImageLength / sizeof (uint32_t)) -
                    1;
                if (emlxs_valid_cksum((uint32_t *)Buffer, CkSumEnd)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                            "Invalid checksum found.");

                        return (EMLXS_IMAGE_BAD);
                }

                type = emlxs_type_check(ImageHdr.Id.Type);

                /* Calculate the program offset */
                image->prog[type].offset = 0;

                /* Acquire the versions */
                image->prog[type].version =
                    (ImageHdr.Id.Type << 24) |
                    (ImageHdr.Id.Id << 16) |
                    (ImageHdr.Id.Ver << 8) |
                    ImageHdr.Id.Rev;

                image->prog[type].revcomp = ImageHdr.Id.un.revcomp;

                /* Validate the file version */
                if ((rval = emlxs_validate_version(hba, &image->prog[type],
                    ImageHdr.Id.Id, type, "DWC file"))) {
                        return (rval);
                }
        }

        /*
         * This checks if a DragonFly (pre-V2 ASIC) SLI2
         * image file is < version 3.8
         */
        if (FC_JEDEC_ID(vpd->biuRev) == DRAGONFLY_JEDEC_ID) {
                ver = (image->prog[SLI2_OVERLAY].version &
                    0x0000ff00) >> 8;

                if (ver >= 0x38) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_image_incompat_msg,
                            "ASIC Check: Image requires DragonFly "
                            "V2 ASIC");

                        return (EMLXS_IMAGE_INCOMPATIBLE);
                }
        }

        return (0);

} /* emlxs_validate_image() */


static uint32_t
emlxs_update_exp_rom(emlxs_hba_t *hba, PWAKE_UP_PARMS WakeUpParms)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        uint32_t next_address;
        uint32_t rval = 0;

        if (WakeUpParms->u1.EROM_prog_wd[0] == 0) {
                return (1);
        }

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        bzero(mbox, sizeof (MAILBOXQ));

        mb = (MAILBOX *)mbox;
        mb->mbxCommand = MBX_LOAD_EXP_ROM;
        mb->un.varLdExpRom.step = EROM_CMD_FIND_IMAGE;
        mb->un.varLdExpRom.progress = 0;
        mb->un.varLdExpRom.un.prog_id = WakeUpParms->u1.EROM_prog_id;
        mbox->mbox_cmpl = NULL;

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to load exp ROM. Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                rval = 1;

                goto SLI_DOWNLOAD_EXIT;
        }

        if (mb->un.varLdExpRom.progress == EROM_RSP_COPY_DONE) {
                (void) emlxs_update_wakeup_parms(hba, WakeUpParms, WakeUpParms);

                rval = 1;
                goto SLI_DOWNLOAD_EXIT;
        }

        if (mb->un.varLdExpRom.progress != EROM_RSP_ERASE_STARTED) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Invalid exp ROM progress. progress=%x",
                    mb->un.varLdExpRom.progress);

                rval = 1;

                goto SLI_DOWNLOAD_EXIT;
        }

        /*
         * continue Erase
         */
        while (mb->un.varLdExpRom.progress != EROM_RSP_ERASE_COMPLETE) {

                next_address = mb->un.varLdExpRom.dl_to_adr;

                bzero((void *)mb, MAILBOX_CMD_BSIZE);

                mb->mbxCommand = MBX_LOAD_EXP_ROM;
                mb->un.varLdExpRom.step = EROM_CMD_CONTINUE_ERASE;
                mb->un.varLdExpRom.dl_to_adr = next_address;
                mb->un.varLdExpRom.progress = 0;
                mb->un.varLdExpRom.un.prog_id = WakeUpParms->u1.EROM_prog_id;
                mbox->mbox_cmpl = NULL;

                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to load exp ROM. Mailbox cmd=%x status=%x",
                            mb->mbxCommand, mb->mbxStatus);

                        rval = 1;
                        goto SLI_DOWNLOAD_EXIT;
                }

        }

        while (mb->un.varLdExpRom.progress != EROM_RSP_COPY_DONE) {
                next_address = mb->un.varLdExpRom.dl_to_adr;

                bzero((void *)mb, MAILBOX_CMD_BSIZE);

                mb->mbxCommand = MBX_LOAD_EXP_ROM;
                mb->un.varLdExpRom.step = EROM_CMD_COPY;
                mb->un.varLdExpRom.dl_to_adr = next_address;
                mb->un.varLdExpRom.progress = 0;
                mb->un.varLdExpRom.un.prog_id = WakeUpParms->u1.EROM_prog_id;
                mbox->mbox_cmpl = NULL;

                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "Unable to load exp ROM. Mailbox cmd=%x status=%x",
                            mb->mbxCommand, mb->mbxStatus);

                        rval = 1;

                        goto SLI_DOWNLOAD_EXIT;
                }
        }

        rval = emlxs_update_wakeup_parms(hba, WakeUpParms, WakeUpParms);

SLI_DOWNLOAD_EXIT:

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_update_exp_rom() */


/*
 *
 * FUNCTION NAME: emlxs_start_abs_download_2mb
 *
 * DESCRIPTION: Perform absolute download for 2 MB flash.  A incoming
 *              buffer may consist of more than 1 file.  This function
 *              will parse the buffer to find all the files.
 *
 *
 * PARAMETERS:
 *
 *
 * RETURNS:
 *
 */
/* ARGSUSED */
static uint32_t
emlxs_start_abs_download_2mb(emlxs_hba_t *hba, caddr_t buffer, uint32_t len,
    uint32_t offline, emlxs_fw_image_t *fw_image)
{
        emlxs_port_t *port = &PPORT;
        uint32_t rval = 0;

        /* If nothing to download then quit now */
        if (!fw_image->awc.version &&
            !fw_image->dwc.version &&
            !fw_image->bwc.version) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_msg,
                    "Nothing new to update.  Exiting.");
                return (0);
        }

        /*
         * Everything checks out, now to just do it
         */
        if (offline) {
                if (emlxs_offline(hba, 0) != FC_SUCCESS) {
                        return (EMLXS_OFFLINE_FAILED);
                }

                if (EMLXS_SLI_HBA_RESET(hba, 1, 1, 0) != FC_SUCCESS) {
                        return (EMLXS_OFFLINE_FAILED);
                }
        }

        if (fw_image->awc.version) {
                rval = emlxs_proc_abs_2mb(hba,
                    (buffer + fw_image->awc.offset),
                    FILE_TYPE_AWC, 0);

                if (rval) {
                        goto SLI_DOWNLOAD_2MB_EXIT;
                }
        }

        if (fw_image->bwc.version) {
                rval = emlxs_proc_abs_2mb(hba,
                    (buffer + fw_image->bwc.offset),
                    FILE_TYPE_BWC,
                    (fw_image->dwc.version)? ALLext:BWCext);

                if (rval) {
                        goto SLI_DOWNLOAD_2MB_EXIT;
                }
        }

        if (fw_image->dwc.version) {
                rval = emlxs_proc_rel_2mb(hba, buffer, fw_image);

                if (rval) {
                        goto SLI_DOWNLOAD_2MB_EXIT;
                }
        }

SLI_DOWNLOAD_2MB_EXIT:

        if (offline) {
                (void) emlxs_online(hba);
        }

        return (rval);

} /* emlxs_start_abs_download_2mb() */


/*
 *
 * FUNCTION NAME: emlxs_proc_abs_2mb
 *
 * DESCRIPTION: Given one of the 3 file types(awc/bwc/dwc), it will reset
 *              the port and download the file with sliIssueMbCommand()
 *
 *
 * PARAMETERS:
 *
 *
 * RETURNS:
 *
 */
static uint32_t
emlxs_proc_abs_2mb(emlxs_hba_t *hba, caddr_t EntireBuffer,
    uint32_t FileType, uint32_t extType)
{
        emlxs_port_t *port = &PPORT;
        PAIF_HDR AifHdr;
        caddr_t Buffer = NULL;
        caddr_t DataBuffer = NULL;
        uint32_t *Src;
        uint32_t *Dst;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        uint32_t DlByteCount;
        uint32_t rval = 0;
        uint32_t SegSize = DL_SLIM_SEG_BYTE_COUNT;
        uint32_t DlToAddr;
        uint32_t DlCount;
        WAKE_UP_PARMS AbsWakeUpParms;
        uint32_t i;
        uint32_t NextAddr;
        uint32_t EraseByteCount;
        uint32_t AreaId;
        uint32_t RspProgress = 0;
        uint32_t ParamsChg;

        AifHdr = (PAIF_HDR)EntireBuffer;
        DlByteCount = AifHdr->RoSize + AifHdr->RwSize;
        DlToAddr = AifHdr->ImageBase;

        if ((DataBuffer = (caddr_t)kmem_zalloc(DL_SLIM_SEG_BYTE_COUNT,
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "%x: Unable to allocate data buffer.", FileType);

                return (EMLXS_IMAGE_FAILED);
        }

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "%x: Unable to allocate mailbox buffer.", FileType);

                kmem_free(DataBuffer, DL_SLIM_SEG_BYTE_COUNT);

                return (EMLXS_IMAGE_FAILED);
        }

        mb = (MAILBOX *)mbox;

        Buffer = EntireBuffer + sizeof (AIF_HDR);

        switch (FileType) {
        case FILE_TYPE_AWC:
                ParamsChg = 0;
                break;

        case FILE_TYPE_BWC:
                rval = emlxs_build_parms_2mb_bwc(hba,
                    AifHdr, extType, &AbsWakeUpParms);

                if (rval == FALSE) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "BWC build parms failed.");

                        rval = EMLXS_IMAGE_FAILED;

                        goto EXIT_ABS_DOWNLOAD;
                }
                ParamsChg = 1;
                break;

        default:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_image_bad_msg,
                    "Invalid file type: %x", FileType);

                rval = EMLXS_IMAGE_BAD;

                goto EXIT_ABS_DOWNLOAD;
        }

        EraseByteCount = AifHdr->Area_Size;
        AreaId = AifHdr->Area_ID;

        emlxs_format_load_area_cmd(mbox,
            DlToAddr,
            EraseByteCount,
            ERASE_FLASH,
            0, DL_FROM_SLIM_OFFSET, AreaId, MBX_LOAD_AREA, CMD_START_ERASE);

        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) != MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "%x: Could not erase 2MB Flash: Mailbox cmd=%x status=%x",
                    FileType, mb->mbxCommand, mb->mbxStatus);

                rval = EMLXS_IMAGE_FAILED;

                goto EXIT_ABS_DOWNLOAD;
        }

        while (mb->un.varLdArea.progress != RSP_ERASE_COMPLETE) {
                NextAddr = mb->un.varLdArea.dl_to_adr;

                emlxs_format_load_area_cmd(mbox,
                    NextAddr,
                    EraseByteCount,
                    ERASE_FLASH,
                    0,
                    DL_FROM_SLIM_OFFSET,
                    AreaId, MBX_LOAD_AREA, CMD_CONTINUE_ERASE);

                if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) !=
                    MBX_SUCCESS) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "%x: Could not erase 2MB Flash2: Mailbox cmd=%x "
                            "status=%x", FileType, mb->mbxCommand,
                            mb->mbxStatus);

                        rval = EMLXS_IMAGE_FAILED;

                        goto EXIT_ABS_DOWNLOAD;
                }
        }

        while (DlByteCount) {
                if (DlByteCount >= SegSize)
                        DlCount = SegSize;
                else
                        DlCount = DlByteCount;

                DlByteCount -= DlCount;

                Dst = (uint32_t *)DataBuffer;
                Src = (uint32_t *)Buffer;

                for (i = 0; i < (DlCount / 4); i++) {
                        *Dst = *Src;
                        Dst++;
                        Src++;
                }

                WRITE_SLIM_COPY(hba, (uint32_t *)DataBuffer,
                    (volatile uint32_t *)((volatile char *)
                    hba->sli.sli3.slim_addr + sizeof (MAILBOX)),
                    (DlCount / sizeof (uint32_t)));

                if ((RspProgress == RSP_DOWNLOAD_MORE) || (RspProgress == 0)) {
                        emlxs_format_load_area_cmd(mbox,
                            DlToAddr,
                            DlCount,
                            PROGRAM_FLASH,
                            (DlByteCount) ? 0 : 1,
                            DL_FROM_SLIM_OFFSET,
                            AreaId,
                            MBX_LOAD_AREA,
                            (DlByteCount) ? CMD_DOWNLOAD : CMD_END_DOWNLOAD);

                        if (EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0) !=
                            MBX_SUCCESS) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_download_failed_msg,
                                    "%x: Could not program 2MB Flash: Mailbox "
                                    "cmd=%x status=%x", FileType,
                                    mb->mbxCommand, mb->mbxStatus);

                                rval = EMLXS_IMAGE_FAILED;

                                goto EXIT_ABS_DOWNLOAD;
                        }
                }

                RspProgress = mb->un.varLdArea.progress;

                Buffer += DlCount;
                DlToAddr += DlCount;
        }

#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->sli.sli3.slim_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);

                rval = EMLXS_IMAGE_FAILED;

                goto EXIT_ABS_DOWNLOAD;
        }
#endif  /* FMA_SUPPORT */

        if (RspProgress != RSP_DOWNLOAD_DONE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "%x: Failed download response received. %x", FileType,
                    RspProgress);

                rval = EMLXS_IMAGE_FAILED;

                goto EXIT_ABS_DOWNLOAD;
        }

        if (ParamsChg) {
                if (emlxs_update_wakeup_parms(hba, &AbsWakeUpParms,
                    &AbsWakeUpParms)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                            "%x: Unable to update parms.", FileType);

                        rval = EMLXS_IMAGE_FAILED;
                }
        }

EXIT_ABS_DOWNLOAD:

        if (DataBuffer) {
                kmem_free(DataBuffer, DL_SLIM_SEG_BYTE_COUNT);
        }

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

        return (rval);

} /* emlxs_proc_abs_2mb() */


static void
emlxs_format_load_area_cmd(MAILBOXQ * mbq,
    uint32_t Base,
    uint32_t DlByteCount,
    uint32_t Function,
    uint32_t Complete,
    uint32_t DataOffset, uint32_t AreaId, uint8_t MbxCmd, uint32_t StepCmd)
{
        MAILBOX *mb = (MAILBOX *)mbq;

        bzero((void *)mb, MAILBOX_CMD_BSIZE);

        mb->mbxCommand = MbxCmd;
        mb->mbxOwner = OWN_HOST;
        mb->un.varLdArea.update_flash = 1;
        mb->un.varLdArea.erase_or_prog = Function;
        mb->un.varLdArea.dl_to_adr = Base;
        mb->un.varLdArea.dl_len = DlByteCount;
        mb->un.varLdArea.load_cmplt = Complete;
        mb->un.varLdArea.method = DL_FROM_SLIM;
        mb->un.varLdArea.area_id = AreaId;
        mb->un.varLdArea.step = StepCmd;
        mb->un.varLdArea.un.dl_from_slim_offset = DataOffset;
        mbq->mbox_cmpl = NULL;

} /* emlxs_format_load_area_cmd() */


/* ARGSUSED */
static uint32_t
emlxs_build_parms_2mb_bwc(emlxs_hba_t *hba,
    PAIF_HDR AifHdr, uint32_t extType, PWAKE_UP_PARMS AbsWakeUpParms)
{
        emlxs_port_t *port = &PPORT;
        uint32_t pId[2];
        uint32_t returnStat;

        /* Read wakeup paramters */
        if (emlxs_read_wakeup_parms(hba, AbsWakeUpParms, 0) ==
            (uint32_t)CFG_DATA_NO_REGION) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to get BWC parameters.");
                return (FALSE);
        }

        pId[0] = AifHdr->AVersion;
        pId[1] = 0;

        if (extType == BWCext) {
                AbsWakeUpParms->u0.boot_bios_wd[0] = pId[0];
                AbsWakeUpParms->u0.boot_bios_wd[1] = pId[1];
                AbsWakeUpParms->u1.EROM_prog_wd[0] = pId[0];
                AbsWakeUpParms->u1.EROM_prog_wd[1] = pId[1];
        }

        else if (extType == ALLext) {
                if (!AbsWakeUpParms->u0.boot_bios_wd[0]) {
                        /* case of EROM inactive */
                        AbsWakeUpParms->u1.EROM_prog_wd[1] = pId[1];
                        AbsWakeUpParms->u1.EROM_prog_wd[0] = pId[0];
                } else {
                        /* case of EROM active */
                        if (AbsWakeUpParms->u0.boot_bios_wd[0] == pId[0]) {
                                /* same ID */
                                AbsWakeUpParms->u0.boot_bios_wd[0] = pId[0];
                                AbsWakeUpParms->u0.boot_bios_wd[1] = pId[1];
                                AbsWakeUpParms->u1.EROM_prog_wd[0] = pId[0];
                                AbsWakeUpParms->u1.EROM_prog_wd[1] = pId[1];
                        } else {
                                /* different ID */
                                AbsWakeUpParms->u1.EROM_prog_wd[0] = pId[0];
                                AbsWakeUpParms->u1.EROM_prog_wd[1] = pId[1];

                                returnStat =
                                    emlxs_update_exp_rom(hba, AbsWakeUpParms);

                                if (returnStat) {
                                        AbsWakeUpParms->u0.boot_bios_wd[0] =
                                            pId[0];
                                        AbsWakeUpParms->u0.boot_bios_wd[1] =
                                            pId[1];
                                }
                        }
                }
        }

        return (TRUE);

} /* emlxs_build_parms_2mb_bwc() */


extern uint32_t
emlxs_get_max_sram(emlxs_hba_t *hba, uint32_t *MaxRbusSize,
    uint32_t *MaxIbusSize)
{
        emlxs_port_t *port = &PPORT;
        MAILBOXQ *mbox;
        MAILBOX *mb;
        uint32_t *Uptr;
        uint32_t rval = 0;

        if (MaxRbusSize) {
                *MaxRbusSize = 0;
        }

        if (MaxIbusSize) {
                *MaxIbusSize = 0;
        }

        if ((mbox = (MAILBOXQ *)kmem_zalloc(sizeof (MAILBOXQ),
            KM_NOSLEEP)) == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to allocate mailbox buffer.");

                return (1);
        }

        mb = (MAILBOX *)mbox;

        emlxs_format_dump(hba, mbox, DMP_MEM_REG, 0, 2, MAX_RBUS_SRAM_SIZE_ADR);

        if ((rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_WAIT, 0)) !=
            MBX_SUCCESS) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_download_failed_msg,
                    "Unable to get SRAM size: Mailbox cmd=%x status=%x",
                    mb->mbxCommand, mb->mbxStatus);

                rval = 1;

                goto Exit_Function;
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                EMLXS_MPDATA_SYNC(hba->sli.sli4.dump_region.dma_handle, 0,
                    hba->sli.sli4.dump_region.size, DDI_DMA_SYNC_FORKERNEL);
                Uptr = (uint32_t *)hba->sli.sli4.dump_region.virt;
        } else {
                Uptr = (uint32_t *)&mb->un.varDmp.resp_offset;
        }

        if (MaxRbusSize) {
                *MaxRbusSize = Uptr[0];
        }

        if (MaxIbusSize) {
                *MaxIbusSize = Uptr[1];
        }

Exit_Function:

        if (mbox) {
                kmem_free(mbox, sizeof (MAILBOXQ));
        }

#ifdef FMA_SUPPORT
        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                if (emlxs_fm_check_dma_handle(hba,
                    hba->sli.sli4.dump_region.dma_handle) != DDI_FM_OK) {
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_invalid_dma_handle_msg,
                            "get_max_sram: hdl=%p",
                            hba->sli.sli4.dump_region.dma_handle);
                        rval = 1;
                }
        }
#endif  /* FMA_SUPPORT */

        return (rval);

} /* emlxs_get_max_sram() */


static uint32_t
emlxs_kern_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_FF;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_kern_check() */

static uint32_t
emlxs_stub_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_2;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_stub_check() */

static uint32_t
emlxs_bios_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_3;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_bios_check() */

static uint32_t
emlxs_sli1_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_6;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_sli1_check() */

static uint32_t
emlxs_sli2_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_7;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_sli2_check() */

static uint32_t
emlxs_sli3_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_B;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_sli3_check() */


static uint32_t
emlxs_sli4_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_E;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_sli4_check() */


static uint32_t
emlxs_sbus_fcode_check(emlxs_hba_t *hba, uint32_t version)
{
        uint8_t *ptr;
        uint8_t ver;

        ver = version & 0xff;
        ptr = hba->model_info.pt_A;

        while (*ptr) {
                if (*ptr++ == ver) {
                        return (1);
                }
        }

        return (0);

} /* emlxs_sbus_fcode_check() */


static uint32_t
emlxs_type_check(uint32_t type)
{
        if (type == 0xff) {
                return (KERNEL_CODE);
        }

        if (type >= MAX_PROG_TYPES) {
                return (RESERVED_D);
        }

        return (type);

} /* emlxs_type_check() */


extern int32_t
emlxs_boot_code_disable(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        PROG_ID Id;
        emlxs_vpd_t *vpd;
        uint8_t boot_state = 0;

        vpd = &VPD;

        if (hba->model_info.chip & EMLXS_BE_CHIPS) {
                return (EMLXS_OP_NOT_SUP);
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                /* Read Boot Config */
                if (emlxs_get_boot_config(hba, &boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to get boot config.");

                        return (FC_FAILURE);
                }

                /* Check if boot code is already disabled */
                if (! boot_state) {
                        return (FC_SUCCESS);
                }

                /* Disable boot code */
                boot_state = 0;
                if (emlxs_set_boot_config(hba, boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to set boot config.");

                        return (FC_FAILURE);
                }

                /* Now read the boot config again to verify */
                if (emlxs_get_boot_config(hba, &boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to get boot config.");

                        return (FC_FAILURE);
                }

                /* return the result */
                return ((boot_state == 0) ? FC_SUCCESS : FC_FAILURE);
        } else {
                if (emlxs_read_wakeup_parms(hba, &hba->wakeup_parms, 0)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_disable: Unable to read wake up parms.");

                        return (FC_FAILURE);
                }

                /* Check if boot code is already disabled */
                if (hba->wakeup_parms.u0.boot_bios_wd[0] == 0) {
                        return (FC_SUCCESS);
                }

                /* Make sure EROM entry has copy of boot bios entry */
                if (!(hba->model_info.chip &
                    (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP)) &&
                    (hba->wakeup_parms.u0.boot_bios_wd[0] !=
                    hba->wakeup_parms.u1.EROM_prog_wd[0]) &&
                    (hba->wakeup_parms.u0.boot_bios_wd[1] !=
                    hba->wakeup_parms.u1.EROM_prog_wd[1])) {
                        (void) emlxs_update_boot_wakeup_parms(hba,
                            &hba->wakeup_parms,
                            &hba->wakeup_parms.u0.boot_bios_id, 1);
                }

                /* Update the bios id with a zero id */
                /* Don't load the EROM this time */
                bzero(&Id, sizeof (PROG_ID));
                (void) emlxs_update_boot_wakeup_parms(hba,
                    &hba->wakeup_parms, &Id, 0);

                /* Now read the parms again to verify */
                (void) emlxs_read_wakeup_parms(hba, &hba->wakeup_parms, 1);
                emlxs_decode_version(hba->wakeup_parms.u0.boot_bios_wd[0],
                    vpd->boot_version, sizeof (vpd->boot_version));
                /* (void) strcpy(vpd->fcode_version, vpd->boot_version); */

                /* Return the result */
                return ((hba->wakeup_parms.u0.boot_bios_wd[0] == 0) ?
                    FC_SUCCESS : FC_FAILURE);
        }

} /* emlxs_boot_code_disable() */


extern int32_t
emlxs_boot_code_enable(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        emlxs_vpd_t *vpd;
        PROG_ID load_list[MAX_LOAD_ENTRY];
        uint32_t i;
        uint32_t count;
        uint8_t boot_state = 0;

        vpd = &VPD;

        if (hba->model_info.chip & EMLXS_BE_CHIPS) {
                return (FC_SUCCESS);
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                /* Read Boot Config */
                if (emlxs_get_boot_config(hba, &boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to get boot config.");

                        return (FC_FAILURE);
                }

                /* Check if boot code is already enabled */
                if (boot_state) {
                        return (FC_SUCCESS);
                }

                /* Enable boot code */
                boot_state = 1;
                if (emlxs_set_boot_config(hba, boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to set boot config.");

                        return (FC_FAILURE);
                }

                /* Now read the boot config again to verify */
                if (emlxs_get_boot_config(hba, &boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to get boot config.");

                        return (FC_FAILURE);
                }

                /* return the result */
                return ((boot_state != 0) ? FC_SUCCESS : FC_FAILURE);
        } else {
                /* Read the wakeup parms */
                if (emlxs_read_wakeup_parms(hba, &hba->wakeup_parms, 0)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_enable: Unable to read wake up parms.");

                        return (FC_FAILURE);
                }

                /* Check if boot code is already enabled */
                if (hba->wakeup_parms.u0.boot_bios_id.Type == BOOT_BIOS) {
                        return (FC_SUCCESS);
                }

                if (!(hba->model_info.chip &
                    (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP))) {
                        if (hba->wakeup_parms.u1.EROM_prog_id.Type
                            != BOOT_BIOS) {
                                return (EMLXS_NO_BOOT_CODE);
                        }

                        /* Update the parms with the boot image id */
                        /* Don't load the EROM this time */
                        (void) emlxs_update_boot_wakeup_parms(hba,
                            &hba->wakeup_parms,
                            &hba->wakeup_parms.u1.EROM_prog_id, 0);
                } else { /* (EMLXS_DRAGONFLY_CHIP | EMLXS_CENTAUR_CHIP) */

                        count = emlxs_get_load_list(hba, load_list);

                        if (!count) {
                                return (FC_FAILURE);
                        }

                        /* Scan load list for a boot image */
                        for (i = 0; i < count; i++) {
                                if (load_list[i].Type == BOOT_BIOS) {
                                        /*
                                         * Update the parms with boot image id
                                         * Don't load the EROM this time
                                         */
                                        (void) emlxs_update_boot_wakeup_parms(
                                            hba, &hba->wakeup_parms,
                                            &load_list[i], 0);

                                        break;
                                }
                        }

                        if (i == count) {
                                return (EMLXS_NO_BOOT_CODE);
                        }
                }

                /* Now read the parms again to verify */
                (void) emlxs_read_wakeup_parms(hba, &hba->wakeup_parms, 1);
                emlxs_decode_version(hba->wakeup_parms.u0.boot_bios_wd[0],
                    vpd->boot_version, sizeof (vpd->boot_version));
                /* (void) strcpy(vpd->fcode_version, vpd->boot_version); */

                /* return the result */
                return ((hba->wakeup_parms.u0.boot_bios_wd[0] != 0) ?
                    FC_SUCCESS : FC_FAILURE);
        }

} /* emlxs_boot_code_enable() */



extern int32_t
emlxs_boot_code_state(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        uint8_t boot_state = 0;

        if (hba->model_info.chip & EMLXS_BE_CHIPS) {
                return (FC_SUCCESS);
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                /* Read Boot Config */
                if (emlxs_get_boot_config(hba, &boot_state)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_state: Unable to read boot config.");

                        return (FC_FAILURE);
                }

                return ((boot_state != 0) ? FC_SUCCESS : FC_FAILURE);
        } else {
                /* Read the wakeup parms */
                if (emlxs_read_wakeup_parms(hba, &hba->wakeup_parms, 1)) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sfs_debug_msg,
                            "boot_code_state: Unable to read wake up parms.");

                        return (FC_FAILURE);
                }

                /* return the result */
                return ((hba->wakeup_parms.u0.boot_bios_wd[0] != 0) ?
                    FC_SUCCESS : FC_FAILURE);
        }

} /* emlxs_boot_code_state() */